Skip to content

Commit fe78f8f

Browse files
committed
Merge pull request #173 from andrewkaufman/geometryTypes
Houdini SceneCache Geometry Types
2 parents 6317b70 + 456f9b4 commit fe78f8f

File tree

5 files changed

+192
-19
lines changed

5 files changed

+192
-19
lines changed

include/IECoreHoudini/SceneCacheNode.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ class SceneCacheNode : public BaseType
9898
{
9999
Cortex,
100100
Houdini,
101+
BoundingBox,
102+
PointCloud
101103
};
102104

103105
/// convenience methods for the common parameters;

src/IECoreHoudini/SOP_SceneCacheSource.cpp

Lines changed: 77 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040

4141
#include "IECore/CoordinateSystem.h"
4242
#include "IECore/Group.h"
43+
#include "IECore/MeshPrimitive.h"
44+
#include "IECore/PointsPrimitive.h"
4345
#include "IECore/TransformOp.h"
4446
#include "IECore/VisibleRenderable.h"
4547

@@ -268,24 +270,8 @@ void SOP_SceneCacheSource::loadObjects( const IECore::SceneInterface *scene, Ima
268270

269271
if ( scene->hasObject() && UT_String( scene->name() ).multiMatch( params.shapeFilter ) && tagged( scene, params.tagFilter ) )
270272
{
271-
ConstObjectPtr object = scene->readObject( time );
272273
std::string name = relativePath( scene, rootSize );
273274

274-
params.hasAnimatedTopology = scene->hasAttribute( SceneCache::animatedObjectTopologyAttribute );
275-
params.hasAnimatedPrimVars = scene->hasAttribute( SceneCache::animatedObjectPrimVarsAttribute );
276-
if ( params.hasAnimatedPrimVars )
277-
{
278-
const ConstObjectPtr animatedPrimVarObj = scene->readAttribute( SceneCache::animatedObjectPrimVarsAttribute, 0 );
279-
const InternedStringVectorData *animatedPrimVarData = IECore::runTimeCast<const InternedStringVectorData>( animatedPrimVarObj );
280-
if ( animatedPrimVarData )
281-
{
282-
const std::vector<InternedString> &values = animatedPrimVarData->readable();
283-
params.animatedPrimVars.clear();
284-
params.animatedPrimVars.resize( values.size() );
285-
std::copy( values.begin(), values.end(), params.animatedPrimVars.begin() );
286-
}
287-
}
288-
289275
Imath::M44d currentTransform;
290276
if ( space == Local )
291277
{
@@ -296,6 +282,58 @@ void SOP_SceneCacheSource::loadObjects( const IECore::SceneInterface *scene, Ima
296282
currentTransform = transform;
297283
}
298284

285+
ConstObjectPtr object = 0;
286+
if ( params.geometryType == BoundingBox )
287+
{
288+
Imath::Box3d bound = scene->readBound( time );
289+
object = MeshPrimitive::createBox( Imath::Box3f( bound.min, bound.max ) );
290+
291+
params.hasAnimatedTopology = false;
292+
params.hasAnimatedPrimVars = true;
293+
params.animatedPrimVars.clear();
294+
params.animatedPrimVars.push_back( "P" );
295+
}
296+
else if ( params.geometryType == PointCloud )
297+
{
298+
std::vector<Imath::V3f> point( 1, scene->readBound( time ).center() );
299+
PointsPrimitivePtr points = new PointsPrimitive( new V3fVectorData( point ) );
300+
std::vector<Imath::V3f> basis1( 1, Imath::V3f( currentTransform[0][0], currentTransform[0][1], currentTransform[0][2] ) );
301+
std::vector<Imath::V3f> basis2( 1, Imath::V3f( currentTransform[1][0], currentTransform[1][1], currentTransform[1][2] ) );
302+
std::vector<Imath::V3f> basis3( 1, Imath::V3f( currentTransform[2][0], currentTransform[2][1], currentTransform[2][2] ) );
303+
points->variables["basis1"] = PrimitiveVariable( PrimitiveVariable::Vertex, new V3fVectorData( basis1 ) );
304+
points->variables["basis2"] = PrimitiveVariable( PrimitiveVariable::Vertex, new V3fVectorData( basis2 ) );
305+
points->variables["basis3"] = PrimitiveVariable( PrimitiveVariable::Vertex, new V3fVectorData( basis3 ) );
306+
307+
params.hasAnimatedTopology = false;
308+
params.hasAnimatedPrimVars = true;
309+
params.animatedPrimVars.clear();
310+
params.animatedPrimVars.push_back( "P" );
311+
params.animatedPrimVars.push_back( "basis1" );
312+
params.animatedPrimVars.push_back( "basis2" );
313+
params.animatedPrimVars.push_back( "basis3" );
314+
315+
object = points;
316+
}
317+
else
318+
{
319+
object = scene->readObject( time );
320+
321+
params.hasAnimatedTopology = scene->hasAttribute( SceneCache::animatedObjectTopologyAttribute );
322+
params.hasAnimatedPrimVars = scene->hasAttribute( SceneCache::animatedObjectPrimVarsAttribute );
323+
if ( params.hasAnimatedPrimVars )
324+
{
325+
const ConstObjectPtr animatedPrimVarObj = scene->readAttribute( SceneCache::animatedObjectPrimVarsAttribute, 0 );
326+
const InternedStringVectorData *animatedPrimVarData = IECore::runTimeCast<const InternedStringVectorData>( animatedPrimVarObj );
327+
if ( animatedPrimVarData )
328+
{
329+
const std::vector<InternedString> &values = animatedPrimVarData->readable();
330+
params.animatedPrimVars.clear();
331+
params.animatedPrimVars.resize( values.size() );
332+
std::copy( values.begin(), values.end(), params.animatedPrimVars.begin() );
333+
}
334+
}
335+
}
336+
299337
// modify the object if necessary
300338
object = modifyObject( object, params );
301339

@@ -449,10 +487,31 @@ bool SOP_SceneCacheSource::convertObject( const IECore::Object *object, const st
449487
return false;
450488
}
451489

452-
// attempt to optimize the conversion by re-using animated primitive variables
453490
const Primitive *primitive = IECore::runTimeCast<const Primitive>( object );
491+
492+
// attempt to optimize the conversion by re-using animated primitive variables
493+
/// \todo: This offset loop is only used because GU_Detail::getRangeByValue is
494+
/// terribly slow. Replace this if SideFx improves that function.
495+
GA_OffsetList offsets;
454496
GA_ROAttributeRef nameAttrRef = gdp->findStringTuple( GA_ATTRIB_PRIMITIVE, "name" );
455-
GA_Range primRange = gdp->getRangeByValue( nameAttrRef, name.c_str() );
497+
if ( nameAttrRef.isValid() )
498+
{
499+
const GA_Attribute *nameAttr = nameAttrRef.getAttribute();
500+
const GA_AIFSharedStringTuple *tuple = nameAttr->getAIFSharedStringTuple();
501+
502+
GA_Range range = gdp->getPrimitiveRange();
503+
for ( GA_Iterator it = range.begin(); !it.atEnd(); ++it )
504+
{
505+
const char *currentName = tuple->getString( nameAttr, it.getOffset(), 0 );
506+
if ( UT_String( currentName ).equal( name.c_str() ) )
507+
{
508+
offsets.append( it.getOffset() );
509+
}
510+
}
511+
}
512+
513+
GA_Range primRange( gdp->getPrimitiveMap(), offsets );
514+
456515
if ( primitive && !params.hasAnimatedTopology && params.hasAnimatedPrimVars && nameAttrRef.isValid() && !primRange.isEmpty() )
457516
{
458517
// this means constant topology and primitive variables, even though multiple samples were written

src/IECoreHoudini/SceneCacheNode.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ static PRM_Name spaceNames[] = {
116116
static PRM_Name geometryTypes[] = {
117117
PRM_Name( "0", "Cortex Primitives" ),
118118
PRM_Name( "1", "Houdini Geometry" ),
119+
PRM_Name( "2", "Bounding Boxes" ),
120+
PRM_Name( "3", "Point Cloud" ),
119121
PRM_Name( 0 ) // sentinal
120122
};
121123

src/IECoreHoudini/bindings/SceneCacheNodeBinding.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ void IECoreHoudini::bindSceneCacheNode()
133133
enum_<SceneCacheNode<OP_Node>::GeometryType>( "GeometryType" )
134134
.value( "Cortex", SceneCacheNode<OP_Node>::Cortex )
135135
.value( "Houdini", SceneCacheNode<OP_Node>::Houdini )
136+
.value( "BoundingBox", SceneCacheNode<OP_Node>::BoundingBox )
137+
.value( "PointCloud", SceneCacheNode<OP_Node>::PointCloud )
136138
;
137139

138140
enum_<OBJ_SceneCacheTransform::Hierarchy>( "Hierarchy" )

test/IECoreHoudini/SceneCacheTest.py

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1447,7 +1447,7 @@ def testNode( node ) :
14471447
testNode( self.xform() )
14481448
testNode( self.geometry() )
14491449

1450-
def writeAnimSCC( self ) :
1450+
def writeAnimSCC( self, rotate = False ) :
14511451

14521452
scene = self.writeSCC()
14531453
sc1 = scene.child( str( 1 ) )
@@ -1458,14 +1458,20 @@ def writeAnimSCC( self ) :
14581458
for time in [ 0.5, 1, 1.5, 2, 5, 10 ] :
14591459

14601460
matrix = IECore.M44d.createTranslated( IECore.V3d( 1, time, 0 ) )
1461+
if rotate :
1462+
matrix.rotate( IECore.V3d( time, 0, 0 ) )
14611463
sc1.writeTransform( IECore.M44dData( matrix ), time )
14621464

14631465
mesh["Cs"] = IECore.PrimitiveVariable( IECore.PrimitiveVariable.Interpolation.Uniform, IECore.V3fVectorData( [ IECore.V3f( time, 1, 0 ) ] * 6 ) )
14641466
sc2.writeObject( mesh, time )
14651467
matrix = IECore.M44d.createTranslated( IECore.V3d( 2, time, 0 ) )
1468+
if rotate :
1469+
matrix.rotate( IECore.V3d( time, 0, 0 ) )
14661470
sc2.writeTransform( IECore.M44dData( matrix ), time )
14671471

14681472
matrix = IECore.M44d.createTranslated( IECore.V3d( 3, time, 0 ) )
1473+
if rotate :
1474+
matrix.rotate( IECore.V3d( time, 0, 0 ) )
14691475
sc3.writeTransform( IECore.M44dData( matrix ), time )
14701476

14711477
return scene
@@ -2575,6 +2581,108 @@ def testTransformOverride( self ) :
25752581
self.assertEqual( IECore.M44d( list(b.parmTransform().asTuple()) ), IECore.M44d.createTranslated( IECore.V3d( 2, time, 0 ) ) )
25762582
self.assertEqual( IECore.M44d( list(c.parmTransform().asTuple()) ), IECore.M44d.createTranslated( IECore.V3d( time, 0, 0 ) ) )
25772583

2584+
def testGeometryTypes( self ) :
2585+
2586+
self.writeAnimSCC()
2587+
times = range( 0, 10 )
2588+
halves = [ x + 0.5 for x in times ]
2589+
quarters = [ x + 0.25 for x in times ]
2590+
times.extend( [ x + 0.75 for x in times ] )
2591+
times.extend( halves )
2592+
times.extend( quarters )
2593+
times.sort()
2594+
2595+
spf = 1.0 / hou.fps()
2596+
2597+
node = self.sop()
2598+
node.parm( "geometryType" ).set( IECoreHoudini.SceneCacheNode.GeometryType.Cortex )
2599+
for time in times :
2600+
hou.setTime( time - spf )
2601+
prims = node.geometry().prims()
2602+
self.assertEqual( len(prims), 3 )
2603+
nameAttr = node.geometry().findPrimAttrib( "name" )
2604+
self.assertEqual( nameAttr.strings(), tuple( [ '/1', '/1/2', '/1/2/3' ] ) )
2605+
for name in nameAttr.strings() :
2606+
self.assertEqual( len([ x for x in prims if x.attribValue( "name" ) == name ]), 1 )
2607+
self.assertEqual( prims[0].vertices()[0].point().position(), hou.Vector3( 1.5, time + 0.5, 0.5 ) )
2608+
self.assertEqual( prims[1].vertices()[0].point().position(), hou.Vector3( 3.5, time*2 + 0.5, 0.5 ) )
2609+
self.assertEqual( prims[2].vertices()[0].point().position(), hou.Vector3( 6.5, time*3 + 0.5, 0.5 ) )
2610+
2611+
node.parm( "geometryType" ).set( IECoreHoudini.SceneCacheNode.GeometryType.Houdini )
2612+
for time in times :
2613+
hou.setTime( time - spf )
2614+
prims = node.geometry().prims()
2615+
self.assertEqual( len(prims), 18 )
2616+
nameAttr = node.geometry().findPrimAttrib( "name" )
2617+
self.assertEqual( nameAttr.strings(), tuple( [ '/1', '/1/2', '/1/2/3' ] ) )
2618+
for name in nameAttr.strings() :
2619+
self.assertEqual( len([ x for x in prims if x.attribValue( "name" ) == name ]), 6 )
2620+
self.assertEqual( prims[0].vertex( 0 ).point().position(), hou.Vector3( 1, time, 0 ) )
2621+
self.assertEqual( prims[4].vertex( 0 ).point().position(), hou.Vector3( 2, time + 1, 1 ) )
2622+
self.assertEqual( prims[6].vertex( 0 ).point().position(), hou.Vector3( 3, time*2, 0 ) )
2623+
self.assertEqual( prims[10].vertex( 0 ).point().position(), hou.Vector3( 4, time*2 + 1, 1 ) )
2624+
self.assertEqual( prims[12].vertex( 0 ).point().position(), hou.Vector3( 6, time*3, 0 ) )
2625+
self.assertEqual( prims[16].vertex( 0 ).point().position(), hou.Vector3( 7, time*3 + 1, 1 ) )
2626+
2627+
node.parm( "geometryType" ).set( IECoreHoudini.SceneCacheNode.GeometryType.BoundingBox )
2628+
for time in times :
2629+
hou.setTime( time - spf )
2630+
prims = node.geometry().prims()
2631+
self.assertEqual( len(prims), 18 )
2632+
nameAttr = node.geometry().findPrimAttrib( "name" )
2633+
self.assertEqual( nameAttr.strings(), tuple( [ '/1', '/1/2', '/1/2/3' ] ) )
2634+
for name in nameAttr.strings() :
2635+
self.assertEqual( len([ x for x in prims if x.attribValue( "name" ) == name ]), 6 )
2636+
self.assertEqual( prims[0].vertex( 0 ).point().position(), hou.Vector3( 1, time, 0 ) )
2637+
self.assertEqual( prims[4].vertex( 0 ).point().position(), hou.Vector3( 7, time*3 + 1, 1 ) )
2638+
self.assertEqual( prims[6].vertex( 0 ).point().position(), hou.Vector3( 3, time*2, 0 ) )
2639+
self.assertEqual( prims[10].vertex( 0 ).point().position(), hou.Vector3( 7, time*3 + 1, 1 ) )
2640+
self.assertEqual( prims[12].vertex( 0 ).point().position(), hou.Vector3( 6, time*3, 0 ) )
2641+
self.assertEqual( prims[16].vertex( 0 ).point().position(), hou.Vector3( 7, time*3 + 1, 1 ) )
2642+
2643+
# re-write with rotation to prove point cloud basis vectors are accurate
2644+
self.writeAnimSCC( rotate = True )
2645+
2646+
scene = IECore.SharedSceneInterfaces.get( TestSceneCache.__testFile )
2647+
a = scene.child( "1" )
2648+
b = a.child( "2" )
2649+
c = b.child( "3" )
2650+
2651+
node.parm( "reload" ).pressButton()
2652+
node.parm( "geometryType" ).set( IECoreHoudini.SceneCacheNode.GeometryType.PointCloud )
2653+
for time in times :
2654+
hou.setTime( time - spf )
2655+
prims = node.geometry().prims()
2656+
self.assertEqual( len(prims), 3 )
2657+
nameAttr = node.geometry().findPrimAttrib( "name" )
2658+
self.assertEqual( nameAttr.strings(), tuple( [ '/1', '/1/2', '/1/2/3' ] ) )
2659+
for name in nameAttr.strings() :
2660+
self.assertEqual( len([ x for x in prims if x.attribValue( "name" ) == name ]), 1 )
2661+
2662+
aPoint = prims[0].vertices()[0].point()
2663+
bPoint = prims[1].vertices()[0].point()
2664+
cPoint = prims[2].vertices()[0].point()
2665+
2666+
aWorld = scene.readTransformAsMatrix( time ) * a.readTransformAsMatrix( time )
2667+
bWorld = aWorld * b.readTransformAsMatrix( time )
2668+
cWorld = bWorld * c.readTransformAsMatrix( time )
2669+
2670+
self.assertTrue( IECore.V3d( list(aPoint.position()) ).equalWithAbsError( aWorld.multVecMatrix( a.readBound( time ).center() ), 1e-6 ) )
2671+
self.assertTrue( IECore.V3d( list(bPoint.position()) ).equalWithAbsError( bWorld.multVecMatrix( b.readBound( time ).center() ), 1e-6 ) )
2672+
self.assertTrue( IECore.V3d( list(cPoint.position()) ).equalWithAbsError( cWorld.multVecMatrix( c.readBound( time ).center() ), 1e-6 ) )
2673+
2674+
self.assertTrue( IECore.V3d( list(aPoint.attribValue( "basis1" )) ).equalWithAbsError( IECore.V3d( aWorld[(0,0)], aWorld[(0,1)], aWorld[(0,2)] ), 1e-6 ) )
2675+
self.assertTrue( IECore.V3d( list(aPoint.attribValue( "basis2" )) ).equalWithAbsError( IECore.V3d( aWorld[(1,0)], aWorld[(1,1)], aWorld[(1,2)] ), 1e-6 ) )
2676+
self.assertTrue( IECore.V3d( list(aPoint.attribValue( "basis3" )) ).equalWithAbsError( IECore.V3d( aWorld[(2,0)], aWorld[(2,1)], aWorld[(2,2)] ), 1e-6 ) )
2677+
2678+
self.assertTrue( IECore.V3d( list(bPoint.attribValue( "basis1" )) ).equalWithAbsError( IECore.V3d( bWorld[(0,0)], bWorld[(0,1)], bWorld[(0,2)] ), 1e-6 ) )
2679+
self.assertTrue( IECore.V3d( list(bPoint.attribValue( "basis2" )) ).equalWithAbsError( IECore.V3d( bWorld[(1,0)], bWorld[(1,1)], bWorld[(1,2)] ), 1e-6 ) )
2680+
self.assertTrue( IECore.V3d( list(bPoint.attribValue( "basis3" )) ).equalWithAbsError( IECore.V3d( bWorld[(2,0)], bWorld[(2,1)], bWorld[(2,2)] ), 1e-6 ) )
2681+
2682+
self.assertTrue( IECore.V3d( list(cPoint.attribValue( "basis1" )) ).equalWithAbsError( IECore.V3d( cWorld[(0,0)], cWorld[(0,1)], cWorld[(0,2)] ), 1e-6 ) )
2683+
self.assertTrue( IECore.V3d( list(cPoint.attribValue( "basis2" )) ).equalWithAbsError( IECore.V3d( cWorld[(1,0)], cWorld[(1,1)], cWorld[(1,2)] ), 1e-6 ) )
2684+
self.assertTrue( IECore.V3d( list(cPoint.attribValue( "basis3" )) ).equalWithAbsError( IECore.V3d( cWorld[(2,0)], cWorld[(2,1)], cWorld[(2,2)] ), 1e-6 ) )
2685+
25782686
def tearDown( self ) :
25792687

25802688
for f in [ TestSceneCache.__testFile, TestSceneCache.__testOutFile, TestSceneCache.__testLinkedOutFile, TestSceneCache.__testHip, TestSceneCache.__testBgeo, TestSceneCache.__testBgeoGz, TestSceneCache.__testGeo ] :

0 commit comments

Comments
 (0)