Skip to content

Commit 6aaad9f

Browse files
committed
SceneShape: Fix reading of time samples for expanded link locations
When reading a link at a location the determination if time is added to the `linkAttributeData` was solely based on the connection of the time plug. In case the time plug of the scene shape is not connected to maya's global time output, we assumed a time remapping to happen. This leads to adding the current time to the `linkAttributeData` if the location is an expanded child location, because we connect the out time of a scene shape to it's childs in time on expansion, which breaks this assumption. We address this issue by traversing the time plug's connection chain and check if the plug is connected to any scene shape or maya's global time
1 parent 173e359 commit 6aaad9f

File tree

5 files changed

+145
-19
lines changed

5 files changed

+145
-19
lines changed

Changes

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
10.4.x.x (relative to 10.4.9.1)
22
========
33

4+
Fixes
5+
---
6+
7+
- IECoreMaya.SceneShape : Fixed reading of time samples for expanded link locations
8+
- Traverse the time plug connections to check for a maya global time connection
49

510
10.4.9.1 (relative to 10.4.9.0)
611
========

src/IECoreMaya/SceneShape.cpp

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,18 @@
4545
#include "maya/MFnDagNode.h"
4646
#include "maya/MTime.h"
4747
#include "maya/MEvaluationNode.h"
48+
#include "maya/MItDependencyGraph.h"
49+
4850

4951
using namespace IECore;
5052
using namespace IECoreScene;
5153
using namespace IECoreMaya;
5254

55+
namespace
56+
{
57+
const MString g_mayaGlobalTimeNodeName( "time1" );
58+
}
59+
5360
MTypeId SceneShape::id = SceneShapeId;
5461
MObject SceneShape::aSceneFilePlug;
5562
MObject SceneShape::aSceneRootPlug;
@@ -256,7 +263,7 @@ ConstObjectPtr SceneShape::readSceneShapeLink( const MDagPath &p )
256263
SceneShape *sceneShape = findScene( p, true, &dagPath );
257264
if ( !sceneShape )
258265
{
259-
throw Exception("readSceneShapeLink: Could not find SceneShape!");
266+
throw Exception( "readSceneShapeLink: Could not find SceneShape!" );
260267
}
261268

262269
const SceneInterface *scene = sceneShape->getSceneInterface().get();
@@ -270,26 +277,36 @@ ConstObjectPtr SceneShape::readSceneShapeLink( const MDagPath &p )
270277
MPlug timePlug = fnChildDag.findPlug( aTime, false, &st );
271278
if( !st )
272279
{
273-
throw Exception( "Could not find 'time' plug in SceneShape!");
280+
throw Exception( "Could not find 'time' plug in SceneShape!" );
274281
}
275282

276-
// if time plug is connected to maya global time, then we assume there's no time remapping between the Maya scene and the loaded scene.
277-
MPlugArray array;
278-
timePlug.connectedTo( array, true, false, &st );
279-
if( !st )
283+
MItDependencyGraph it = MItDependencyGraph( timePlug, MFn::kInvalid, MItDependencyGraph::kUpstream, MItDependencyGraph::kDepthFirst, MItDependencyGraph::kPlugLevel );
284+
for ( ; !it.isDone() ; it.next() )
280285
{
281-
throw Exception( "Could not find 'time' plug connections in SceneShape!");
282-
}
286+
MPlug currPlug = it.thisPlug( &st );
287+
CHECK_MSTATUS(st);
288+
MFnDependencyNode nodeFn( currPlug.node(), &st );
289+
CHECK_MSTATUS(st);
283290

284-
for ( unsigned int i = 0; i < array.length(); i++ )
285-
{
286-
if ( array[i].name() == "time1.outTime" )
291+
if ( nodeFn.name() == g_mayaGlobalTimeNodeName )
287292
{
288-
/// connected to time, so no time remapping between maya scene and loaded scene.
293+
/// The plug is connected to maya global time, so no time remapping is happening
289294
return LinkedScene::linkAttributeData( scene );
290295
}
296+
297+
unsigned int nodeId = nodeFn.typeId().id();
298+
bool isSceneShape = nodeId == SceneShapeId || nodeId == SceneShapeProxyId;
299+
if ( !isSceneShape )
300+
{
301+
/// The time plug is not connected to global maya time or a scene shape (expanded hierarchy),
302+
/// so we assume some kind of time manipulation (a maya add node to offset the value or similiar)
303+
MTime time;
304+
timePlug.getValue( time );
305+
return LinkedScene::linkAttributeData( scene, time.as( MTime::kSeconds ) );
306+
}
291307
}
292-
/// couldn't find connection to maya time, so this node is mapping the time some other way.
308+
309+
/// The time plug doesn't have any connection at all (time hold)
293310
MTime time;
294311
timePlug.getValue( time );
295312
return LinkedScene::linkAttributeData( scene, time.as( MTime::kSeconds ) );

test/IECoreMaya/LiveSceneTest.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -556,9 +556,7 @@ def testSceneShapeCustomReaders( self ):
556556
self.assertFalse( spheresScene.hasAttribute( IECoreScene.LinkedScene.linkAttribute ) )
557557
leafScene = spheresScene.child("A").child("a")
558558
self.assertTrue( leafScene.hasAttribute( IECoreScene.LinkedScene.linkAttribute ) )
559-
# When expanding, we connect the child time attributes to their scene shape parent time attribute to propagate time remapping. When checking for time remapping, the scene shape
560-
# currently only checks the direct connection, so we have here time in the link attributes. Will have to look out for performance issues.
561-
self.assertEqual( leafScene.readAttribute( IECoreScene.LinkedScene.linkAttribute, 0 ), IECore.CompoundData( { "fileName":IECore.StringData('test/IECore/data/sccFiles/animatedSpheres.scc'), "root":IECore.InternedStringVectorData([ 'A', 'a' ]), 'time':IECore.DoubleData( 0 ) } ) )
559+
self.assertEqual( leafScene.readAttribute( IECoreScene.LinkedScene.linkAttribute, 0 ), IECore.CompoundData( { "fileName":IECore.StringData('test/IECore/data/sccFiles/animatedSpheres.scc'), "root":IECore.InternedStringVectorData([ 'A', 'a' ]) } ) )
562560
self.assertFalse( leafScene.hasObject() )
563561

564562
# expand scene to meshes

test/IECoreMaya/SceneShapeProxyTest.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ class SceneShapeProxyTest( SceneShapeTest.SceneShapeTest ):
4242

4343
def setUp( self ):
4444
IECoreMaya.TestCase.setUp(self)
45-
self._node = maya.cmds.createNode( "ieSceneShapeProxy" )
45+
self._shapeType = "ieSceneShapeProxy"
46+
self._node = maya.cmds.createNode( self._shapeType )
4647

4748
if __name__ == "__main__":
4849
IECoreMaya.TestProgram( plugins = [ "ieCore" ] )

test/IECoreMaya/SceneShapeTest.py

Lines changed: 107 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,12 @@ class SceneShapeTest( IECoreMaya.TestCase ) :
4747
__testPlugFile = "test/testPlug.scc"
4848
__testPlugAnimFile = "test/testPlugAnim.scc"
4949
__testPlugAttrFile = "test/testPlugAttr.scc"
50+
__testLinkFile = "test/test.lscc"
5051

5152
def setUp( self ):
5253
super( SceneShapeTest, self ).setUp()
53-
self._node = maya.cmds.createNode( "ieSceneShape" )
54+
self._shapeType = "ieSceneShape"
55+
self._node = maya.cmds.createNode( self._shapeType )
5456

5557
def writeSCC( self, file, rotation=imath.V3d( 0, 0, 0 ), time=0 ) :
5658

@@ -550,10 +552,113 @@ def testLinkedLiveSceneTags( self ) :
550552
self.assertEqual( sorted([ str(x) for x in child2.readTags() ]), ["ObjectType:MeshPrimitive","b"] )
551553
self.assertEqual( sorted([ str(x) for x in child3.readTags() ]), ["ObjectType:MeshPrimitive","c"] )
552554

555+
def testReadSceneShapeLink( self ):
556+
557+
# Create an .lscc with three child locations that we will time remap once we have read it back into maya
558+
self.writeAnimSCC( SceneShapeTest.__testFile )
559+
560+
sceneCache = IECoreScene.SceneCache( SceneShapeTest.__testFile, IECore.IndexedIO.OpenMode.Read )
561+
linkedScene = IECoreScene.LinkedScene( SceneShapeTest.__testLinkFile, IECore.IndexedIO.OpenMode.Write )
562+
563+
noRemapLocation = linkedScene.createChild( "NoRemap" )
564+
noRemapLocation.writeLink( sceneCache )
565+
remapLocation = linkedScene.createChild( "Remap" )
566+
remapLocation.writeLink( sceneCache )
567+
holdLocation = linkedScene.createChild( "Hold" )
568+
holdLocation.writeLink( sceneCache )
569+
570+
del noRemapLocation, remapLocation, holdLocation
571+
del linkedScene
572+
573+
# Create a root level scene shape node to read in the .lscc
574+
rootFn = IECoreMaya.FnSceneShape.create( "Root", shapeType=self._shapeType )
575+
maya.cmds.setAttr( rootFn.name() + ".file", SceneShapeTest.__testLinkFile, type="string" )
576+
maya.cmds.setAttr( rootFn.name() + ".root", "/", type="string" )
577+
578+
holdFn, noRemapFn, remapFn = rootFn.expandOnce()
579+
580+
# check that we don't have any other value than global maya time
581+
mayaTime = maya.cmds.currentTime( query=True )
582+
self.assertEqual( maya.cmds.getAttr( holdFn.name() + ".time" ), mayaTime )
583+
self.assertEqual( maya.cmds.getAttr( noRemapFn.name() + ".time" ), mayaTime )
584+
self.assertEqual( maya.cmds.getAttr( remapFn.name() + ".time" ), mayaTime )
585+
586+
# break the time connection to create a time hold
587+
maya.cmds.disconnectAttr( rootFn.name() + ".outTime", holdFn.name() + ".time" )
588+
maya.cmds.setAttr( holdFn.name() + ".time", 5.0 )
589+
self.assertEqual( maya.cmds.getAttr( holdFn.name() + ".time" ), 5.0 )
590+
591+
# add an offset to create a time remapping
592+
addNode = maya.cmds.createNode( "addDoubleLinear" )
593+
maya.cmds.connectAttr( "time1.outTime", addNode + ".input1" )
594+
maya.cmds.setAttr( addNode + ".input2", 10.0 )
595+
maya.cmds.connectAttr( addNode + ".output", remapFn.name() + ".time", force=True )
596+
self.assertEqual( maya.cmds.getAttr( remapFn.name() + ".time" ), 11.0 )
597+
598+
# set up the LiveScenes for actual testing
599+
holdFn.expandAll()
600+
holdLiveScene = IECoreMaya.LiveScene()
601+
holdLocation = holdLiveScene.child( "|Root|Hold" )
602+
holdChild1 = holdLocation.child( holdLocation.childNames()[0] )
603+
holdChild2 = holdChild1.child( holdChild1.childNames()[0] )
604+
holdChild3 = holdChild2.child( holdChild2.childNames()[0] )
605+
holdLocations = [holdLocation, holdChild1, holdChild2, holdChild3]
606+
607+
noRemapFn.expandAll()
608+
noRemapLiveScene = IECoreMaya.LiveScene()
609+
noRemapLocation = noRemapLiveScene.child( "|Root|NoRemap" )
610+
noRemapChild1 = noRemapLocation.child( noRemapLocation.childNames()[0] )
611+
noRemapChild2 = noRemapChild1.child( noRemapChild1.childNames()[0] )
612+
noRemapChild3 = noRemapChild2.child( noRemapChild2.childNames()[0] )
613+
noRemapLocations = [noRemapLocation, noRemapChild1, noRemapChild2, noRemapChild3]
614+
615+
remapFn.expandAll()
616+
remapLiveScene = IECoreMaya.LiveScene()
617+
remapLocation = remapLiveScene.child( "|Root|Remap" )
618+
remapChild1 = remapLocation.child( remapLocation.childNames()[0] )
619+
remapChild2 = remapChild1.child( remapChild1.childNames()[0] )
620+
remapChild3 = remapChild2.child( remapChild2.childNames()[0] )
621+
remapLocations = [remapLocation, remapChild1, remapChild2, remapChild3]
622+
623+
# set up some constants used during testing
624+
FPS = 24
625+
TIME_HOLD = 5.0
626+
TIME_OFFSET = 10.0
627+
628+
for time in [1.0, 5.0, 10.0, 50.0, 100.0]:
629+
630+
maya.cmds.currentTime( time, edit=True )
631+
mayaTime = maya.cmds.currentTime( query=True )
632+
# check that we actually moved in time
633+
self.assertEqual( mayaTime, time )
634+
635+
# test no time remap
636+
for location in noRemapLocations:
637+
nodeTime = maya.cmds.getAttr( str(location.dagPath()) + ".outTime" )
638+
linkAttr = location.readAttribute( IECoreScene.LinkedScene.linkAttribute, mayaTime )
639+
self.assertFalse( linkAttr.has_key( "time" ) )
640+
self.assertEqual( nodeTime, mayaTime )
641+
642+
# test time remap
643+
for location in remapLocations:
644+
linkAttr = location.readAttribute( IECoreScene.LinkedScene.linkAttribute, mayaTime )
645+
self.assertTrue( linkAttr.has_key( "time" ) )
646+
self.assertEqual( linkAttr["time"].value * FPS, mayaTime + TIME_OFFSET )
647+
648+
# test time hold
649+
for location in holdLocations:
650+
linkAttr = location.readAttribute( IECoreScene.LinkedScene.linkAttribute, mayaTime )
651+
self.assertTrue( linkAttr.has_key( "time" ) )
652+
self.assertEqual( linkAttr["time"].value * FPS, TIME_HOLD )
653+
553654

554655
def tearDown( self ) :
555656

556-
for f in [ SceneShapeTest.__testFile, SceneShapeTest.__testPlugFile, SceneShapeTest.__testPlugAnimFile, SceneShapeTest.__testPlugAttrFile ] :
657+
for f in [
658+
SceneShapeTest.__testFile, SceneShapeTest.__testPlugFile,
659+
SceneShapeTest.__testPlugAnimFile, SceneShapeTest.__testPlugAttrFile,
660+
SceneShapeTest.__testLinkFile
661+
]:
557662
if os.path.exists( f ) :
558663
os.remove( f )
559664

0 commit comments

Comments
 (0)