Skip to content

Commit c019ffc

Browse files
committed
USDScene : Handle USDGeomXformable's ResetXformStack
Because we don't have an equivalent in Cortex, we need to emulate this by applying the inverse of the parent matrix to the local matrix of the location where the stack reset is requested.
1 parent c73ab69 commit c019ffc

File tree

2 files changed

+134
-28
lines changed

2 files changed

+134
-28
lines changed

contrib/IECoreUSD/src/IECoreUSD/USDScene.cpp

Lines changed: 62 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,47 @@ class ShaderNetworkCache : public LRUCache<pxr::SdfPath, IECoreScene::ConstShade
438438

439439
};
440440

441+
Imath::M44d localTransform( const pxr::UsdPrim &prim, pxr::UsdTimeCode time )
442+
{
443+
pxr::UsdGeomXformable transformable( prim );
444+
if( !transformable )
445+
{
446+
return Imath::M44d();
447+
}
448+
449+
pxr::GfMatrix4d transform;
450+
bool reset = false;
451+
transformable.GetLocalTransformation( &transform, &reset, time );
452+
Imath::M44d result = DataAlgo::fromUSD( transform );
453+
454+
if( reset )
455+
{
456+
// Apply inverse of parent's world transform.
457+
Imath::M44d parentWorldTransform;
458+
pxr::UsdPrim parentPrim = prim.GetParent();
459+
while( parentPrim )
460+
{
461+
parentWorldTransform = parentWorldTransform * localTransform( parentPrim, time );
462+
parentPrim = parentPrim.GetParent();
463+
}
464+
result = result * parentWorldTransform.inverse();
465+
}
466+
467+
if( ( prim.GetParent().IsPseudoRoot() || reset ) && pxr::UsdGeomGetStageUpAxis( prim.GetStage() ) == pxr::UsdGeomTokens->z )
468+
{
469+
// Apply Z-up to Y-up correction
470+
static Imath::M44d b(
471+
0, 0, 1, 0,
472+
1, 0, 0, 0,
473+
0, 1, 0, 0,
474+
0, 0, 0, 1
475+
);
476+
result = result * b;
477+
}
478+
479+
return result;
480+
}
481+
441482
} // namespace
442483

443484
class USDScene::Location : public RefCounted
@@ -720,33 +761,7 @@ ConstDataPtr USDScene::readTransform( double time ) const
720761

721762
Imath::M44d USDScene::readTransformAsMatrix( double time ) const
722763
{
723-
pxr::UsdGeomXformable transformable( m_location->prim );
724-
if( !transformable )
725-
{
726-
return Imath::M44d();
727-
}
728-
729-
bool zUp = m_location->prim.GetParent().IsPseudoRoot() && pxr::UsdGeomGetStageUpAxis( m_root->getStage() ) == pxr::UsdGeomTokens->z;
730-
731-
pxr::GfMatrix4d transform;
732-
bool reset = false;
733-
734-
transformable.GetLocalTransformation( &transform, &reset, m_root->getTime( time ) );
735-
Imath::M44d returnValue = DataAlgo::fromUSD( transform );
736-
737-
if ( zUp )
738-
{
739-
static Imath::M44d b
740-
(
741-
0, 0, 1, 0,
742-
1, 0, 0, 0,
743-
0, 1, 0, 0,
744-
0, 0, 0, 1
745-
);
746-
747-
returnValue = returnValue * b;
748-
}
749-
return returnValue;
764+
return localTransform( m_location->prim, m_root->getTime( time ) );
750765
}
751766

752767
ConstObjectPtr USDScene::readObject( double time, const Canceller *canceller ) const
@@ -1396,7 +1411,26 @@ void USDScene::transformHash( double time, IECore::MurmurHash &h ) const
13961411
{
13971412
h.append( m_root->fileName() );
13981413
appendPrimOrMasterPath( m_location->prim, h );
1399-
if( xformable.TransformMightBeTimeVarying() )
1414+
1415+
bool mightBeTimeVarying = xformable.TransformMightBeTimeVarying();
1416+
if( !mightBeTimeVarying && xformable.GetResetXformStack() )
1417+
{
1418+
// Because we have to apply the inverse of our parent's transform, if
1419+
// that is time varying then so are we.
1420+
pxr::UsdPrim parentPrim = m_location->prim.GetParent();
1421+
while( parentPrim )
1422+
{
1423+
pxr::UsdGeomXformable parentXFormable( parentPrim );
1424+
if( parentXFormable && parentXFormable.TransformMightBeTimeVarying() )
1425+
{
1426+
mightBeTimeVarying = true;
1427+
break;
1428+
}
1429+
parentPrim = parentPrim.GetParent();
1430+
}
1431+
}
1432+
1433+
if( mightBeTimeVarying )
14001434
{
14011435
h.append( time );
14021436
}

contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3406,5 +3406,77 @@ def testRoundTripIndexedNormals( self ) :
34063406
root = IECoreScene.SceneInterface.create( fileName, IECore.IndexedIO.OpenMode.Read )
34073407
self.assertEqual( root.child( "mesh").readObject( 0.0 ), mesh )
34083408

3409+
def testResetXFormStack( self ) :
3410+
3411+
fileName = os.path.join( self.temporaryDirectory(), "resetXFormStack.usda" )
3412+
stage = pxr.Usd.Stage.CreateNew( fileName )
3413+
3414+
g1 = pxr.UsdGeom.Xform.Define( stage, "/g1" )
3415+
g1.AddScaleOp().Set( pxr.Gf.Vec3f( 1, 2, 3 ) )
3416+
3417+
g2 = pxr.UsdGeom.Xform.Define( stage, "/g1/g2" )
3418+
g2.AddTranslateOp().Set( pxr.Gf.Vec3f( 4, 5, 6 ) )
3419+
3420+
g3 = pxr.UsdGeom.Xform.Define( stage, "/g1/g2/g3" )
3421+
g3.AddTranslateOp().Set( pxr.Gf.Vec3f( 1, 0, 1 ) )
3422+
g3.SetResetXformStack( True )
3423+
3424+
pxr.UsdGeom.Sphere.Define( stage, "/g1/g2/g3/s" )
3425+
3426+
stage.GetRootLayer().Save()
3427+
del stage
3428+
3429+
worldTransform = imath.M44d()
3430+
location = IECoreScene.SceneInterface.create( fileName, IECore.IndexedIO.OpenMode.Read )
3431+
for name in [ "g1", "g2", "g3", "s" ] :
3432+
location = location.child( name )
3433+
worldTransform = location.readTransformAsMatrix( 0 ) * worldTransform
3434+
self.assertEqual(
3435+
location.hash( location.HashType.TransformHash, 1 ), # There is no animation,
3436+
location.hash( location.HashType.TransformHash, 2 ), # so hashes should be identical
3437+
)
3438+
3439+
self.assertEqual( worldTransform, imath.M44d().translate( imath.V3f( 1, 0, 1 ) ) )
3440+
3441+
def testResetXFormStackHash( self ) :
3442+
3443+
fileName = os.path.join( self.temporaryDirectory(), "resetXFormStackHash.usda" )
3444+
stage = pxr.Usd.Stage.CreateNew( fileName )
3445+
3446+
# /g1 (animated transform)
3447+
# /g2a (reset transform stack)
3448+
# /g2b
3449+
3450+
g1 = pxr.UsdGeom.Xform.Define( stage, "/g1" )
3451+
scaleOp = g1.AddScaleOp()
3452+
scaleOp.Set( pxr.Gf.Vec3f( 2 ), 1 )
3453+
scaleOp.Set( pxr.Gf.Vec3f( 4 ), 2 )
3454+
3455+
g2a = pxr.UsdGeom.Xform.Define( stage, "/g1/g2a" )
3456+
g2a.SetResetXformStack( True )
3457+
pxr.UsdGeom.Xform.Define( stage, "/g1/g2b" )
3458+
3459+
stage.GetRootLayer().Save()
3460+
del stage
3461+
3462+
root = IECoreScene.SceneInterface.create( fileName, IECore.IndexedIO.OpenMode.Read )
3463+
g2a = root.scene( [ "g1", "g2a" ] )
3464+
g2b = root.scene( [ "g1", "g2b" ] )
3465+
3466+
# Hash should be animated because the reset needs to account
3467+
# for animation on parent.
3468+
3469+
self.assertNotEqual(
3470+
g2a.hash( g2a.HashType.TransformHash, 1 ),
3471+
g2a.hash( g2a.HashType.TransformHash, 2 ),
3472+
)
3473+
3474+
# Hash should be static, because there is no reset.
3475+
3476+
self.assertEqual(
3477+
g2b.hash( g2a.HashType.TransformHash, 1 ),
3478+
g2b.hash( g2a.HashType.TransformHash, 2 ),
3479+
)
3480+
34093481
if __name__ == "__main__":
34103482
unittest.main()

0 commit comments

Comments
 (0)