From 3ee9f337dc97ee4f3dad4dc26eca3ee7e42b2024 Mon Sep 17 00:00:00 2001 From: John Haddon Date: Wed, 15 Jun 2022 16:02:28 +0100 Subject: [PATCH 1/3] USDScene : Support material purpose (both read and write) USD allows different materials to be bound for different purposes, with those purposes being `full` or `preview`. See https://graphics.pixar.com/usd/release/wp_usdshade.html. We represent this in Cortex as attributes like `ai:surface:full` or `surface:preview` etc. The intention is not to resolve which of these materials to use during loading in the SceneInterface, but simply to make them available for downstream use. In Gaffer, this might mean using a ShuffleAttributes node to copy `surface:preview` to `surface` so that it gets rendered. Possibly contentious : I removed the explicit management of shader type from `populateMaterial()`, replacing calls like `CreateDisplacementOutput()` with `CreateOutput()`. This is now symmetrical with the reading code, where we happily make attributes from all outputs regardless of their name. It also simplifies `populateMaterial()`, since we already have the exact string to be passed to `CreateOutput()`, rather than splitting it only for `CreateDisplacementOutput()` to join it back together again. --- contrib/IECoreUSD/src/IECoreUSD/USDScene.cpp | 194 +++++++++--------- contrib/IECoreUSD/src/IECoreUSD/USDScene.h | 9 +- .../IECoreUSD/test/IECoreUSD/USDSceneTest.py | 40 +++- .../test/IECoreUSD/data/materialPurpose.usda | 57 +++++ 4 files changed, 201 insertions(+), 99 deletions(-) create mode 100644 contrib/IECoreUSD/test/IECoreUSD/data/materialPurpose.usda diff --git a/contrib/IECoreUSD/src/IECoreUSD/USDScene.cpp b/contrib/IECoreUSD/src/IECoreUSD/USDScene.cpp index d1b7dd250a..6788b53d9a 100644 --- a/contrib/IECoreUSD/src/IECoreUSD/USDScene.cpp +++ b/contrib/IECoreUSD/src/IECoreUSD/USDScene.cpp @@ -76,7 +76,6 @@ IECORE_POP_DEFAULT_VISIBILITY #include "boost/algorithm/string/predicate.hpp" #include "boost/algorithm/string/replace.hpp" #include "boost/algorithm/string/split.hpp" -#include "boost/container/flat_map.hpp" #include "boost/format.hpp" #include "boost/functional/hash.hpp" @@ -358,46 +357,15 @@ SceneInterface::NameList setNamesInternal( const pxr::UsdPrim &prim, bool includ return result; } -void populateMaterial( pxr::UsdShadeMaterial &mat, const std::map< const InternedString, ConstShaderNetworkPtr > &shaderTypes ) +void populateMaterial( pxr::UsdShadeMaterial &mat, const boost::container::flat_map &shaders ) { - for( auto &shaderType : shaderTypes ) + for( const auto &[output, shaderNetwork] : shaders ) { - std::string type = AttributeAlgo::nameToUSD( shaderType.first.string() ).name.GetString(); - std::string prefix; - size_t colonPos = type.rfind( ":" ); - if( colonPos != std::string::npos ) - { - prefix = type.substr( 0, colonPos ); - type = type.substr( colonPos + 1 ); - } - - pxr::UsdShadeOutput matOutput; - pxr::TfToken renderContext = prefix.size() ? pxr::TfToken( prefix ) : pxr::UsdShadeTokens->universalRenderContext; - if( type == "surface" ) - { - matOutput = mat.CreateSurfaceOutput( renderContext ); - } - else if( type == "displacement" ) - { - matOutput = mat.CreateDisplacementOutput( renderContext ); - } - else if( type == "volume" ) - { - matOutput = mat.CreateVolumeOutput( renderContext ); - } - else - { - IECore::msg( - IECore::Msg::Warning, "IECoreUSD::ShaderAlgo::writeShaderNetwork", - boost::format( "Unrecognized shader type \"%1%\"" ) % type - ); - - continue; - } + pxr::UsdShadeOutput matOutput = mat.CreateOutput( output, pxr::SdfValueTypeNames->Token ); - std::string shaderContainerName = boost::replace_all_copy( shaderType.first.string(), ":", "_" ) + "_shaders"; + std::string shaderContainerName = boost::replace_all_copy( output.GetString(), ":", "_" ) + "_shaders"; pxr::UsdGeomScope shaderContainer = pxr::UsdGeomScope::Define( mat.GetPrim().GetStage(), mat.GetPath().AppendChild( pxr::TfToken( shaderContainerName ) ) ); - pxr::UsdShadeOutput networkOut = ShaderAlgo::writeShaderNetwork( shaderType.second.get(), shaderContainer.GetPrim() ); + pxr::UsdShadeOutput networkOut = ShaderAlgo::writeShaderNetwork( shaderNetwork.get(), shaderContainer.GetPrim() ); if( networkOut.GetPrim().IsValid() ) { @@ -406,6 +374,28 @@ void populateMaterial( pxr::UsdShadeMaterial &mat, const std::map< const Interne } } +std::tuple materialOutputAndPurpose( const std::string &attributeName ) +{ + for( const auto &purpose : { pxr::UsdShadeTokens->preview, pxr::UsdShadeTokens->full } ) + { + if( + boost::ends_with( attributeName, purpose.GetString() ) && + attributeName.size() > purpose.GetString().size() + ) + { + size_t colonIndex = attributeName.size() - purpose.GetString().size() - 1; + if( attributeName[colonIndex] == ':' ) + { + return std::make_tuple( + AttributeAlgo::nameToUSD( attributeName.substr( 0, colonIndex ) ).name, + pxr::TfToken( attributeName.substr( colonIndex + 1 ) ) + ); + } + } + } + return { AttributeAlgo::nameToUSD( attributeName ).name, pxr::UsdShadeTokens->allPurpose }; +} + /// SdfPath is the appropriate cache key for _storage_, but we need a /// `UsdShadeOutput` for computation. This struct provides the implicit /// conversion that LRUCache needs to make that possible. @@ -468,6 +458,14 @@ class USDScene::IO : public RefCounted m_timeCodesPerSecond( m_stage->GetTimeCodesPerSecond() ), m_shaderNetworkCache( 10 * 1024 * 1024 ) // 10Mb { + // Although the USD API implies otherwise, we need a different + // cache per-purpose because `UsdShadeMaterialBindingAPI::ComputeBoundMaterial()` + // gives inconsistent results if the cache is shared. We pre-populate + // `m_usdBindingsCaches` here because it wouldn't be thread-safe to + // make insertions in `computeBoundMaterial()`. + m_usdBindingsCaches[pxr::UsdShadeTokens->allPurpose]; + m_usdBindingsCaches[pxr::UsdShadeTokens->full]; + m_usdBindingsCaches[pxr::UsdShadeTokens->preview]; } ~IO() override @@ -534,14 +532,22 @@ class USDScene::IO : public RefCounted return m_allTags; } - pxr::UsdShadeMaterial computeBoundMaterial( const pxr::UsdPrim &prim ) + pxr::UsdShadeMaterial computeBoundMaterial( const pxr::UsdPrim &prim, const pxr::TfToken &materialPurpose ) { // This should be thread safe, despite using caches, because // BindingsCache and CollectionQueryCache are implemented by USD as // tbb::concurrent_unordered_map - return pxr::UsdShadeMaterialBindingAPI( prim ).ComputeBoundMaterial( - &m_usdBindingsCache, &m_usdCollectionQueryCache + pxr::UsdRelationship bindingRelationship; + pxr::UsdShadeMaterial material = pxr::UsdShadeMaterialBindingAPI( prim ).ComputeBoundMaterial( + &m_usdBindingsCaches.at( materialPurpose ), &m_usdCollectionQueryCache, materialPurpose, &bindingRelationship ); + if( material && materialPurpose != pxr::UsdShadeTokens->allPurpose && bindingRelationship.GetBaseName() != materialPurpose ) + { + // Ignore USD fallback to the all purpose binding. We want to load only the bindings that actually exist, + // and then allow people to manage them after loading. + return pxr::UsdShadeMaterial(); + } + return material; } IECoreScene::ConstShaderNetworkPtr readShaderNetwork( const pxr::UsdShadeOutput &output ) @@ -579,7 +585,7 @@ class USDScene::IO : public RefCounted std::once_flag m_allTagsFlag; SceneInterface::NameList m_allTags; - pxr::UsdShadeMaterialBindingAPI::BindingsCache m_usdBindingsCache; + boost::container::flat_map m_usdBindingsCaches; pxr::UsdShadeMaterialBindingAPI::CollectionQueryCache m_usdCollectionQueryCache; ShaderNetworkCache m_shaderNetworkCache; @@ -605,7 +611,7 @@ USDScene::USDScene( IOPtr io, LocationPtr location ) USDScene::~USDScene() { - if( m_shaders.size() ) + if( m_materials.size() ) { try { @@ -626,24 +632,29 @@ USDScene::~USDScene() materialContainer.GetPrim().SetMetadata( g_metadataAutoMaterials, true ); } - // Use a hash to identify the combination of shaders in this material - IECore::MurmurHash materialHash; - for( auto &shaderType : m_shaders ) + for( const auto &[purpose, material] : m_materials ) { - materialHash.append( shaderType.first ); - materialHash.append( shaderType.second->Object::hash() ); - } - pxr::TfToken matName( "material_" + materialHash.toString() ); + // Use a hash to identify the combination of shaders in this material. + IECore::MurmurHash materialHash; + for( const auto &[output, shaderNetwork] : material ) + { + materialHash.append( output ); + materialHash.append( shaderNetwork->Object::hash() ); + } + pxr::TfToken matName( "material_" + materialHash.toString() ); - pxr::SdfPath matPath = materialContainer.GetPrim().GetPath().AppendChild( matName ); - pxr::UsdShadeMaterial mat = pxr::UsdShadeMaterial::Get( materialContainer.GetPrim().GetStage(), matPath ); - if( !mat ) - { - // Another location has not yet defined this material - mat = pxr::UsdShadeMaterial::Define( materialContainer.GetPrim().GetStage(), matPath ); - populateMaterial( mat, m_shaders ); + // Write the material if it hasn't been written already. + pxr::SdfPath matPath = materialContainer.GetPrim().GetPath().AppendChild( matName ); + pxr::UsdShadeMaterial mat = pxr::UsdShadeMaterial::Get( materialContainer.GetPrim().GetStage(), matPath ); + if( !mat ) + { + mat = pxr::UsdShadeMaterial::Define( materialContainer.GetPrim().GetStage(), matPath ); + populateMaterial( mat, material ); + } + + // Bind the material to this location + pxr::UsdShadeMaterialBindingAPI( m_location->prim ).Bind( mat, pxr::UsdShadeTokens->fallbackStrength, purpose ); } - pxr::UsdShadeMaterialBindingAPI( m_location->prim ).Bind( mat ); } catch( std::exception &e ) { @@ -839,19 +850,14 @@ bool USDScene::hasAttribute( const SceneInterface::Name &name ) const } else { - pxr::UsdShadeMaterial mat = m_root->computeBoundMaterial( m_location->prim ); - pxr::UsdPrim matPrim = mat.GetPrim(); - - if( matPrim.IsValid() ) + const auto &[output, purpose] = materialOutputAndPurpose( name.string() ); + if( pxr::UsdShadeMaterial mat = m_root->computeBoundMaterial( m_location->prim, purpose ) ) { - pxr::TfToken n = AttributeAlgo::nameToUSD( name.string() ).name; - pxr::UsdShadeOutput o = mat.GetOutput( n ); - if( o && pxr::UsdAttribute( o ).IsAuthored() ) + if( pxr::UsdShadeOutput o = mat.GetOutput( output ) ) { - return true; + return o.GetAttr().IsAuthored(); } } - return false; } } @@ -907,15 +913,18 @@ void USDScene::attributeNames( SceneInterface::NameList &attrs ) const } } - pxr::UsdShadeMaterial mat = m_root->computeBoundMaterial( m_location->prim ); - - if( mat.GetPrim().IsValid() ) + for( const auto &purpose : { pxr::UsdShadeTokens->allPurpose, pxr::UsdShadeTokens->preview, pxr::UsdShadeTokens->full } ) { - for( pxr::UsdShadeOutput &o : mat.GetOutputs() ) + if( pxr::UsdShadeMaterial mat = m_root->computeBoundMaterial( m_location->prim, purpose ) ) { - if( o && pxr::UsdAttribute( o ).IsAuthored() ) + for( pxr::UsdShadeOutput &o : mat.GetOutputs( /* onlyAuthored = */ true ) ) { - attrs.push_back( AttributeAlgo::nameFromUSD( { o.GetBaseName(), false } ) ); + InternedString attrName = AttributeAlgo::nameFromUSD( { o.GetBaseName() , false } ); + if( !purpose.IsEmpty() ) + { + attrName = attrName.string() + ":" + purpose.GetString(); + } + attrs.push_back( attrName ); } } } @@ -1000,24 +1009,17 @@ ConstObjectPtr USDScene::readAttribute( const SceneInterface::Name &name, double } else { - pxr::UsdShadeMaterial mat = m_root->computeBoundMaterial( m_location->prim ); - - if( mat.GetPrim().IsValid() ) + const auto &[output, purpose] = materialOutputAndPurpose( name.string() ); + if( pxr::UsdShadeMaterial mat = m_root->computeBoundMaterial( m_location->prim, purpose ) ) { - pxr::TfToken n = AttributeAlgo::nameToUSD( name.string() ).name; - - // If there's no output declared, then we will return nullptr, versus - // having an output with no source connected, which will return an - // empty shader network - pxr::UsdShadeOutput o = mat.GetOutput( n ); - if( o && pxr::UsdAttribute( o ).IsAuthored() ) + pxr::UsdShadeOutput o = mat.GetOutput( output ); + if( o && o.GetAttr().IsAuthored() ) { return m_root->readShaderNetwork( o ); } } + return nullptr; } - - return nullptr; } void USDScene::writeAttribute( const SceneInterface::Name &name, const Object *attribute, double time ) @@ -1079,7 +1081,8 @@ void USDScene::writeAttribute( const SceneInterface::Name &name, const Object *a } else if( const IECoreScene::ShaderNetwork *shaderNetwork = runTimeCast( attribute ) ) { - m_shaders[name] = shaderNetwork; + const auto &[output, purpose] = materialOutputAndPurpose( name.string() ); + m_materials[purpose][output] = shaderNetwork; } else if( name.string() == "gaffer:globals" ) { @@ -1432,9 +1435,19 @@ void USDScene::attributesHash( double time, IECore::MurmurHash &h ) const } } - pxr::UsdShadeMaterial mat = m_root->computeBoundMaterial( m_location->prim ); + bool haveMaterials = false; + for( const auto &purpose : { pxr::UsdShadeTokens->allPurpose, pxr::UsdShadeTokens->preview, pxr::UsdShadeTokens->full } ) + { + if( pxr::UsdShadeMaterial mat = m_root->computeBoundMaterial( m_location->prim, purpose ) ) + { + // \todo - This does not consider the possibility that the material could contain time-varying + // attributes + append( mat.GetPrim().GetPath(), h ); + haveMaterials = true; + } + } - if( haveAttributes || mat.GetPrim().IsValid() ) + if( haveAttributes || haveMaterials ) { h.append( m_root->fileName() ); @@ -1446,13 +1459,6 @@ void USDScene::attributesHash( double time, IECore::MurmurHash &h ) const appendPrimOrMasterPath( m_location->prim, h ); } - if( mat.GetPrim().IsValid() ) - { - // \todo - This does not consider the possibility that the material could contain time-varying - // attributes - append( mat.GetPrim().GetPath(), h ); - } - if( mightBeTimeVarying ) { h.append( time ); diff --git a/contrib/IECoreUSD/src/IECoreUSD/USDScene.h b/contrib/IECoreUSD/src/IECoreUSD/USDScene.h index 5c6d4e685a..cee778e1ec 100644 --- a/contrib/IECoreUSD/src/IECoreUSD/USDScene.h +++ b/contrib/IECoreUSD/src/IECoreUSD/USDScene.h @@ -42,6 +42,7 @@ #include "IECore/PathMatcherData.h" +#include "boost/container/flat_map.hpp" // Included here to avoid it being included indirectly via `stage.h`, inside the // scope of IECORE_PUSH_DEFAULT_VISIBILITY. #include "boost/function/function_base.hpp" @@ -119,7 +120,13 @@ class USDScene : public IECoreScene::SceneInterface IOPtr m_root; LocationPtr m_location; - std::map< const IECore::InternedString, IECoreScene::ConstShaderNetworkPtr > m_shaders; + // Contains all the shader networks for a single material, mapping from material output + // (e.g. "surface", "displacement" etc) to the shading network that drives that output. + using MaterialNetworks = boost::container::flat_map; + // Contains the materials to be bound for this location, indexed by purpose. + using Materials = boost::container::flat_map; + Materials m_materials; + }; IE_CORE_DECLAREPTR( USDScene ) diff --git a/contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py b/contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py index 3632792240..a604f2cd45 100644 --- a/contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py +++ b/contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py @@ -2686,10 +2686,6 @@ def testShaders( self ) : shaderLocation.writeAttribute( "volume", oneShaderNetwork, 0 ) # USD supports shaders without a prefix - # A shader type that doesn't correspond to anything in USD won't be written out, - # but make sure it doesn't crash anything - shaderLocation.writeAttribute( "testBad:badShaderType", oneShaderNetwork, 0 ) - del writerRoot, shaderLocation # Read via USD API @@ -3154,5 +3150,41 @@ def testColor4fShaderParameterComponentConnections( self ) : root = IECoreScene.SceneInterface.create( fileName, IECore.IndexedIO.OpenMode.Read ) self.assertEqual( root.child( "object" ).readAttribute( "ai:surface", 0 ), network ) + def testMaterialPurpose( self ) : + + def assertExpected( root ) : + + sphere = root.child( "model" ).child( "sphere" ) + + self.assertEqual( set( sphere.attributeNames() ), { "surface", "surface:full", "surface:preview" } ) + for n in ( "surface", "surface:full", "surface:preview" ) : + self.assertTrue( sphere.hasAttribute( n ) ) + + self.assertEqual( + sphere.readAttribute( "surface", 0 ).getShader( "surface" ).parameters["base"], + IECore.FloatData( 0 ) + ) + + self.assertEqual( + sphere.readAttribute( "surface:full", 0 ).getShader( "surface" ).parameters["base"], + IECore.FloatData( 0.5 ) + ) + + self.assertEqual( + sphere.readAttribute( "surface:preview", 0 ).getShader( "surface" ).parameters["base"], + IECore.FloatData( 1 ) + ) + + inRoot = IECoreScene.SceneInterface.create( os.path.dirname( __file__ ) + "/data/materialPurpose.usda", IECore.IndexedIO.OpenMode.Read ) + assertExpected( inRoot ) + + roundTripFileName = os.path.join( self.temporaryDirectory(), "materialPurpose.usda" ) + outRoot = IECoreScene.SceneInterface.create( roundTripFileName, IECore.IndexedIO.OpenMode.Write ) + + IECoreScene.SceneAlgo.copy( inRoot, outRoot, 0, 0, 24, IECoreScene.SceneAlgo.ProcessFlags.All ) + + roundTripRoot = IECoreScene.SceneInterface.create( roundTripFileName, IECore.IndexedIO.OpenMode.Read ) + assertExpected( roundTripRoot ) + if __name__ == "__main__": unittest.main() diff --git a/contrib/IECoreUSD/test/IECoreUSD/data/materialPurpose.usda b/contrib/IECoreUSD/test/IECoreUSD/data/materialPurpose.usda new file mode 100644 index 0000000000..b6c3887670 --- /dev/null +++ b/contrib/IECoreUSD/test/IECoreUSD/data/materialPurpose.usda @@ -0,0 +1,57 @@ +#usda 1.0 + +def "model" +{ + + def Sphere "sphere" + { + rel material:binding = + rel material:binding:full = + rel material:binding:preview = + } + + def Scope "materials" + { + + def Material "material" + { + + token outputs:surface.connect = + + def Shader "surface" + { + uniform token info:id = "arnold:standard_surface" + float inputs:base = 0 + token outputs:DEFAULT_OUTPUT + } + } + + def Material "fullMaterial" + { + + token outputs:surface.connect = + + def Shader "surface" + { + uniform token info:id = "arnold:standard_surface" + float inputs:base = 0.5 + token outputs:DEFAULT_OUTPUT + } + } + + def Material "previewMaterial" + { + + token outputs:surface.connect = + + def Shader "surface" + { + uniform token info:id = "arnold:standard_surface" + float inputs:base = 1 + token outputs:DEFAULT_OUTPUT + } + } + + } + +} From c4991ce327a42ca6db09a59ae913b2ff9c63d96b Mon Sep 17 00:00:00 2001 From: John Haddon Date: Wed, 15 Jun 2022 18:01:32 +0100 Subject: [PATCH 2/3] USDScene : Add todo --- contrib/IECoreUSD/src/IECoreUSD/USDScene.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/contrib/IECoreUSD/src/IECoreUSD/USDScene.cpp b/contrib/IECoreUSD/src/IECoreUSD/USDScene.cpp index 6788b53d9a..c21b2d5035 100644 --- a/contrib/IECoreUSD/src/IECoreUSD/USDScene.cpp +++ b/contrib/IECoreUSD/src/IECoreUSD/USDScene.cpp @@ -532,6 +532,15 @@ class USDScene::IO : public RefCounted return m_allTags; } + /// \todo This "flattens" material assignment, so that materials assigned at `/root` are loaded as attributes + /// on `/root/child` as well. This is not really what we want - we want to load sparsely and let attribute + /// inheritance do the rest. This would be complicated by two factors : + /// + /// - USD's collection-based bindings. A collection-based binding on an ancestor prim would need to be transformed + /// into a Cortex attribute on the prim, if the collection includes the prim. + /// - USD's `bindingStrength` concept, where `UsdShadeTokens->strongerThanDescendants` allows an ancestor's + /// binding to clobber descendant bindings during resolution. It is not clear how to represent that in Cortex - + /// perhaps by not loading the descendant attributes at all? pxr::UsdShadeMaterial computeBoundMaterial( const pxr::UsdPrim &prim, const pxr::TfToken &materialPurpose ) { // This should be thread safe, despite using caches, because From 61d5da90534cae7f16d96e2e60f09567bd01d11d Mon Sep 17 00:00:00 2001 From: John Haddon Date: Thu, 16 Jun 2022 16:39:54 +0100 Subject: [PATCH 3/3] SceneCacheFileFormatTest : Fix `pxr` imports They were relying on some other code to have imported the submodules, but that is not the case when testing with `TEST_USD_SCRIPT=contrib/IECoreUSD/test/IECoreUSD/SceneCacheFileFormatTest.py`. Also remove trailing whitespace. --- .../IECoreUSD/SceneCacheFileFormatTest.py | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/contrib/IECoreUSD/test/IECoreUSD/SceneCacheFileFormatTest.py b/contrib/IECoreUSD/test/IECoreUSD/SceneCacheFileFormatTest.py index 56e379dc7b..1a67c30303 100644 --- a/contrib/IECoreUSD/test/IECoreUSD/SceneCacheFileFormatTest.py +++ b/contrib/IECoreUSD/test/IECoreUSD/SceneCacheFileFormatTest.py @@ -38,8 +38,8 @@ import tempfile import unittest - -import pxr +import pxr.Usd +import pxr.Sdf import imath @@ -91,7 +91,7 @@ def _writeScene( self, fileName, transformRootChildren=False, writeInvalidUSDNam if transformRootChildren: t.writeTransform( IECore.M44dData(imath.M44d().translate(imath.V3d( 2, 0, 0 ))), 2.0 ) - + boxA = IECoreScene.MeshPrimitive.createBox( imath.Box3f( imath.V3f( 0 ), imath.V3f( 1 ) ) ) if writeCs: boxA["Cs"] = IECoreScene.PrimitiveVariable( @@ -278,7 +278,7 @@ def testAnimatedVisibility( self ): # invisible self.assertEqual( parentVisibility.Get( 1012 ), "invisible" ) - + # still invisible self.assertEqual( parentVisibility.Get( 1021 ), "invisible" ) @@ -304,7 +304,7 @@ def testAnimatedVisibility( self ): # invisible self.assertEqual( parent.readAttribute( IECoreScene.SceneInterface.visibilityName, 1012 / fps ).value, False ) - + # still invisible self.assertEqual( parent.readAttribute( IECoreScene.SceneInterface.visibilityName, 1021 / fps ).value, False ) @@ -326,7 +326,7 @@ def testTagsLoadedAsCollections( self ): t.writeTags( ["t1", "all", "asset-(12)"] ) s.writeTags( ["s1", "all", "asset-(12)"] ) del m, t, s - + # root stage = pxr.Usd.Stage.Open( fileName ) root = stage.GetPseudoRoot() @@ -431,17 +431,17 @@ def testDefaultPrimVars( self ): normalsData[0], pxr.Gf.Vec3f(0.0, 0.0, -1.0) ) - + self.assertEqual( normalsData[8], pxr.Gf.Vec3f(0.0, 0.0, 1.0) ) - + self.assertEqual( normalsData[16], pxr.Gf.Vec3f(0.0, 1.0, 0.0) ) - + self.assertEqual( normalsData[23], pxr.Gf.Vec3f(0.0, -1.0, 0.0) @@ -454,12 +454,12 @@ def testDefaultPrimVars( self ): uvsData[0], pxr.Gf.Vec2f(0.375, 0.0) ) - + self.assertEqual( uvsData[8], pxr.Gf.Vec2f(0.375, 1.0) ) - + self.assertEqual( uvsData[12], pxr.Gf.Vec2f(0.125, .25) @@ -544,7 +544,7 @@ def testCustomPrimvars( self ): # interpolation self.assertEqual( customPoint.GetMetadata( "interpolation" ), "vertex" ) - + # value self.assertEqual( customPoint.Get( 24.0 ), pxr.Vt.Vec3fArray( 1, [ pxr.Gf.Vec3f( 12 ) ] ) ) @@ -645,7 +645,7 @@ def testCornersAndCreases( self ): # crease indices self.assertEqual( mesh.creaseIds(), IECore.IntVectorData( [ 0, 1, 2 ] ) ) - + # crease sharpness self.assertEqual( mesh.creaseSharpnesses()[0], 4.0 ) @@ -693,7 +693,7 @@ def testPointsPrimitive( self ): points = location.readObject( 1.0 ) pointsData = points["P"].data - + self.assertEqual( pointsData[0], imath.V3f( 1, 2, 3 ) ) self.assertEqual( pointsData[1], imath.V3f( 12, 13, 14 ) ) @@ -712,7 +712,7 @@ def testCurvesPrimitive( self ): "catmullRom" : IECore.CubicBasisf.catmullRom(), "linear" : IECore.CubicBasisf.linear(), "bezier" : IECore.CubicBasisf.bezier(), - + } children = [] @@ -1046,7 +1046,7 @@ def testLinksTimeOffset( self ): fps = 24.0 fileName = os.path.join( self.temporaryDirectory(), "testUSDTimeOffsetLinked.scc" ) self._writeScene( fileName, transformRootChildren=True ) - + for start, end in ( ( 0.5, 2.0 ), ( 1.5, 2.5 ), ( 1.0, 1.5) ): linkedScene = IECoreScene.SharedSceneInterfaces.get( fileName ) linkFileName = "{}/testUSDTimeOffsetLink_{}_{}.lscc".format( self.temporaryDirectory(), start, end ) @@ -1057,7 +1057,7 @@ def testLinksTimeOffset( self ): linkedOffset.writeAttribute( IECoreScene.LinkedScene.linkAttribute, linkDataA, start ) linkedOffset.writeAttribute( IECoreScene.LinkedScene.linkAttribute, linkDataB, end ) - + noTimeOffset = l.createChild( "linked" ) noTimeOffset.writeLink( linkedScene ) @@ -1134,7 +1134,7 @@ def testWriteTimeSamplesWitinFrameRange( self ): } stage.Export( fileName, args=args ) - + scene = IECoreScene.SharedSceneInterfaces.get( fileName ) sphere = scene.child( "Sphere" )