diff --git a/contrib/IECoreUSD/src/IECoreUSD/USDScene.cpp b/contrib/IECoreUSD/src/IECoreUSD/USDScene.cpp index 4853e27d07..fce4291823 100644 --- a/contrib/IECoreUSD/src/IECoreUSD/USDScene.cpp +++ b/contrib/IECoreUSD/src/IECoreUSD/USDScene.cpp @@ -56,6 +56,7 @@ IECORE_PUSH_DEFAULT_VISIBILITY #include "pxr/usd/usd/stage.h" #include "pxr/usd/usdGeom/bboxCache.h" #include "pxr/usd/usdGeom/camera.h" +#include "pxr/usd/usdGeom/gprim.h" #include "pxr/usd/usdGeom/metrics.h" #include "pxr/usd/usdGeom/pointInstancer.h" #include "pxr/usd/usdGeom/primvar.h" @@ -803,6 +804,7 @@ namespace const IECore::InternedString g_purposeAttributeName( "usd:purpose" ); const IECore::InternedString g_kindAttributeName( "usd:kind" ); +const IECore::InternedString g_doubleSidedAttributeName( "doubleSided" ); } // namespace @@ -828,6 +830,10 @@ bool USDScene::hasAttribute( const SceneInterface::Name &name ) const pxr::TfToken kind; return model.GetKind( &kind ); } + else if( name == g_doubleSidedAttributeName ) + { + return pxr::UsdGeomGprim( m_location->prim ).GetDoubleSidedAttr().HasAuthoredValue(); + } else if( auto attribute = AttributeAlgo::findUSDAttribute( m_location->prim, name.string() ) ) { return attribute.HasAuthoredValue(); @@ -876,6 +882,11 @@ void USDScene::attributeNames( SceneInterface::NameList &attrs ) const attrs.push_back( g_kindAttributeName ); } + if( pxr::UsdGeomGprim( m_location->prim ).GetDoubleSidedAttr().HasAuthoredValue() ) + { + attrs.push_back( g_doubleSidedAttributeName ); + } + std::vector attributes = m_location->prim.GetAuthoredAttributes(); for( const auto &attribute : attributes ) { @@ -961,6 +972,16 @@ ConstObjectPtr USDScene::readAttribute( const SceneInterface::Name &name, double } return new StringData( kind.GetString() ); } + else if( name == g_doubleSidedAttributeName ) + { + pxr::UsdAttribute attr = pxr::UsdGeomGprim( m_location->prim ).GetDoubleSidedAttr(); + bool doubleSided; + if( attr.HasAuthoredValue() && attr.Get( &doubleSided, m_root->getTime( time ) ) ) + { + return new BoolData( doubleSided ); + } + return nullptr; + } else if( pxr::UsdAttribute attribute = AttributeAlgo::findUSDAttribute( m_location->prim, name.string() ) ) { return DataAlgo::fromUSD( attribute, m_root->getTime( time ) ); @@ -1022,6 +1043,28 @@ void USDScene::writeAttribute( const SceneInterface::Name &name, const Object *a } } } + else if( name == g_doubleSidedAttributeName ) + { + if( auto *data = reportedCast( attribute, "USDScene::writeAttribute", name.c_str() ) ) + { + pxr::UsdGeomGprim gprim( m_location->prim ); + if( gprim ) + { + gprim.GetDoubleSidedAttr().Set( data->readable(), m_root->getTime( time ) ); + } + else + { + // We're hamstrung by the fact that USD considers `doubleSided` to be a property + // of a Gprim and not an inheritable attribute as it was in RenderMan and is in Cortex. + // We can't author a Gprim here, because it isn't a concrete type, so we must rely on + // `writeObject()` having been called first to get a suitable concrete type in place. + IECore::msg( + IECore::Msg::Warning, "USDScene::writeAttribute", + boost::format( "Unable to write attribute \"%1%\" to \"%2%\", because it is not a Gprim" ) % name % m_location->prim.GetPath() + ); + } + } + } else if( const IECoreScene::ShaderNetwork *shaderNetwork = runTimeCast( attribute ) ) { m_shaders[name] = shaderNetwork; @@ -1356,6 +1399,13 @@ void USDScene::attributesHash( double time, IECore::MurmurHash &h ) const // Kind can not be animated so no need to update `mightBeTimeVarying`. } + auto doubleSidedAttr = pxr::UsdGeomGprim( m_location->prim ).GetDoubleSidedAttr(); + if( doubleSidedAttr && doubleSidedAttr.HasAuthoredValue() ) + { + haveAttributes = true; + mightBeTimeVarying |= doubleSidedAttr.ValueMightBeTimeVarying(); + } + std::vector attributes = m_location->prim.GetAuthoredAttributes(); for( const auto &attribute : attributes ) { diff --git a/contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py b/contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py index dfb2448f99..7c263a7337 100644 --- a/contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py +++ b/contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py @@ -2967,5 +2967,85 @@ def testExposedShaderInput( self ) : self.assertEqual( network.getOutput(), "surface" ) self.assertEqual( network.getShader( "surface" ).parameters["diffuse_roughness"].value, 0.75 ) + def testReadDoubleSidedAttribute( self ) : + + root = IECoreScene.SceneInterface.create( + os.path.join( os.path.dirname( __file__ ), "data", "doubleSidedAttribute.usda" ), + IECore.IndexedIO.OpenMode.Read + ) + + for name, doubleSided in { + "sphere" : None, + "singleSidedSphere" : False, + "doubleSidedSphere" : True + }.items() : + object = root.child( name ) + if doubleSided is None : + self.assertFalse( object.hasAttribute( "doubleSided" ) ) + self.assertNotIn( "doubleSided", object.attributeNames() ) + else : + self.assertTrue( object.hasAttribute( "doubleSided" ) ) + self.assertIn( "doubleSided", object.attributeNames() ) + self.assertEqual( object.readAttribute( "doubleSided", 1 ), IECore.BoolData( doubleSided ) ) + + def testWriteDoubleSidedAttribute( self ) : + + # Write via SceneInterface + + fileName = os.path.join( self.temporaryDirectory(), "doubleSidedAttribute.usda" ) + root = IECoreScene.SceneInterface.create( fileName, IECore.IndexedIO.OpenMode.Write ) + + toWrite = ( + ( "singleSidedSphere", "before", False ), + ( "doubleSidedSphere", "before", True ), + ( "doubleSidedSphereWrittenAfter", "after", True ), + ( "doubleSidedNoObject", "never", True ), + ( "sphere", "before", None ), + ) + + for name, writeObject, doubleSided in toWrite : + + child = root.createChild( name ) + if writeObject == "before" : + child.writeObject( IECoreScene.SpherePrimitive(), 1 ) + + if doubleSided is not None : + + with IECore.CapturingMessageHandler() as mh : + child.writeAttribute( "doubleSided", IECore.BoolData( doubleSided ), 1 ) + + if writeObject != "before" : + self.assertEqual( len( mh.messages ), 1 ) + self.assertEqual( + mh.messages[0].message, + 'Unable to write attribute "doubleSided" to "/{}", because it is not a Gprim'.format( + name + ) + ) + + if writeObject == "after" : + child.writeObject( IECoreScene.SpherePrimitive(), 1 ) + + del root, child + + # Verify via USD API + + stage = pxr.Usd.Stage.Open( fileName ) + + for name, writeObject, doubleSided in toWrite : + + if writeObject != "before" : + doubleSided = None + + if doubleSided is None : + self.assertFalse( + pxr.UsdGeom.Gprim( stage.GetPrimAtPath( "/" + name ) ).GetDoubleSidedAttr().HasAuthoredValue(), + ) + else : + self.assertEqual( + pxr.UsdGeom.Gprim( stage.GetPrimAtPath( "/" + name ) ).GetDoubleSidedAttr().Get( 1 ), + doubleSided + ) + if __name__ == "__main__": unittest.main() diff --git a/contrib/IECoreUSD/test/IECoreUSD/data/doubleSidedAttribute.usda b/contrib/IECoreUSD/test/IECoreUSD/data/doubleSidedAttribute.usda new file mode 100644 index 0000000000..1e5dd4c652 --- /dev/null +++ b/contrib/IECoreUSD/test/IECoreUSD/data/doubleSidedAttribute.usda @@ -0,0 +1,16 @@ +#usda 1.0 + +def Sphere "doubleSidedSphere" +{ + uniform bool doubleSided = 1 +} + +def Sphere "singleSidedSphere" +{ + uniform bool doubleSided = 0 +} + +def Sphere "sphere" +{ +} +