Skip to content

Commit 059b91c

Browse files
IECoreUSD : Option for relative prototype paths inside a PointInstancer
If you set the env var IECOREUSD_POINTINSTANCER_RELATIVEPROTOTYPES=1, if an instancer named "/inst" contains an internal prototype path like "/inst/Prototypes/proto", that prototype path will now be loaded as "./Prototypes/proto". This is compatible with how point instancers will now be handled in Gaffer, allowing them to be relocated within the scene hierarchy.
1 parent 83864a1 commit 059b91c

File tree

4 files changed

+102
-1
lines changed

4 files changed

+102
-1
lines changed

Changes

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
10.5.x.x (relative to 10.5.12.0)
22
========
33

4+
Features
5+
--------
6+
7+
- PointInstancerAlgo : Added support for the env var IECOREUSD_POINTINSTANCER_RELATIVEPROTOTYPES. If this is set to "1", then when USD PointInstancers are loaded as point clouds, if they contain prototype paths beneath themselves in the hierarchy, those prototype paths will be loaded as relative paths, starting with "./". This aligns with how Gaffer will now handle prototype paths, and allows point instancers to be relocated in the hierarchy.
8+
49
Fixes
510
-----
611

contrib/IECoreUSD/src/IECoreUSD/PointInstancerAlgo.cpp

+36-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,19 @@ using namespace IECoreUSD;
5353
namespace
5454
{
5555

56+
bool checkEnvFlag( const char *envVar, bool def )
57+
{
58+
const char *value = getenv( envVar );
59+
if( value )
60+
{
61+
return std::string( value ) != "0";
62+
}
63+
else
64+
{
65+
return def;
66+
}
67+
}
68+
5669
IECore::ObjectPtr readPointInstancer( pxr::UsdGeomPointInstancer &pointInstancer, pxr::UsdTimeCode time, const Canceller *canceller )
5770
{
5871
pxr::VtVec3fArray pointsData;
@@ -108,16 +121,38 @@ IECore::ObjectPtr readPointInstancer( pxr::UsdGeomPointInstancer &pointInstancer
108121

109122
// Prototype paths
110123

124+
const static bool gafferUSDPointInstancersRelativeProtoypes = checkEnvFlag( "IECOREUSD_POINTINSTANCER_RELATIVEPROTOTYPES", false );
125+
111126
pxr::SdfPathVector targets;
112127
Canceller::check( canceller );
113128
pointInstancer.GetPrototypesRel().GetForwardedTargets( &targets );
114129

130+
const pxr::SdfPath &primPath = pointInstancer.GetPath();
131+
115132
IECore::StringVectorDataPtr prototypeRootsData = new IECore::StringVectorData();
116133
auto &prototypeRoots = prototypeRootsData->writable();
117134
prototypeRoots.reserve( targets.size() );
118135
for( const auto &t : targets )
119136
{
120-
prototypeRoots.push_back( t.GetString() );
137+
if( !gafferUSDPointInstancersRelativeProtoypes )
138+
{
139+
prototypeRoots.push_back( t.GetString() );
140+
}
141+
else
142+
{
143+
if( t.HasPrefix( primPath ) )
144+
{
145+
// The ./ prefix shouldn't be necessary - we want to just use the abscence of a leading
146+
// slash to indicate relative paths. We can remove the prefix here once we deprecate the
147+
// env var GAFFERSCENE_INSTANCER_EXPLICITABSOLUTEPATHS and have Gaffer always require a leading
148+
// slash for absolute paths
149+
prototypeRoots.push_back( "./" + t.MakeRelativePath( primPath ).GetString() );
150+
}
151+
else
152+
{
153+
prototypeRoots.push_back( t.GetString() );
154+
}
155+
}
121156
}
122157

123158
newPoints->variables["prototypeRoots"] = IECoreScene::PrimitiveVariable( IECoreScene::PrimitiveVariable::Constant, prototypeRootsData );

contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py

+35
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
import shutil
4343
import tempfile
4444
import imath
45+
import subprocess
46+
import sys
4547
import threading
4648
import time
4749

@@ -4486,6 +4488,39 @@ def testAssetPathSlashes ( self ) :
44864488
self.assertNotIn( "\\", xform.readAttribute( "render:testAsset", 0 ).value )
44874489
self.assertTrue( pathlib.Path( xform.readAttribute( "render:testAsset", 0 ).value ).is_file() )
44884490

4491+
4492+
@unittest.skipIf( os.environ.get("IECOREUSD_POINTINSTANCER_RELATIVEPROTOTYPES", "0") == "0", "Set IECOREUSD_POINTINSTANCER_RELATIVEPROTOTYPES to run relative version of this test." )
4493+
def testPointInstancerRelative( self ) :
4494+
root = IECoreScene.SceneInterface.create(
4495+
os.path.join( os.path.dirname( __file__ ), "data", "pointInstancerWeirdPrototypes.usda" ),
4496+
IECore.IndexedIO.OpenMode.Read
4497+
)
4498+
pointInstancer = root.child( "inst" )
4499+
obj = pointInstancer.readObject(0.0)
4500+
self.assertEqual( obj["prototypeRoots"].data, IECore.StringVectorData( [ './Prototypes/sphere', '/cube' ] ) )
4501+
4502+
def testPointInstancerRelativeRunner( self ) :
4503+
env = os.environ.copy()
4504+
env["IECOREUSD_POINTINSTANCER_RELATIVEPROTOTYPES"] = "1"
4505+
4506+
try :
4507+
subprocess.check_output(
4508+
[ sys.executable, __file__, "USDSceneTest.testPointInstancerRelative" ],
4509+
env = env, stderr = subprocess.STDOUT
4510+
)
4511+
except subprocess.CalledProcessError as e :
4512+
self.fail( e.output )
4513+
4514+
def testPointInstancerAbsolute( self ) :
4515+
root = IECoreScene.SceneInterface.create(
4516+
os.path.join( os.path.dirname( __file__ ), "data", "pointInstancerWeirdPrototypes.usda" ),
4517+
IECore.IndexedIO.OpenMode.Read
4518+
)
4519+
pointInstancer = root.child( "inst" )
4520+
obj = pointInstancer.readObject(0.0)
4521+
self.assertEqual( obj["prototypeRoots"].data, IECore.StringVectorData( [ '/inst/Prototypes/sphere', '/cube' ] ) )
4522+
4523+
44894524
@unittest.skipIf( not haveVDB, "No IECoreVDB" )
44904525
def testUsdVolVolumeSlashes( self ) :
44914526

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#usda 1.0
2+
(
3+
)
4+
5+
def PointInstancer "inst" (
6+
kind = "group"
7+
)
8+
{
9+
point3f[] positions = [(0, 0, -20), (0, 0, -16), (0, 0, -12), (0, 0, -8), (0, 0, -4), (0, 0, 0), (0, 0, 4), (0, 0, 8), (0, 0, 12), (0, 0, 16)]
10+
int[] protoIndices = [0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
11+
rel prototypes = [ </inst/Prototypes/sphere>, </cube> ]
12+
13+
def Scope "Prototypes" (
14+
kind = "group"
15+
)
16+
{
17+
def Sphere "sphere"
18+
{
19+
double radius = 1
20+
}
21+
}
22+
}
23+
24+
def Cube "cube"
25+
{
26+
}

0 commit comments

Comments
 (0)