Skip to content

Commit 96eb4c3

Browse files
committed
USD PrimitiveAlgo : Improve primitive variable validity checks
We were already doing checks when loading from `UsdGeomPrimvar`, but not from the special attributes like `UsdGeomCurves.GetWidthsAttr()` or `UsdGeomPointBased.GetVelocitiesAttr()`.
1 parent beb7cef commit 96eb4c3

File tree

8 files changed

+148
-78
lines changed

8 files changed

+148
-78
lines changed

Changes

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
10.5.x.x (relative to 10.5.1.0)
22
========
33

4+
Improvements
5+
------------
6+
7+
- USD PrimitiveAlgo : Added `readPrimitiveVariable()` utility method for reading from regular `UsdAttributes`.
8+
49
Fixes
510
-----
611

12+
- USDScene : Fixed handling of invalid values on the following attributes :
13+
- PointBased : `positions`, `normals`, `velocities`, `accelerations`.
14+
- Curves : `widths`.
15+
- PointInstancer : `ids`, `protoIndices`, `orientations`, `scales`, `velocities`, `accelerations`, `angularVelocities`.
16+
- Points : `ids`, `widths`.
17+
Invalid values are now ignored with a warning, instead of loading as invalid primitive variables.
718
- ToMayaMeshConverter : No longer locks normals set on the Mesh from the scc.
819

920
Breaking Changes

contrib/IECoreUSD/include/IECoreUSD/PrimitiveAlgo.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ IECOREUSD_API pxr::TfToken toUSD( IECoreScene::PrimitiveVariable::Interpolation
7575
IECOREUSD_API void readPrimitiveVariables( const pxr::UsdGeomPrimvarsAPI &primvarsAPI, pxr::UsdTimeCode timeCode, IECoreScene::Primitive *primitive, const IECore::Canceller *canceller = nullptr );
7676
/// As above, but also reads "P", "N" etc from `pointBased`.
7777
IECOREUSD_API void readPrimitiveVariables( const pxr::UsdGeomPointBased &pointBased, pxr::UsdTimeCode timeCode, IECoreScene::Primitive *primitive, const IECore::Canceller *canceller = nullptr );
78+
/// Reads the value for `attribute`, adding it as a primitive variable with the specified `name` and `interpolation`.
79+
IECOREUSD_API void readPrimitiveVariable( const pxr::UsdAttribute &attribute, pxr::UsdTimeCode timeCode, IECoreScene::Primitive *primitive, const std::string &name, IECoreScene::PrimitiveVariable::Interpolation interpolation = IECoreScene::PrimitiveVariable::Vertex );
7880
/// Returns true if any of the primitive variables might be animated.
7981
IECOREUSD_API bool primitiveVariablesMightBeTimeVarying( const pxr::UsdGeomPrimvarsAPI &primvarsAPI );
8082
/// Returns true if any of the primitive variables might be animated, including the

contrib/IECoreUSD/src/IECoreUSD/CurvesAlgo.cpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,9 @@ IECore::ObjectPtr readCurves( pxr::UsdGeomBasisCurves &curves, pxr::UsdTimeCode
109109
PrimitiveAlgo::readPrimitiveVariables( curves, time, newCurves.get(), canceller );
110110

111111
Canceller::check( canceller );
112-
PrimitiveVariable::Interpolation widthInterpolation = PrimitiveAlgo::fromUSD( curves.GetWidthsInterpolation() );
113-
DataPtr widthData = DataAlgo::fromUSD( curves.GetWidthsAttr(), time, /* arrayAccepted = */ widthInterpolation != PrimitiveVariable::Constant );
114-
if( widthData )
115-
{
116-
newCurves->variables["width"] = PrimitiveVariable( widthInterpolation, widthData );
117-
}
112+
PrimitiveAlgo::readPrimitiveVariable(
113+
curves.GetWidthsAttr(), time, newCurves.get(), "width", PrimitiveAlgo::fromUSD( curves.GetWidthsInterpolation() )
114+
);
118115

119116
return newCurves;
120117
}

contrib/IECoreUSD/src/IECoreUSD/PointInstancerAlgo.cpp

Lines changed: 14 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -65,47 +65,26 @@ IECore::ObjectPtr readPointInstancer( pxr::UsdGeomPointInstancer &pointInstancer
6565

6666
// Per point attributes
6767

68-
if( auto protoIndicesData = DataAlgo::fromUSD( pointInstancer.GetProtoIndicesAttr(), time ) )
69-
{
70-
Canceller::check( canceller );
71-
newPoints->variables["prototypeIndex"] = IECoreScene::PrimitiveVariable( IECoreScene::PrimitiveVariable::Vertex, protoIndicesData );
72-
}
68+
Canceller::check( canceller );
69+
PrimitiveAlgo::readPrimitiveVariable( pointInstancer.GetProtoIndicesAttr(), time, newPoints.get(), "prototypeIndex" );
7370

74-
if( auto idsData = DataAlgo::fromUSD( pointInstancer.GetIdsAttr(), time ) )
75-
{
76-
Canceller::check( canceller );
77-
newPoints->variables["instanceId"] = IECoreScene::PrimitiveVariable( IECoreScene::PrimitiveVariable::Vertex, idsData );
78-
}
71+
Canceller::check( canceller );
72+
PrimitiveAlgo::readPrimitiveVariable( pointInstancer.GetIdsAttr(), time, newPoints.get(), "instanceId" );
7973

80-
if( auto orientationData = DataAlgo::fromUSD( pointInstancer.GetOrientationsAttr(), time ) )
81-
{
82-
Canceller::check( canceller );
83-
newPoints->variables["orientation"] = IECoreScene::PrimitiveVariable( IECoreScene::PrimitiveVariable::Vertex, orientationData );
84-
}
74+
Canceller::check( canceller );
75+
PrimitiveAlgo::readPrimitiveVariable( pointInstancer.GetOrientationsAttr(), time, newPoints.get(), "orientation" );
8576

86-
if( auto scaleData = DataAlgo::fromUSD( pointInstancer.GetScalesAttr(), time ) )
87-
{
88-
Canceller::check( canceller );
89-
newPoints->variables["scale"] = IECoreScene::PrimitiveVariable( IECoreScene::PrimitiveVariable::Vertex, scaleData );
90-
}
77+
Canceller::check( canceller );
78+
PrimitiveAlgo::readPrimitiveVariable( pointInstancer.GetScalesAttr(), time, newPoints.get(), "scale" );
9179

92-
if( auto velocityData = DataAlgo::fromUSD( pointInstancer.GetVelocitiesAttr(), time ) )
93-
{
94-
Canceller::check( canceller );
95-
newPoints->variables["velocity"] = IECoreScene::PrimitiveVariable( IECoreScene::PrimitiveVariable::Vertex, velocityData );
96-
}
80+
Canceller::check( canceller );
81+
PrimitiveAlgo::readPrimitiveVariable( pointInstancer.GetVelocitiesAttr(), time, newPoints.get(), "velocity" );
9782

98-
if( auto accelerationData = DataAlgo::fromUSD( pointInstancer.GetAccelerationsAttr(), time ) )
99-
{
100-
Canceller::check( canceller );
101-
newPoints->variables["acceleration"] = IECoreScene::PrimitiveVariable( IECoreScene::PrimitiveVariable::Vertex, accelerationData );
102-
}
83+
Canceller::check( canceller );
84+
PrimitiveAlgo::readPrimitiveVariable( pointInstancer.GetAccelerationsAttr(), time, newPoints.get(), "acceleration" );
10385

104-
if( auto angularVelocityData = DataAlgo::fromUSD( pointInstancer.GetAngularVelocitiesAttr(), time ) )
105-
{
106-
Canceller::check( canceller );
107-
newPoints->variables["angularVelocity"] = IECoreScene::PrimitiveVariable( IECoreScene::PrimitiveVariable::Vertex, angularVelocityData );
108-
}
86+
Canceller::check( canceller );
87+
PrimitiveAlgo::readPrimitiveVariable( pointInstancer.GetAngularVelocitiesAttr(), time, newPoints.get(), "angularVelocity" );
10988

11089
// Prototype paths
11190

contrib/IECoreUSD/src/IECoreUSD/PointsAlgo.cpp

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -61,18 +61,12 @@ IECore::ObjectPtr readPoints( pxr::UsdGeomPoints &points, pxr::UsdTimeCode time,
6161
PrimitiveAlgo::readPrimitiveVariables( points, time, newPoints.get(), canceller );
6262

6363
Canceller::check( canceller );
64-
if( auto i = boost::static_pointer_cast<Int64VectorData>( DataAlgo::fromUSD( points.GetIdsAttr(), time ) ) )
65-
{
66-
newPoints->variables["id"] = IECoreScene::PrimitiveVariable( IECoreScene::PrimitiveVariable::Vertex, i );
67-
}
64+
PrimitiveAlgo::readPrimitiveVariable( points.GetIdsAttr(), time, newPoints.get(), "id" );
6865

69-
PrimitiveVariable::Interpolation widthInterpolation = PrimitiveAlgo::fromUSD( points.GetWidthsInterpolation() );
7066
Canceller::check( canceller );
71-
DataPtr widthData = DataAlgo::fromUSD( points.GetWidthsAttr(), time, /* arrayAccepted = */ widthInterpolation != PrimitiveVariable::Constant );
72-
if( widthData )
73-
{
74-
newPoints->variables["width"] = PrimitiveVariable( widthInterpolation, widthData );
75-
}
67+
PrimitiveAlgo::readPrimitiveVariable(
68+
points.GetWidthsAttr(), time, newPoints.get(), "width", PrimitiveAlgo::fromUSD( points.GetWidthsInterpolation() )
69+
);
7670

7771
return newPoints;
7872
}

contrib/IECoreUSD/src/IECoreUSD/PrimitiveAlgo.cpp

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,17 @@ pxr::TfToken IECoreUSD::PrimitiveAlgo::toUSD( IECoreScene::PrimitiveVariable::In
209209
namespace
210210
{
211211

212+
void addPrimitiveVariableIfValid( IECoreScene::Primitive *primitive, const std::string &name, const IECoreScene::PrimitiveVariable &primitiveVariable, const UsdAttribute &source )
213+
{
214+
if( !primitive->isPrimitiveVariableValid( primitiveVariable ) )
215+
{
216+
IECore::msg( IECore::MessageHandler::Level::Warning, "IECoreUSD::PrimitiveAlgo", boost::format( "Ignoring invalid primitive variable \"%1%\"" ) % source.GetPath().GetAsString() );
217+
return;
218+
}
219+
220+
primitive->variables[name] = primitiveVariable;
221+
}
222+
212223
void readPrimitiveVariable( const pxr::UsdGeomPrimvar &primVar, pxr::UsdTimeCode time, const std::string &name, IECoreScene::Primitive *primitive, bool constantAcceptsArray )
213224
{
214225
IECoreScene::PrimitiveVariable::Interpolation interpolation = IECoreUSD::PrimitiveAlgo::fromUSD( primVar.GetInterpolation() );
@@ -242,14 +253,9 @@ void readPrimitiveVariable( const pxr::UsdGeomPrimvar &primVar, pxr::UsdTimeCode
242253
indices = DataAlgo::fromUSD( srcIndices );
243254
}
244255

245-
const IECoreScene::PrimitiveVariable primitiveVariable( interpolation, data, indices );
246-
if( !primitive->isPrimitiveVariableValid( primitiveVariable ) )
247-
{
248-
IECore::msg( IECore::MessageHandler::Level::Warning, "IECoreUSD::PrimitiveAlgo", boost::format( "Skipping invalid UsdGeomPrimvar \"%1%\"" ) % primVar.GetAttr().GetPath().GetAsString() );
249-
return;
250-
}
251-
252-
primitive->variables[name] = primitiveVariable;
256+
addPrimitiveVariableIfValid(
257+
primitive, name, IECoreScene::PrimitiveVariable( interpolation, data, indices ), primVar
258+
);
253259
}
254260

255261
pxr::UsdSkelCache *skelCache()
@@ -387,7 +393,10 @@ bool readPrimitiveVariables( const pxr::UsdSkelRoot &skelRoot, const pxr::UsdGeo
387393

388394
Canceller::check( canceller );
389395
p->setInterpretation( GeometricData::Point );
390-
primitive->variables["P"] = IECoreScene::PrimitiveVariable( IECoreScene::PrimitiveVariable::Vertex, p );
396+
addPrimitiveVariableIfValid(
397+
primitive, "P", IECoreScene::PrimitiveVariable( IECoreScene::PrimitiveVariable::Vertex, p ),
398+
pointBased.GetPointsAttr()
399+
);
391400

392401
// we'll consider normals optional and return true regardless of whether normals were skinned successfully
393402
pxr::VtVec3fArray normals;
@@ -397,7 +406,10 @@ bool readPrimitiveVariables( const pxr::UsdSkelRoot &skelRoot, const pxr::UsdGeo
397406
if( auto n = boost::static_pointer_cast<V3fVectorData>( DataAlgo::fromUSD( normals ) ) )
398407
{
399408
n->setInterpretation( GeometricData::Normal );
400-
primitive->variables["N"] = IECoreScene::PrimitiveVariable( PrimitiveAlgo::fromUSD( pointBased.GetNormalsInterpolation() ), n );
409+
addPrimitiveVariableIfValid(
410+
primitive, "N", IECoreScene::PrimitiveVariable( PrimitiveAlgo::fromUSD( pointBased.GetNormalsInterpolation() ), n ),
411+
pointBased.GetNormalsAttr()
412+
);
401413
}
402414
}
403415

@@ -449,7 +461,7 @@ void IECoreUSD::PrimitiveAlgo::readPrimitiveVariables( const pxr::UsdGeomPrimvar
449461
name = "Cs";
450462
constantAcceptsArray = false;
451463
}
452-
readPrimitiveVariable( primVar, time, name, primitive, constantAcceptsArray );
464+
::readPrimitiveVariable( primVar, time, name, primitive, constantAcceptsArray );
453465
}
454466

455467
// USD uses "st" for the primary texture coordinates and we use "uv",
@@ -496,32 +508,29 @@ void IECoreUSD::PrimitiveAlgo::readPrimitiveVariables( const pxr::UsdGeomPointBa
496508
if( !skelRoot || !::readPrimitiveVariables( skelRoot, pointBased, time, primitive, canceller ) )
497509
{
498510
Canceller::check( canceller );
499-
if( auto p = boost::static_pointer_cast<V3fVectorData>( DataAlgo::fromUSD( pointBased.GetPointsAttr(), time ) ) )
500-
{
501-
primitive->variables["P"] = IECoreScene::PrimitiveVariable( IECoreScene::PrimitiveVariable::Vertex, p );
502-
}
511+
readPrimitiveVariable( pointBased.GetPointsAttr(), time, primitive, "P" );
503512

504513
Canceller::check( canceller );
505514
if( !primitive->variables.count( "N" ) )
506515
{
507516
// Only load `PointBased::GetNormalsAttr()` if we didn't already load `primvars:normals`.
508517
// From the USD API docs : "If normals and primvars:normals are both specified, the latter has precedence."
509-
if( auto n = boost::static_pointer_cast<V3fVectorData>( DataAlgo::fromUSD( pointBased.GetNormalsAttr(), time ) ) )
510-
{
511-
primitive->variables["N"] = IECoreScene::PrimitiveVariable( PrimitiveAlgo::fromUSD( pointBased.GetNormalsInterpolation() ), n );
512-
}
518+
readPrimitiveVariable( pointBased.GetNormalsAttr(), time, primitive, "N", PrimitiveAlgo::fromUSD( pointBased.GetNormalsInterpolation() ) );
513519
}
514520
}
515521

516522
Canceller::check( canceller );
517-
if( auto v = boost::static_pointer_cast<V3fVectorData>( DataAlgo::fromUSD( pointBased.GetVelocitiesAttr(), time ) ) )
518-
{
519-
primitive->variables["velocity"] = IECoreScene::PrimitiveVariable( IECoreScene::PrimitiveVariable::Vertex, v );
520-
}
523+
readPrimitiveVariable( pointBased.GetVelocitiesAttr(), time, primitive, "velocity" );
524+
525+
Canceller::check( canceller );
526+
readPrimitiveVariable( pointBased.GetAccelerationsAttr(), time, primitive, "acceleration" );
527+
}
521528

522-
if( auto a = boost::static_pointer_cast<V3fVectorData>( DataAlgo::fromUSD( pointBased.GetAccelerationsAttr(), time ) ) )
529+
void IECoreUSD::PrimitiveAlgo::readPrimitiveVariable( const pxr::UsdAttribute &attribute, pxr::UsdTimeCode timeCode, IECoreScene::Primitive *primitive, const std::string &name, IECoreScene::PrimitiveVariable::Interpolation interpolation )
530+
{
531+
if( auto d = DataAlgo::fromUSD( attribute, timeCode, /* arrayAccepted = */ interpolation != PrimitiveVariable::Constant ) )
523532
{
524-
primitive->variables["acceleration"] = IECoreScene::PrimitiveVariable( IECoreScene::PrimitiveVariable::Vertex, a );
533+
addPrimitiveVariableIfValid( primitive, name, IECoreScene::PrimitiveVariable( interpolation, d ), attribute );
525534
}
526535
}
527536

contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -736,10 +736,11 @@ def testCanWriteCurves( self ) :
736736
for i in range( 16 ) :
737737
for j in range( 16 ) :
738738
vertsPerCurveData.append( 4 );
739-
for k in range( 3 ) :
739+
for k in range( 4 ) :
740740
positionData.append( imath.V3f( [i, j, k] ) )
741741

742742
curvesPrimitive = IECoreScene.CurvesPrimitive( IECore.IntVectorData( vertsPerCurveData ), IECore.CubicBasisf.linear(), False, IECore.V3fVectorData( positionData ) )
743+
self.assertTrue( curvesPrimitive.arePrimitiveVariablesValid() )
743744

744745
root.writeObject( curvesPrimitive, 0.0 )
745746

@@ -3544,7 +3545,7 @@ def testInvalidPrimitiveVariables( self ) :
35443545

35453546
self.assertEqual( len( mh.messages ), 1 )
35463547
self.assertEqual( mh.messages[0].level, IECore.Msg.Level.Warning )
3547-
self.assertEqual( mh.messages[0].message, "Skipping invalid UsdGeomPrimvar \"/pCube1.primvars:st\"" )
3548+
self.assertEqual( mh.messages[0].message, "Ignoring invalid primitive variable \"/pCube1.primvars:st\"" )
35483549

35493550
self.assertNotIn( "uv", badCube )
35503551
self.assertTrue( badCube.arePrimitiveVariablesValid() )
@@ -3553,6 +3554,82 @@ def testInvalidPrimitiveVariables( self ) :
35533554
del goodCube["uv"]
35543555
self.assertEqual( goodCube, badCube )
35553556

3557+
def testInvalidPointBasedAttributes( self ) :
3558+
3559+
fileName = os.path.join( self.temporaryDirectory(), "pointBased.usda" )
3560+
stage = pxr.Usd.Stage.CreateNew( fileName )
3561+
3562+
curves = pxr.UsdGeom.BasisCurves.Define( stage, "/curves" )
3563+
curves.CreateCurveVertexCountsAttr().Set( [ 4 ] )
3564+
curves.CreatePointsAttr().Set( [ ( 1, 1, 1 ), ( 2, 2, 2 ) ] )
3565+
curves.CreateVelocitiesAttr().Set( [ ( 1, 1, 1 ) ] )
3566+
curves.CreateAccelerationsAttr().Set( [ ( 0, 0, 0 ) ] )
3567+
curves.CreateNormalsAttr().Set( [ ( 1, 0, 0 ) ] )
3568+
3569+
stage.GetRootLayer().Save()
3570+
3571+
root = IECoreScene.SceneInterface.create( fileName, IECore.IndexedIO.OpenMode.Read )
3572+
with IECore.CapturingMessageHandler() as mh :
3573+
curves = root.child( "curves" ).readObject( 0 )
3574+
3575+
# No valid primitive variables.
3576+
self.assertEqual( curves.keys(), [] )
3577+
# And a warning message for every one we tried to load.
3578+
messages = { m.message for m in mh.messages }
3579+
for attribute in [
3580+
"/curves.points",
3581+
"/curves.velocities",
3582+
"/curves.accelerations",
3583+
"/curves.normals",
3584+
] :
3585+
self.assertIn(
3586+
f"Ignoring invalid primitive variable \"{attribute}\"",
3587+
messages
3588+
)
3589+
3590+
def testInvalidCurvesAttributes( self ) :
3591+
3592+
fileName = os.path.join( self.temporaryDirectory(), "curves.usda" )
3593+
stage = pxr.Usd.Stage.CreateNew( fileName )
3594+
3595+
curves = pxr.UsdGeom.BasisCurves.Define( stage, "/curves" )
3596+
curves.CreateCurveVertexCountsAttr().Set( [ 4 ] )
3597+
curves.CreateWidthsAttr().Set( [ 1, 2 ] )
3598+
3599+
stage.GetRootLayer().Save()
3600+
3601+
root = IECoreScene.SceneInterface.create( fileName, IECore.IndexedIO.OpenMode.Read )
3602+
with IECore.CapturingMessageHandler() as mh :
3603+
curves = root.child( "curves" ).readObject( 0 )
3604+
3605+
self.assertEqual( len( mh.messages ), 1 )
3606+
self.assertEqual(
3607+
mh.messages[0].message, f"Ignoring invalid primitive variable \"/curves.widths\"",
3608+
)
3609+
self.assertNotIn( "width", curves )
3610+
3611+
def testInvalidPointsAttributes( self ) :
3612+
3613+
fileName = os.path.join( self.temporaryDirectory(), "points.usda" )
3614+
stage = pxr.Usd.Stage.CreateNew( fileName )
3615+
3616+
points = pxr.UsdGeom.Points.Define( stage, "/points" )
3617+
points.CreatePointsAttr().Set( [ ( 1, 1, 1 ), ( 2, 2, 2 ), ( 3, 3, 3 ) ] )
3618+
points.CreateWidthsAttr().Set( [ 1, 2 ] )
3619+
points.CreateIdsAttr().Set( [ 1, 2 ] )
3620+
3621+
stage.GetRootLayer().Save()
3622+
3623+
root = IECoreScene.SceneInterface.create( fileName, IECore.IndexedIO.OpenMode.Read )
3624+
with IECore.CapturingMessageHandler() as mh :
3625+
points = root.child( "points" ).readObject( 0 )
3626+
3627+
self.assertEqual( len( mh.messages ), 2 )
3628+
self.assertEqual( mh.messages[0].message, f"Ignoring invalid primitive variable \"/points.ids\"" )
3629+
self.assertEqual( mh.messages[1].message, f"Ignoring invalid primitive variable \"/points.widths\"" )
3630+
self.assertNotIn( "id", points )
3631+
self.assertNotIn( "width", points )
3632+
35563633
def testNormalsPrimVar( self ) :
35573634

35583635
root = IECoreScene.SceneInterface.create( os.path.dirname( __file__ ) + "/data/normalsPrimVar.usda", IECore.IndexedIO.OpenMode.Read )

contrib/IECoreUSD/test/IECoreUSD/data/curves.usda

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
def BasisCurves "borderLines"
44
{
55
float3[] extent = [(-5, -5, -0), (5, 5, 0)]
6+
int[] curveVertexCounts = [ 4 ]
67
point3f[] points.timeSamples = {
78
1.0000000298023224: [(-5, -5, 0), (5, -5, 0), (5, 5, 0), (-5, 5, 0)],
89
}

0 commit comments

Comments
 (0)