Skip to content

Commit e3d5682

Browse files
authored
Merge pull request #1233 from johnhaddon/usdVaryingLengthArrays
USDScene : Support Houdini's varying-length-array-per-vertex primvars
2 parents b071e59 + 33c6b49 commit e3d5682

File tree

6 files changed

+96
-44
lines changed

6 files changed

+96
-44
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ jobs:
9090
buildType: RELWITHDEBINFO
9191
options: .github/workflows/main/options.windows
9292
dependenciesURL: https://github.com/hypothetical-inc/gafferDependencies/releases/download/3.1.1/gafferDependencies-3.1.1-Python3-windows.zip
93-
tests: testCore testCorePython testScene testImage testAlembic testVDB
93+
tests: testCore testCorePython testScene testImage testAlembic testUSD testVDB
9494
publish: false
9595

9696
runs-on: ${{ matrix.os }}

contrib/IECoreUSD/include/IECoreUSD/AttributeAlgo.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ IECORE_PUSH_DEFAULT_VISIBILITY
4343
#include "pxr/base/tf/token.h"
4444
#include "pxr/usd/usd/attribute.h"
4545
#include "pxr/usd/usd/prim.h"
46+
#include "pxr/usd/usdGeom/primvar.h"
4647
IECORE_POP_DEFAULT_VISIBILITY
4748

4849
// AttributeAlgo is suite of utilities for loading/writing Cortex/USD Attributes.
@@ -53,8 +54,6 @@ namespace IECoreUSD
5354
namespace AttributeAlgo
5455
{
5556

56-
57-
5857
// Find a UsdAttribute under the given prim which matches the given cortex name. This UsdAttribute
5958
// could be either a constant primvar or a custom attribute with an appropriate name. If no matching
6059
// UsdAttribute is found, returns an invalid UsdAttribute
@@ -66,6 +65,10 @@ IECOREUSD_API pxr::UsdAttribute findUSDAttribute( const pxr::UsdPrim &prim, std:
6665
// or a non-custom primvar, then an empty string is returned.
6766
IECOREUSD_API IECore::InternedString cortexAttributeName( const pxr::UsdAttribute &attribute );
6867

68+
// Returns true if the primvar should be loaded as a Cortex attribute rather than
69+
// a PrimitiveVariable.
70+
IECOREUSD_API bool isCortexAttribute( const pxr::UsdGeomPrimvar &primVar );
71+
6972
struct Name
7073
{
7174
pxr::TfToken name;

contrib/IECoreUSD/src/IECoreUSD/AttributeAlgo.cpp

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -45,42 +45,56 @@ IECORE_POP_DEFAULT_VISIBILITY
4545
using namespace IECoreUSD;
4646
using namespace pxr;
4747

48-
4948
namespace
5049
{
50+
5151
static const pxr::TfToken g_cortexPrimitiveVariableMetadataToken( "cortex_isConstantPrimitiveVariable" );
5252
static const pxr::TfToken g_cortexPrimitiveVariableMetadataTokenDeprecated( "IECOREUSD_CONSTANT_PRIMITIVE_VARIABLE" );
5353
static const std::string g_primVarPrefix = "primvars:";
5454
static const std::string g_primVarUserPrefix = "primvars:user:";
5555
static const std::string g_renderPrefix = "render:";
5656
static const std::string g_userPrefix = "user:";
5757

58-
// Cortex Attribute in USD are converted to constant primvar because they behave similarly ( hierarchy inheritance and availability for shading )
59-
// so we ignore non constant primvar as well as constant primvar that are "tagged" with a special metadata `IECOREUSD_CONSTANT_PRIMITIVE_VARIABLE`
60-
bool isCortexAttribute( const pxr::UsdGeomPrimvar &primVar )
58+
}
59+
60+
bool IECoreUSD::AttributeAlgo::isCortexAttribute( const pxr::UsdGeomPrimvar &primVar )
6161
{
62-
if( primVar.GetInterpolation() == pxr::UsdGeomTokens->constant )
62+
if( primVar.GetInterpolation() != pxr::UsdGeomTokens->constant )
6363
{
64-
// skip any non const prim vars or with metadata for Cortex const Primitive Variable
65-
pxr::VtValue metadataValue;
66-
if( primVar.GetAttr().GetMetadata( AttributeAlgo::cortexPrimitiveVariableMetadataToken(), &metadataValue ) )
67-
{
68-
return !metadataValue.Get<bool>();
69-
}
70-
else if( primVar.GetAttr().GetMetadata( AttributeAlgo::cortexPrimitiveVariableMetadataTokenDeprecated(), &metadataValue ) )
71-
{
72-
return !metadataValue.Get<bool>();
73-
}
74-
// constant prim var without the metadata then it's a Cortex attribute
75-
else
64+
return false;
65+
}
66+
67+
// We have a constant primvar. Check the metadata to see if it has been
68+
// tagged as a true primvar and not an attribute. If the metadata exists,
69+
// that is the final word on the matter.
70+
71+
pxr::VtValue metadataValue;
72+
if( primVar.GetAttr().GetMetadata( AttributeAlgo::cortexPrimitiveVariableMetadataToken(), &metadataValue ) )
73+
{
74+
return !metadataValue.Get<bool>();
75+
}
76+
else if( primVar.GetAttr().GetMetadata( AttributeAlgo::cortexPrimitiveVariableMetadataTokenDeprecated(), &metadataValue ) )
77+
{
78+
return !metadataValue.Get<bool>();
79+
}
80+
81+
// Check for companion `<name>:lengths` primvar. This is a convention
82+
// Houdini uses to store varying-length-array primvars per-vertex or
83+
// per-face. We want to load the two primvars side by side as primitive
84+
// variables.
85+
86+
const TfToken lengthsName( primVar.GetName().GetString() + ":lengths" );
87+
if( auto lengthsPrimVar = UsdGeomPrimvarsAPI( primVar.GetAttr().GetPrim() ).GetPrimvar( lengthsName ) )
88+
{
89+
if( lengthsPrimVar.GetInterpolation() != pxr::UsdGeomTokens->constant )
7690
{
77-
return true;
91+
return false;
7892
}
7993
}
8094

81-
return false;
82-
}
95+
// Everything else should be loaded as a Cortex attribute.
8396

97+
return true;
8498
}
8599

86100
pxr::TfToken IECoreUSD::AttributeAlgo::cortexPrimitiveVariableMetadataToken()
@@ -228,3 +242,4 @@ IECore::InternedString IECoreUSD::AttributeAlgo::cortexAttributeName( const pxr:
228242
}
229243
return IECore::InternedString();
230244
}
245+

contrib/IECoreUSD/src/IECoreUSD/PrimitiveAlgo.cpp

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -439,28 +439,9 @@ void IECoreUSD::PrimitiveAlgo::readPrimitiveVariables( const pxr::UsdGeomPrimvar
439439
continue;
440440
}
441441

442-
if ( primVar.GetInterpolation() == pxr::UsdGeomTokens->constant )
442+
if( AttributeAlgo::isCortexAttribute( primVar ) )
443443
{
444-
pxr::VtValue metadataValue;
445-
if ( primVar.GetAttr().GetMetadata( AttributeAlgo::cortexPrimitiveVariableMetadataToken(), &metadataValue ) )
446-
{
447-
if ( !metadataValue.Get<bool>() )
448-
{
449-
continue;
450-
}
451-
}
452-
else if ( primVar.GetAttr().GetMetadata( AttributeAlgo::cortexPrimitiveVariableMetadataTokenDeprecated(), &metadataValue ) )
453-
{
454-
if ( !metadataValue.Get<bool>() )
455-
{
456-
continue;
457-
}
458-
}
459-
// constant prim var without the metadata then it's a Cortex Attribute so we skip it as PrimitiveVariable.
460-
else
461-
{
462-
continue;
463-
}
444+
continue;
464445
}
465446

466447
bool constantAcceptsArray = true;

contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2900,5 +2900,36 @@ def testShadersAcrossDifferentScopes( self ):
29002900
) ]
29012901
)
29022902

2903+
def testHoudiniVaryingLengthArrayPrimVar( self ) :
2904+
2905+
root = IECoreScene.SceneInterface.create(
2906+
os.path.join( os.path.dirname( __file__ ), "data", "houdiniVaryingLengthArrayPrimVar.usda" ),
2907+
IECore.IndexedIO.OpenMode.Read
2908+
)
2909+
2910+
points = root.child( "plane" )
2911+
self.assertEqual( points.attributeNames(), [] )
2912+
self.assertFalse( points.hasAttribute( "varyingLengthArray" ) )
2913+
2914+
primitive = points.readObject( 1.0 )
2915+
self.assertEqual(
2916+
set( primitive.keys() ),
2917+
{ "P", "varyingLengthArray", "varyingLengthArray:lengths" }
2918+
)
2919+
self.assertEqual(
2920+
primitive["varyingLengthArray"],
2921+
IECoreScene.PrimitiveVariable(
2922+
IECoreScene.PrimitiveVariable.Interpolation.Constant,
2923+
IECore.FloatVectorData( [ 1, 1, 1, 2, 3, 3, 3, 3, 4 ] )
2924+
)
2925+
)
2926+
self.assertEqual(
2927+
primitive["varyingLengthArray:lengths"],
2928+
IECoreScene.PrimitiveVariable(
2929+
IECoreScene.PrimitiveVariable.Interpolation.Vertex,
2930+
IECore.IntVectorData( [ 3, 1, 4, 1 ] )
2931+
)
2932+
)
2933+
29032934
if __name__ == "__main__":
29042935
unittest.main()
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#usda 1.0
2+
3+
def Points "plane"
4+
{
5+
point3f[] points = [(-0.5, -0.5, 0), (0.5, -0.5, 0), (-0.5, 0.5, 0), (0.5, 0.5, 0)]
6+
7+
# USD doesn't allow array primvars with varying-length per vertex.
8+
# Houdini works around this by writing two primvars :
9+
#
10+
# - A constant one with all the arrays concatenated together
11+
# - A vertex one with the lengths of the array per vertex
12+
13+
float[] primvars:varyingLengthArray = [ 1, 1, 1, 2, 3, 3, 3, 3, 4 ] (
14+
interpolation = "constant"
15+
)
16+
17+
int[] primvars:varyingLengthArray:lengths = [ 3, 1, 4, 1 ] (
18+
interpolation = "vertex"
19+
)
20+
21+
}
22+

0 commit comments

Comments
 (0)