Skip to content

Commit 3c163a8

Browse files
authored
Merge pull request #1451 from danieldresser-ie/usdInstRel
IECoreUSD : Load prototype paths inside a PointInstancer as relative
2 parents 4f36947 + 1560cef commit 3c163a8

File tree

4 files changed

+96
-1
lines changed

4 files changed

+96
-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

+29-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,31 @@ IECore::ObjectPtr readPointInstancer( pxr::UsdGeomPointInstancer &pointInstancer
108121

109122
// Prototype paths
110123

124+
const static bool g_relativePrototypes = checkEnvFlag( "IECOREUSD_POINTINSTANCER_RELATIVE_PROTOTYPES", 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( !g_relativePrototypes || !t.HasPrefix( primPath ) )
138+
{
139+
prototypeRoots.push_back( t.GetString() );
140+
}
141+
else
142+
{
143+
// The ./ prefix shouldn't be necessary - we want to just use the absence of a leading
144+
// slash to indicate relative paths. We can remove the prefix here once we deprecate the
145+
// GAFFERSCENE_INSTANCER_EXPLICIT_ABSOLUTE_PATHS env var and have Gaffer always require a leading
146+
// slash for absolute paths.
147+
prototypeRoots.push_back( "./" + t.MakeRelativePath( primPath ).GetString() );
148+
}
121149
}
122150

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

contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py

+36
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,40 @@ 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+
def _testPointInstancerRelativePrototypes( self ) :
4492+
4493+
root = IECoreScene.SceneInterface.create(
4494+
os.path.join( os.path.dirname( __file__ ), "data", "pointInstancerWeirdPrototypes.usda" ),
4495+
IECore.IndexedIO.OpenMode.Read
4496+
)
4497+
pointInstancer = root.child( "inst" )
4498+
obj = pointInstancer.readObject(0.0)
4499+
4500+
if os.environ.get( "IECOREUSD_POINTINSTANCER_RELATIVE_PROTOTYPES", "0" ) != "0" :
4501+
self.assertEqual( obj["prototypeRoots"].data, IECore.StringVectorData( [ './Prototypes/sphere', '/cube' ] ) )
4502+
else :
4503+
self.assertEqual( obj["prototypeRoots"].data, IECore.StringVectorData( [ '/inst/Prototypes/sphere', '/cube' ] ) )
4504+
4505+
def testPointInstancerRelativePrototypes( self ) :
4506+
4507+
for relative in [ "0", "1", None ] :
4508+
4509+
with self.subTest( relative = relative ) :
4510+
4511+
env = os.environ.copy()
4512+
if relative is not None :
4513+
env["IECOREUSD_POINTINSTANCER_RELATIVE_PROTOTYPES"] = relative
4514+
else :
4515+
env.pop( "IECOREUSD_POINTINSTANCER_RELATIVE_PROTOTYPES", None )
4516+
4517+
try :
4518+
subprocess.check_output(
4519+
[ sys.executable, __file__, "USDSceneTest._testPointInstancerRelativePrototypes" ],
4520+
env = env, stderr = subprocess.STDOUT
4521+
)
4522+
except subprocess.CalledProcessError as e :
4523+
self.fail( e.output )
4524+
44894525
@unittest.skipIf( not haveVDB, "No IECoreVDB" )
44904526
def testUsdVolVolumeSlashes( self ) :
44914527

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)