Skip to content

Commit 1d6bdb8

Browse files
committed
USDScene : Support doubleSided attribute
Write support is not ideal, because USD will only author `doubleSided` onto Gprims. So it is impossible to write onto transform-only locations, and `writeObject()` must be called before `writeAttribute()` on geometric locations. It may be possible to improve on this by buffering attributes and automatically inheriting them from transforms onto primitives, but it's not clear that the complexity is justified at this point.
1 parent 366b72f commit 1d6bdb8

File tree

3 files changed

+146
-0
lines changed

3 files changed

+146
-0
lines changed

contrib/IECoreUSD/src/IECoreUSD/USDScene.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ IECORE_PUSH_DEFAULT_VISIBILITY
5656
#include "pxr/usd/usd/stage.h"
5757
#include "pxr/usd/usdGeom/bboxCache.h"
5858
#include "pxr/usd/usdGeom/camera.h"
59+
#include "pxr/usd/usdGeom/gprim.h"
5960
#include "pxr/usd/usdGeom/metrics.h"
6061
#include "pxr/usd/usdGeom/pointInstancer.h"
6162
#include "pxr/usd/usdGeom/primvar.h"
@@ -803,6 +804,7 @@ namespace
803804

804805
const IECore::InternedString g_purposeAttributeName( "usd:purpose" );
805806
const IECore::InternedString g_kindAttributeName( "usd:kind" );
807+
const IECore::InternedString g_doubleSidedAttributeName( "doubleSided" );
806808

807809
} // namespace
808810

@@ -828,6 +830,10 @@ bool USDScene::hasAttribute( const SceneInterface::Name &name ) const
828830
pxr::TfToken kind;
829831
return model.GetKind( &kind );
830832
}
833+
else if( name == g_doubleSidedAttributeName )
834+
{
835+
return pxr::UsdGeomGprim( m_location->prim ).GetDoubleSidedAttr().HasAuthoredValue();
836+
}
831837
else if( auto attribute = AttributeAlgo::findUSDAttribute( m_location->prim, name.string() ) )
832838
{
833839
return attribute.HasAuthoredValue();
@@ -876,6 +882,11 @@ void USDScene::attributeNames( SceneInterface::NameList &attrs ) const
876882
attrs.push_back( g_kindAttributeName );
877883
}
878884

885+
if( pxr::UsdGeomGprim( m_location->prim ).GetDoubleSidedAttr().HasAuthoredValue() )
886+
{
887+
attrs.push_back( g_doubleSidedAttributeName );
888+
}
889+
879890
std::vector<pxr::UsdAttribute> attributes = m_location->prim.GetAuthoredAttributes();
880891
for( const auto &attribute : attributes )
881892
{
@@ -961,6 +972,16 @@ ConstObjectPtr USDScene::readAttribute( const SceneInterface::Name &name, double
961972
}
962973
return new StringData( kind.GetString() );
963974
}
975+
else if( name == g_doubleSidedAttributeName )
976+
{
977+
pxr::UsdAttribute attr = pxr::UsdGeomGprim( m_location->prim ).GetDoubleSidedAttr();
978+
bool doubleSided;
979+
if( attr.HasAuthoredValue() && attr.Get( &doubleSided, m_root->getTime( time ) ) )
980+
{
981+
return new BoolData( doubleSided );
982+
}
983+
return nullptr;
984+
}
964985
else if( pxr::UsdAttribute attribute = AttributeAlgo::findUSDAttribute( m_location->prim, name.string() ) )
965986
{
966987
return DataAlgo::fromUSD( attribute, m_root->getTime( time ) );
@@ -1022,6 +1043,28 @@ void USDScene::writeAttribute( const SceneInterface::Name &name, const Object *a
10221043
}
10231044
}
10241045
}
1046+
else if( name == g_doubleSidedAttributeName )
1047+
{
1048+
if( auto *data = reportedCast<const BoolData>( attribute, "USDScene::writeAttribute", name.c_str() ) )
1049+
{
1050+
pxr::UsdGeomGprim gprim( m_location->prim );
1051+
if( gprim )
1052+
{
1053+
gprim.GetDoubleSidedAttr().Set( data->readable(), m_root->getTime( time ) );
1054+
}
1055+
else
1056+
{
1057+
// We're hamstrung by the fact that USD considers `doubleSided` to be a property
1058+
// of a Gprim and not an inheritable attribute as it was in RenderMan and is in Cortex.
1059+
// We can't author a Gprim here, because it isn't a concrete type, so we must rely on
1060+
// `writeObject()` having been called first to get a suitable concrete type in place.
1061+
IECore::msg(
1062+
IECore::Msg::Warning, "USDScene::writeAttribute",
1063+
boost::format( "Unable to write attribute \"%1%\" to \"%2%\", because it is not a Gprim" ) % name % m_location->prim.GetPath()
1064+
);
1065+
}
1066+
}
1067+
}
10251068
else if( const IECoreScene::ShaderNetwork *shaderNetwork = runTimeCast<const ShaderNetwork>( attribute ) )
10261069
{
10271070
m_shaders[name] = shaderNetwork;
@@ -1356,6 +1399,13 @@ void USDScene::attributesHash( double time, IECore::MurmurHash &h ) const
13561399
// Kind can not be animated so no need to update `mightBeTimeVarying`.
13571400
}
13581401

1402+
auto doubleSidedAttr = pxr::UsdGeomGprim( m_location->prim ).GetDoubleSidedAttr();
1403+
if( doubleSidedAttr && doubleSidedAttr.HasAuthoredValue() )
1404+
{
1405+
haveAttributes = true;
1406+
mightBeTimeVarying |= doubleSidedAttr.ValueMightBeTimeVarying();
1407+
}
1408+
13591409
std::vector<pxr::UsdAttribute> attributes = m_location->prim.GetAuthoredAttributes();
13601410
for( const auto &attribute : attributes )
13611411
{

contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2967,5 +2967,85 @@ def testExposedShaderInput( self ) :
29672967
self.assertEqual( network.getOutput(), "surface" )
29682968
self.assertEqual( network.getShader( "surface" ).parameters["diffuse_roughness"].value, 0.75 )
29692969

2970+
def testReadDoubleSidedAttribute( self ) :
2971+
2972+
root = IECoreScene.SceneInterface.create(
2973+
os.path.join( os.path.dirname( __file__ ), "data", "doubleSidedAttribute.usda" ),
2974+
IECore.IndexedIO.OpenMode.Read
2975+
)
2976+
2977+
for name, doubleSided in {
2978+
"sphere" : None,
2979+
"singleSidedSphere" : False,
2980+
"doubleSidedSphere" : True
2981+
}.items() :
2982+
object = root.child( name )
2983+
if doubleSided is None :
2984+
self.assertFalse( object.hasAttribute( "doubleSided" ) )
2985+
self.assertNotIn( "doubleSided", object.attributeNames() )
2986+
else :
2987+
self.assertTrue( object.hasAttribute( "doubleSided" ) )
2988+
self.assertIn( "doubleSided", object.attributeNames() )
2989+
self.assertEqual( object.readAttribute( "doubleSided", 1 ), IECore.BoolData( doubleSided ) )
2990+
2991+
def testWriteDoubleSidedAttribute( self ) :
2992+
2993+
# Write via SceneInterface
2994+
2995+
fileName = os.path.join( self.temporaryDirectory(), "doubleSidedAttribute.usda" )
2996+
root = IECoreScene.SceneInterface.create( fileName, IECore.IndexedIO.OpenMode.Write )
2997+
2998+
toWrite = (
2999+
( "singleSidedSphere", "before", False ),
3000+
( "doubleSidedSphere", "before", True ),
3001+
( "doubleSidedSphereWrittenAfter", "after", True ),
3002+
( "doubleSidedNoObject", "never", True ),
3003+
( "sphere", "before", None ),
3004+
)
3005+
3006+
for name, writeObject, doubleSided in toWrite :
3007+
3008+
child = root.createChild( name )
3009+
if writeObject == "before" :
3010+
child.writeObject( IECoreScene.SpherePrimitive(), 1 )
3011+
3012+
if doubleSided is not None :
3013+
3014+
with IECore.CapturingMessageHandler() as mh :
3015+
child.writeAttribute( "doubleSided", IECore.BoolData( doubleSided ), 1 )
3016+
3017+
if writeObject != "before" :
3018+
self.assertEqual( len( mh.messages ), 1 )
3019+
self.assertEqual(
3020+
mh.messages[0].message,
3021+
'Unable to write attribute "doubleSided" to "/{}", because it is not a Gprim'.format(
3022+
name
3023+
)
3024+
)
3025+
3026+
if writeObject == "after" :
3027+
child.writeObject( IECoreScene.SpherePrimitive(), 1 )
3028+
3029+
del root, child
3030+
3031+
# Verify via USD API
3032+
3033+
stage = pxr.Usd.Stage.Open( fileName )
3034+
3035+
for name, writeObject, doubleSided in toWrite :
3036+
3037+
if writeObject != "before" :
3038+
doubleSided = None
3039+
3040+
if doubleSided is None :
3041+
self.assertFalse(
3042+
pxr.UsdGeom.Gprim( stage.GetPrimAtPath( "/" + name ) ).GetDoubleSidedAttr().HasAuthoredValue(),
3043+
)
3044+
else :
3045+
self.assertEqual(
3046+
pxr.UsdGeom.Gprim( stage.GetPrimAtPath( "/" + name ) ).GetDoubleSidedAttr().Get( 1 ),
3047+
doubleSided
3048+
)
3049+
29703050
if __name__ == "__main__":
29713051
unittest.main()
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#usda 1.0
2+
3+
def Sphere "doubleSidedSphere"
4+
{
5+
uniform bool doubleSided = 1
6+
}
7+
8+
def Sphere "singleSidedSphere"
9+
{
10+
uniform bool doubleSided = 0
11+
}
12+
13+
def Sphere "sphere"
14+
{
15+
}
16+

0 commit comments

Comments
 (0)