Skip to content

Commit 1774769

Browse files
committed
Merge pull request #97 from andrewkaufman/ropLinkControl
ForceObjects parameter for SceneCache ROP
2 parents 223f2ee + 57cae00 commit 1774769

File tree

7 files changed

+216
-12
lines changed

7 files changed

+216
-12
lines changed

include/IECoreHoudini/HoudiniScene.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ class HoudiniScene : public IECore::SceneInterface
9393
virtual IECore::SceneInterfacePtr scene( const Path &path, MissingBehaviour missingBehaviour = SceneInterface::ThrowIfMissing );
9494
virtual IECore::ConstSceneInterfacePtr scene( const Path &path, MissingBehaviour missingBehaviour = SceneInterface::ThrowIfMissing ) const;
9595

96+
/// Convenience method to access the Houdini node this scene refers to
97+
const OP_Node *node() const;
98+
9699
typedef boost::function<bool (const OP_Node *)> HasFn;
97100
typedef boost::function<IECore::ConstObjectPtr (const OP_Node *)> ReadFn;
98101
typedef boost::function<bool (const OP_Node *, const Name &)> HasTagFn;

include/IECoreHoudini/ROP_SceneCacheWriter.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,11 @@ class ROP_SceneCacheWriter : public ROP_Node
5656

5757
static PRM_Name pFile;
5858
static PRM_Name pRootObject;
59+
static PRM_Name pForceObjects;
5960

6061
static PRM_Default fileDefault;
6162
static PRM_Default rootObjectDefault;
63+
static PRM_SpareData forceObjectsSpareData;
6264

6365
static OP_Node *create( OP_Network *net, const char *name, OP_Operator *op );
6466
static OP_TemplatePair *buildParameters();
@@ -69,14 +71,27 @@ class ROP_SceneCacheWriter : public ROP_Node
6971
virtual ROP_RENDER_CODE renderFrame( fpreal time, UT_Interrupt *boss );
7072
virtual ROP_RENDER_CODE endRender();
7173

74+
virtual bool updateParmsFlags();
75+
7276
/// Called recursively to traverse the IECoreHoudini::HoudiniScene, starting with the Root Object,
7377
/// and write the hierarchy to the output file.
7478
virtual ROP_RENDER_CODE doWrite( const IECore::SceneInterface *liveScene, IECore::SceneInterface *outScene, double time );
7579

7680
private :
7781

82+
bool linked( const std::string &file ) const;
83+
84+
enum Mode
85+
{
86+
NaturalLink = 0,
87+
ForcedLink,
88+
NaturalExpand,
89+
ForcedExpand
90+
};
91+
7892
IECore::ConstSceneInterfacePtr m_liveScene;
7993
IECore::SceneInterfacePtr m_outScene;
94+
UT_StringMMPattern *m_forceFilter;
8095

8196
};
8297

include/IECoreHoudini/SceneCacheNode.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ class SceneCacheNode : public BaseType
108108

109109
/// Access point to the actual SceneCache. All users should only access the cache
110110
/// using this method, in order to avoid re-opening existing files.
111-
IECore::ConstSceneInterfacePtr scene();
111+
IECore::ConstSceneInterfacePtr scene() const;
112112

113113
protected :
114114

src/IECoreHoudini/HoudiniScene.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ HoudiniScene::~HoudiniScene()
9090
{
9191
}
9292

93+
const OP_Node *HoudiniScene::node() const
94+
{
95+
return retrieveNode( false, NullIfMissing );
96+
}
97+
9398
std::string HoudiniScene::fileName() const
9499
{
95100
throw Exception( "HoudiniScene does not support fileName()." );

src/IECoreHoudini/ROP_SceneCacheWriter.cpp

Lines changed: 98 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,14 @@
3636

3737
#include "GEO/GEO_AttributeHandle.h"
3838
#include "GU/GU_Detail.h"
39-
#include "OBJ/OBJ_Node.h"
39+
#include "OBJ/OBJ_Node.h"
40+
#include "OP/OP_Bundle.h"
4041
#include "OP/OP_Director.h"
4142
#include "PRM/PRM_Include.h"
43+
#include "PRM/PRM_Parm.h"
4244
#include "PRM/PRM_SpareData.h"
4345
#include "ROP/ROP_Error.h"
46+
#include "UT/UT_StringMMPattern.h"
4447

4548
#include "IECore/LinkedScene.h"
4649

@@ -54,12 +57,13 @@ using namespace IECoreHoudini;
5457
const char *ROP_SceneCacheWriter::typeName = "ieSceneCacheWriter";
5558

5659
ROP_SceneCacheWriter::ROP_SceneCacheWriter( OP_Network *net, const char *name, OP_Operator *op )
57-
: ROP_Node( net, name, op ), m_liveScene( 0 ), m_outScene( 0 )
60+
: ROP_Node( net, name, op ), m_liveScene( 0 ), m_outScene( 0 ), m_forceFilter( 0 )
5861
{
5962
}
6063

6164
ROP_SceneCacheWriter::~ROP_SceneCacheWriter()
6265
{
66+
delete m_forceFilter;
6367
}
6468

6569
OP_Node *ROP_SceneCacheWriter::create( OP_Network *net, const char *name, OP_Operator *op )
@@ -69,16 +73,18 @@ OP_Node *ROP_SceneCacheWriter::create( OP_Network *net, const char *name, OP_Ope
6973

7074
PRM_Name ROP_SceneCacheWriter::pFile( "file", "File" );
7175
PRM_Name ROP_SceneCacheWriter::pRootObject( "rootObject", "Root Object" );
76+
PRM_Name ROP_SceneCacheWriter::pForceObjects( "forceObjects", "Force Objects" );
7277

7378
PRM_Default ROP_SceneCacheWriter::fileDefault( 0, "$HIP/output.scc" );
7479
PRM_Default ROP_SceneCacheWriter::rootObjectDefault( 0, "/obj" );
80+
PRM_SpareData ROP_SceneCacheWriter::forceObjectsSpareData;
7581

7682
OP_TemplatePair *ROP_SceneCacheWriter::buildParameters()
7783
{
7884
static PRM_Template *thisTemplate = 0;
7985
if ( !thisTemplate )
8086
{
81-
thisTemplate = new PRM_Template[3];
87+
thisTemplate = new PRM_Template[4];
8288

8389
thisTemplate[0] = PRM_Template(
8490
PRM_FILE, 1, &pFile, &fileDefault, 0, 0, 0, 0, 0,
@@ -89,6 +95,15 @@ OP_TemplatePair *ROP_SceneCacheWriter::buildParameters()
8995
PRM_STRING, PRM_TYPE_DYNAMIC_PATH, 1, &pRootObject, &rootObjectDefault, 0, 0, 0,
9096
&PRM_SpareData::objPath, 0, "The node to use as the root of the SceneCache"
9197
);
98+
99+
forceObjectsSpareData.copyFrom( PRM_SpareData::objPath );
100+
forceObjectsSpareData.setOpRelative( "/obj" );
101+
102+
thisTemplate[2] = PRM_Template(
103+
PRM_STRING, PRM_TYPE_DYNAMIC_PATH_LIST, 1, &pForceObjects, 0, 0, 0, 0,
104+
&forceObjectsSpareData, 0, "Optional list of nodes to force as expanded objects. "
105+
"If this list is used, then links will be stored for any node not listed."
106+
);
92107
}
93108

94109
static OP_TemplatePair *templatePair = 0;
@@ -116,7 +131,7 @@ int ROP_SceneCacheWriter::startRender( int nframes, fpreal s, fpreal e )
116131
m_liveScene = new IECoreHoudini::HoudiniScene( nodePath, emptyPath, emptyPath );
117132

118133
// wrapping with a LinkedScene to ensure full expansion when writing the non-linked file
119-
if ( boost::filesystem::path( file ).extension().string() != ".lscc" )
134+
if ( !linked( file ) )
120135
{
121136
m_liveScene = new LinkedScene( m_liveScene );
122137
}
@@ -137,6 +152,39 @@ int ROP_SceneCacheWriter::startRender( int nframes, fpreal s, fpreal e )
137152
return false;
138153
}
139154

155+
UT_String forceObjects;
156+
evalString( forceObjects, pForceObjects.getToken(), 0, 0 );
157+
158+
m_forceFilter = 0;
159+
if ( linked( file ) && !forceObjects.equal( "" ) )
160+
{
161+
// get the list of nodes matching the filter
162+
const PRM_SpareData *data = getParm( pForceObjects.getToken() ).getSparePtr();
163+
OBJ_Node *baseNode = OPgetDirector()->findNode( data->getOpRelative() )->castToOBJNode();
164+
OP_Bundle *bundle = getParmBundle( pForceObjects.getToken(), 0, forceObjects, baseNode, data->getOpFilter() );
165+
166+
// add all of the parent nodes
167+
UT_PtrArray<OP_Node *> nodes;
168+
bundle->getMembers( nodes );
169+
size_t numNodes = nodes.entries();
170+
for ( size_t i = 0; i < numNodes; ++i )
171+
{
172+
OP_Node *current = nodes[i]->getParent();
173+
while ( current )
174+
{
175+
bundle->addOp( current );
176+
current = current->getParent();
177+
}
178+
}
179+
180+
// build a matchable filter from all these nodes
181+
UT_WorkBuffer buffer;
182+
bundle->buildString( buffer );
183+
buffer.copyIntoString( forceObjects );
184+
m_forceFilter = new UT_StringMMPattern();
185+
m_forceFilter->compile( forceObjects );
186+
}
187+
140188
return true;
141189
}
142190

@@ -193,20 +241,47 @@ ROP_RENDER_CODE ROP_SceneCacheWriter::doWrite( const SceneInterface *liveScene,
193241
outScene->writeTransform( liveScene->readTransform( time ), time );
194242
}
195243

196-
bool link = false;
244+
Mode mode = NaturalExpand;
245+
const HoudiniScene *hScene = IECore::runTimeCast<const HoudiniScene>( liveScene );
246+
if ( hScene && m_forceFilter )
247+
{
248+
UT_String nodePath;
249+
hScene->node()->getFullPath( nodePath );
250+
mode = ( nodePath.multiMatch( *m_forceFilter ) ) ? ForcedExpand : ForcedLink;
251+
}
252+
197253
SceneInterface::NameList attrs;
198254
liveScene->attributeNames( attrs );
199255
for ( SceneInterface::NameList::iterator it = attrs.begin(); it != attrs.end(); ++it )
200256
{
201-
outScene->writeAttribute( *it, liveScene->readAttribute( *it, time ), time );
202257
if ( *it == LinkedScene::linkAttribute )
203258
{
204-
link = true;
259+
if ( mode == ForcedExpand )
260+
{
261+
continue;
262+
}
263+
264+
mode = NaturalLink;
265+
}
266+
267+
outScene->writeAttribute( *it, liveScene->readAttribute( *it, time ), time );
268+
}
269+
270+
if ( mode == ForcedLink )
271+
{
272+
const SceneCacheNode<OP_Node> *sceneNode = static_cast< const SceneCacheNode<OP_Node>* >( hScene->node() );
273+
if ( sceneNode )
274+
{
275+
ConstSceneInterfacePtr scene = sceneNode->scene();
276+
if ( scene )
277+
{
278+
IECore::runTimeCast<LinkedScene>( outScene )->writeLink( scene );
279+
return ROP_CONTINUE_RENDER;
280+
}
205281
}
206282
}
207283

208-
// If this is a link, we exit now, since all other write calls will throw exceptions
209-
if ( link )
284+
if ( mode == NaturalLink )
210285
{
211286
return ROP_CONTINUE_RENDER;
212287
}
@@ -243,3 +318,17 @@ ROP_RENDER_CODE ROP_SceneCacheWriter::doWrite( const SceneInterface *liveScene,
243318

244319
return ROP_CONTINUE_RENDER;
245320
}
321+
322+
bool ROP_SceneCacheWriter::updateParmsFlags()
323+
{
324+
UT_String value;
325+
evalString( value, pFile.getToken(), 0, 0 );
326+
std::string file = value.toStdString();
327+
enableParm( pForceObjects.getToken(), linked( file ) );
328+
return true;
329+
}
330+
331+
bool ROP_SceneCacheWriter::linked( const std::string &file ) const
332+
{
333+
return ( boost::filesystem::path( file ).extension().string() == ".lscc" );
334+
}

src/IECoreHoudini/SceneCacheNode.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,8 +365,13 @@ void SceneCacheNode<BaseType>::createMenu( PRM_Name *menu, const std::vector<std
365365
}
366366

367367
template<typename BaseType>
368-
ConstSceneInterfacePtr SceneCacheNode<BaseType>::scene()
368+
ConstSceneInterfacePtr SceneCacheNode<BaseType>::scene() const
369369
{
370+
if ( !this->hasParm( pFile.getToken() ) || !this->hasParm( pRoot.getToken() ) )
371+
{
372+
return 0;
373+
}
374+
370375
try
371376
{
372377
return this->scene( getFile(), getPath() );

test/IECoreHoudini/SceneCacheTest.py

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1353,7 +1353,7 @@ def compareScene( self, a, b, time = 0, bakedObjects = [], parentTransform = Non
13531353
IECore.TransformOp()( input=ma, copyInput=False, matrix=IECore.M44dData( parentTransform ) )
13541354
self.assertEqual( ma, mb )
13551355

1356-
self.assertEqual( a.childNames(), b.childNames() )
1356+
self.assertEqual( sorted( a.childNames() ), sorted( b.childNames() ) )
13571357
for child in a.childNames() :
13581358
self.compareScene( a.child( child ), b.child( child ), time = time, bakedObjects = bakedObjects, parentTransform = parentTransform )
13591359

@@ -1582,6 +1582,93 @@ def testRopLinked( self ) :
15821582
xform.parm( "expand" ).pressButton()
15831583
self.assertEqual( xform.parm( "root" ).menuItems(), ( "/", "/1", "/1/2" ) )
15841584

1585+
def testRopForceObjects( self ) :
1586+
1587+
s = self.writeSCC()
1588+
d = s.child( "1" ).createChild( "4" )
1589+
e = d.createChild( "5" )
1590+
box = IECore.MeshPrimitive.createBox(IECore.Box3f(IECore.V3f(0),IECore.V3f(1)))
1591+
d.writeObject( box, 0 )
1592+
e.writeObject( box, 0 )
1593+
1594+
del s, d, e
1595+
1596+
def testLinks( bakedObjects = None ) :
1597+
1598+
if os.path.exists( TestSceneCache.__testLinkedOutFile ) :
1599+
os.remove( TestSceneCache.__testLinkedOutFile )
1600+
1601+
rop.parm( "execute" ).pressButton()
1602+
self.assertEqual( rop.errors(), "" )
1603+
self.assertTrue( os.path.exists( TestSceneCache.__testLinkedOutFile ) )
1604+
linked = IECore.LinkedScene( TestSceneCache.__testLinkedOutFile, IECore.IndexedIO.OpenMode.Read )
1605+
if bakedObjects :
1606+
live = IECoreHoudini.HoudiniScene( xform.path(), rootPath = [ xform.name() ] )
1607+
self.compareScene( linked, live, bakedObjects = bakedObjects )
1608+
else :
1609+
orig = IECore.SceneCache( TestSceneCache.__testFile, IECore.IndexedIO.OpenMode.Read )
1610+
self.compareScene( orig, linked )
1611+
1612+
# make sure the links are where we expect
1613+
unlinked = IECore.SceneCache( TestSceneCache.__testLinkedOutFile, IECore.IndexedIO.OpenMode.Read )
1614+
a = unlinked.child( "1" )
1615+
self.assertFalse( a.hasAttribute( IECore.LinkedScene.linkAttribute ) )
1616+
self.assertFalse( a.hasAttribute( IECore.LinkedScene.fileNameLinkAttribute ) )
1617+
self.assertFalse( a.hasAttribute( IECore.LinkedScene.rootLinkAttribute ) )
1618+
self.assertFalse( a.hasAttribute( IECore.LinkedScene.timeLinkAttribute ) )
1619+
b = a.child( "2" )
1620+
self.assertEqual( b.childNames(), [] )
1621+
self.assertFalse( b.hasAttribute( IECore.LinkedScene.linkAttribute ) )
1622+
self.assertTrue( b.hasAttribute( IECore.LinkedScene.fileNameLinkAttribute ) )
1623+
self.assertTrue( b.hasAttribute( IECore.LinkedScene.rootLinkAttribute ) )
1624+
self.assertFalse( b.hasAttribute( IECore.LinkedScene.timeLinkAttribute ) )
1625+
self.assertEqual( b.readAttribute( IECore.LinkedScene.fileNameLinkAttribute, 0 ), IECore.StringData( TestSceneCache.__testFile ) )
1626+
self.assertEqual( b.readAttribute( IECore.LinkedScene.rootLinkAttribute, 0 ), IECore.InternedStringVectorData( [ "1", "2" ] ) )
1627+
d = a.child( "4" )
1628+
self.assertFalse( d.hasAttribute( IECore.LinkedScene.linkAttribute ) )
1629+
self.assertFalse( d.hasAttribute( IECore.LinkedScene.fileNameLinkAttribute ) )
1630+
self.assertFalse( d.hasAttribute( IECore.LinkedScene.rootLinkAttribute ) )
1631+
self.assertFalse( d.hasAttribute( IECore.LinkedScene.timeLinkAttribute ) )
1632+
1633+
# force b and below as links even though they are expanded
1634+
xform = self.xform()
1635+
xform.parm( "expand" ).pressButton()
1636+
rop = self.rop( xform )
1637+
rop.parm( "file" ).set( TestSceneCache.__testLinkedOutFile )
1638+
rop.parm( "forceObjects" ).set( "*4*" )
1639+
testLinks()
1640+
1641+
# make sure parents expand if their child is forced
1642+
rop.parm( "forceObjects" ).set( "*5*" )
1643+
testLinks()
1644+
1645+
# make sure normal geo gets expanded regardless
1646+
geo = xform.createNode( "geo", "real" )
1647+
geo.createNode( "box" )
1648+
testLinks( bakedObjects = [ "real" ] )
1649+
unlinked = IECore.SceneCache( TestSceneCache.__testLinkedOutFile, IECore.IndexedIO.OpenMode.Read )
1650+
real = unlinked.child( "real" )
1651+
self.assertFalse( real.hasAttribute( IECore.LinkedScene.linkAttribute ) )
1652+
self.assertFalse( real.hasAttribute( IECore.LinkedScene.fileNameLinkAttribute ) )
1653+
self.assertFalse( real.hasAttribute( IECore.LinkedScene.rootLinkAttribute ) )
1654+
self.assertFalse( real.hasAttribute( IECore.LinkedScene.timeLinkAttribute ) )
1655+
self.assertTrue( real.hasObject() )
1656+
geo.destroy()
1657+
1658+
# make sure natural links (unexpanded branches) still work
1659+
hou.node( xform.path() + "/1/2" ).parm( "collapse" ).pressButton()
1660+
testLinks()
1661+
1662+
# make sure normal SceneCaches aren't broken by forceObjects
1663+
rop.parm( "file" ).set( TestSceneCache.__testOutFile )
1664+
self.assertFalse( os.path.exists( TestSceneCache.__testOutFile ) )
1665+
rop.parm( "execute" ).pressButton()
1666+
self.assertEqual( rop.errors(), "" )
1667+
self.assertTrue( os.path.exists( TestSceneCache.__testOutFile ) )
1668+
orig = IECore.SceneCache( TestSceneCache.__testFile, IECore.IndexedIO.OpenMode.Read )
1669+
result = IECore.SceneCache( TestSceneCache.__testOutFile, IECore.IndexedIO.OpenMode.Read )
1670+
self.compareScene( orig, result )
1671+
15851672
def testRopErrors( self ) :
15861673

15871674
xform = self.xform()

0 commit comments

Comments
 (0)