@@ -46,6 +46,7 @@ IECORE_PUSH_DEFAULT_VISIBILITY
4646#include " pxr/base/gf/matrix4f.h"
4747#include " pxr/base/gf/matrix4d.h"
4848#include " pxr/usd/usdGeom/mesh.h"
49+ #include " pxr/usd/usdGeom/subset.h"
4950#include " pxr/usd/usdSkel/animQuery.h"
5051#include " pxr/usd/usdSkel/bindingAPI.h"
5152#include " pxr/usd/usdSkel/blendShapeQuery.h"
@@ -56,6 +57,10 @@ IECORE_PUSH_DEFAULT_VISIBILITY
5657#include " pxr/usd/usdSkel/utils.h"
5758IECORE_POP_DEFAULT_VISIBILITY
5859
60+ #include " boost/algorithm/string/classification.hpp"
61+ #include " boost/algorithm/string/predicate.hpp"
62+ #include " boost/algorithm/string/split.hpp"
63+
5964#include " fmt/ostream.h"
6065
6166using namespace std ;
@@ -68,6 +73,47 @@ using namespace IECoreUSD;
6873// Writing primitive variables
6974// ////////////////////////////////////////////////////////////////////////
7075
76+ namespace {
77+
78+ pxr::TfToken toUSDElementType ( const pxr::UsdPrim &prim )
79+ {
80+ if ( prim.IsA <pxr::UsdGeomMesh>() )
81+ {
82+ return pxr::UsdGeomTokens->face ;
83+ }
84+ return pxr::TfToken ();
85+ }
86+
87+ void writeGeomSubsetIndices ( const pxr::TfToken &familyName, const StringVectorData *data, const IntVectorData *indicesData, const pxr::TfToken &elementType, pxr::UsdGeomPointBased &pointBased, pxr::UsdTimeCode time )
88+ {
89+ int32_t subsetNameIdx = 0 ;
90+ pxr::TfToken familyType = pxr::UsdGeomTokens->partition ;
91+
92+ for ( const std::string &subsetName : data->readable () )
93+ {
94+ if ( subsetName.empty () && subsetNameIdx == 0 )
95+ {
96+ familyType = pxr::UsdGeomTokens->nonOverlapping ;
97+ subsetNameIdx++;
98+ continue ;
99+ }
100+ pxr::VtIntArray usdIndices;
101+ int32_t idx = 0 ;
102+ for ( const int32_t &faceIdx : indicesData->readable () )
103+ {
104+ if ( faceIdx == subsetNameIdx )
105+ {
106+ usdIndices.push_back ( idx );
107+ }
108+ idx++;
109+ }
110+ pxr::UsdGeomSubset::CreateGeomSubset ( pointBased, pxr::TfToken ( subsetName ), elementType, usdIndices, familyName, familyType );
111+ ++subsetNameIdx;
112+ }
113+ }
114+
115+ }; // namespace
116+
71117void IECoreUSD::PrimitiveAlgo::writePrimitiveVariable ( const IECoreScene::PrimitiveVariable &primitiveVariable, pxr::UsdGeomPrimvar &primVar, pxr::UsdTimeCode time )
72118{
73119 const pxr::TfToken usdInterpolation = toUSD ( primitiveVariable.interpolation );
@@ -124,6 +170,35 @@ void IECoreUSD::PrimitiveAlgo::writePrimitiveVariable( const std::string &name,
124170 writePrimitiveVariable ( name, primitiveVariable, pxr::UsdGeomPrimvarsAPI ( gPrim .GetPrim () ), time );
125171}
126172
173+ void IECoreUSD::PrimitiveAlgo::writeGeomSubsets ( const pxr::TfToken &familyName, const IECoreScene::PrimitiveVariable &primitiveVariable, pxr::UsdGeomPointBased &pointBased, pxr::UsdTimeCode time )
174+ {
175+ if ( !primitiveVariable.indices )
176+ {
177+ IECore::msg ( IECore::MessageHandler::Level::Warning, " IECoreUSD::PrimitiveAlgo" , " No index data for UsdGeomSubset family {}" , familyName.GetString () );
178+ return ;
179+ }
180+
181+ if ( pointBased.GetPrim ().IsA <pxr::UsdGeomMesh>() &&
182+ primitiveVariable.interpolation != IECoreScene::PrimitiveVariable::Uniform )
183+ {
184+ IECore::msg ( IECore::MessageHandler::Level::Warning, " IECoreUSD::PrimitiveAlgo" , " Invalid Interpolation {} for UsdGeomSubset family {}" , primitiveVariable.interpolation , familyName.GetString () );
185+ return ;
186+ }
187+
188+ const pxr::TfToken elementType = toUSDElementType ( pointBased.GetPrim () );
189+
190+ if ( elementType.IsEmpty () )
191+ {
192+ IECore::msg ( IECore::MessageHandler::Level::Warning, " IECoreUSD::PrimitiveAlgo" , " Invalid elementType for GeomSubset family {}" , familyName.GetString () );
193+ return ;
194+ }
195+
196+ if ( const StringVectorData *data = static_cast <const StringVectorData *>( primitiveVariable.data .get () ) )
197+ {
198+ writeGeomSubsetIndices ( familyName, data, primitiveVariable.indices .get (), elementType, pointBased, time );
199+ }
200+ }
201+
127202void IECoreUSD::PrimitiveAlgo::writePrimitiveVariable ( const std::string &name, const IECoreScene::PrimitiveVariable &value, pxr::UsdGeomPointBased &pointBased, pxr::UsdTimeCode time )
128203{
129204 if ( name == " P" )
@@ -138,6 +213,15 @@ void IECoreUSD::PrimitiveAlgo::writePrimitiveVariable( const std::string &name,
138213 {
139214 pointBased.CreateAccelerationsAttr ().Set ( PrimitiveAlgo::toUSDExpanded ( value ), time );
140215 }
216+ else if ( boost::algorithm::starts_with ( name, " geomSubset:" ) )
217+ {
218+ std::vector<std::string> split;
219+ boost::algorithm::split ( split, name, boost::algorithm::is_any_of ( " :" ) );
220+ if ( split.size () > 1 )
221+ {
222+ writeGeomSubsets ( pxr::TfToken ( split[1 ] ), value, pointBased, time );
223+ }
224+ }
141225 else
142226 {
143227 writePrimitiveVariable ( name, value, static_cast <pxr::UsdGeomGprim &>( pointBased ), time );
@@ -498,6 +582,74 @@ bool skelAnimMightBeTimeVarying( const pxr::UsdPrim &prim )
498582 return animQuery.JointTransformsMightBeTimeVarying () || animQuery.BlendShapeWeightsMightBeTimeVarying ();
499583}
500584
585+ bool geomSubsetsMightBeTimeVarying ( const pxr::UsdGeomPointBased &pointBased )
586+ {
587+ for ( const pxr::UsdGeomSubset &geomSubset : pxr::UsdGeomSubset::GetAllGeomSubsets ( pointBased ) )
588+ {
589+ // We don't check ElementType here as it is considered invalid if it is TimeVarying.
590+ if ( geomSubset.GetIndicesAttr ().ValueMightBeTimeVarying () )
591+ {
592+ return true ;
593+ }
594+ if ( geomSubset.GetFamilyNameAttr ().ValueMightBeTimeVarying () )
595+ {
596+ return true ;
597+ }
598+ }
599+ return false ;
600+ }
601+
602+ void readGeomSubsetNamesAndIndices (
603+ const pxr::UsdGeomPointBased &pointBased,
604+ const pxr::TfToken &elementType,
605+ const pxr::TfToken &familyName,
606+ pxr::UsdTimeCode time,
607+ const IECoreScene::Primitive *primitive,
608+ std::vector<std::string> &geomSubsetNames,
609+ std::vector<int > &geomSubsetIndices,
610+ const Canceller *canceller
611+ )
612+ {
613+ int32_t geomSubsetIdx = 0 ;
614+
615+ const pxr::TfToken familyType = pxr::UsdGeomSubset::GetFamilyType ( pointBased, familyName );
616+ const pxr::VtIntArray unassignedIndices = pxr::UsdGeomSubset::GetUnassignedIndices ( pointBased, elementType, familyName, time );
617+ if ( unassignedIndices.size () && familyType == pxr::UsdGeomTokens->uniform )
618+ {
619+ // This shouldn't happen based on the ValidateFamily functions above, but leaving in here if it does happen.
620+ IECore::msg ( IECore::MessageHandler::Level::Warning, " IECoreUSD::PrimitiveAlgo" , " FamilyName: {} familyType: {} has unassigned indices." , familyName.GetString (), familyType.GetString () );
621+ }
622+
623+ const auto &geomSubsets = pxr::UsdGeomSubset::GetGeomSubsets ( pointBased, elementType, familyName );
624+ geomSubsetNames.resize ( unassignedIndices.size () || familyType == pxr::UsdGeomTokens->nonOverlapping ? geomSubsets.size () + 1 : geomSubsets.size () );
625+ geomSubsetIndices.resize ( primitive->variableSize ( IECoreUSD::PrimitiveAlgo::elementTypeFromUSD ( elementType ) ) );
626+
627+ if ( unassignedIndices.size () || familyType == pxr::UsdGeomTokens->nonOverlapping )
628+ {
629+ Canceller::check ( canceller );
630+ geomSubsetNames[geomSubsetIdx] = std::string ();
631+ for ( const int &idx : unassignedIndices )
632+ {
633+ geomSubsetIndices[idx] = geomSubsetIdx;
634+ }
635+ ++geomSubsetIdx;
636+ }
637+
638+ for ( const pxr::UsdGeomSubset &geomSubset : geomSubsets )
639+ {
640+ Canceller::check ( canceller );
641+ geomSubsetNames[geomSubsetIdx] = geomSubset.GetPrim ().GetName ().GetString ();
642+ pxr::UsdAttribute indicesAttr = geomSubset.GetIndicesAttr ();
643+ pxr::VtIntArray geomIndices;
644+ indicesAttr.Get ( &geomIndices, time );
645+ for ( const int &idx : geomIndices )
646+ {
647+ geomSubsetIndices[idx] = geomSubsetIdx;
648+ }
649+ ++geomSubsetIdx;
650+ }
651+ }
652+
501653} // namespace
502654
503655void IECoreUSD::PrimitiveAlgo::readPrimitiveVariables ( const pxr::UsdGeomPrimvarsAPI &primvarsAPI, pxr::UsdTimeCode time, IECoreScene::Primitive *primitive, const Canceller *canceller )
@@ -562,7 +714,72 @@ void IECoreUSD::PrimitiveAlgo::readPrimitiveVariables( const pxr::UsdGeomPrimvar
562714 primitive->variables .erase ( it );
563715 }
564716 }
717+ }
718+
719+ void IECoreUSD::PrimitiveAlgo::readGeomSubsets ( const pxr::UsdGeomPointBased &pointBased, pxr::UsdTimeCode time, IECoreScene::Primitive *primitive, const Canceller *canceller )
720+ {
721+ // We're only interested in a subset of element types based on UsdGeom types.
722+ const pxr::TfToken elementType = toUSDElementType ( pointBased.GetPrim () );
723+
724+ if ( elementType.IsEmpty () )
725+ {
726+ if ( pxr::UsdGeomSubset::GetGeomSubsets ( pointBased ).size () )
727+ {
728+ IECore::msg ( IECore::MessageHandler::Level::Warning, " IECoreUSD::PrimitiveAlgo" , " Prim {} with UsdGeomSubsets has unsupported elementType - skipping" , pointBased.GetPrim ().GetName ().GetString () );
729+ }
730+ return ;
731+ }
732+
733+ // Collect all valid family names.
734+ std::set<pxr::TfToken> validGeomSubsetFamilyNames;
565735
736+ // UsdGeomSubsets without a familyName always default to familyType unrestricted so we don't bother accessing those.
737+ for ( const auto &geomSubset : pxr::UsdGeomSubset::GetGeomSubsets ( pointBased, elementType ) )
738+ {
739+ if ( !geomSubset.GetFamilyNameAttr ().HasAuthoredValue () )
740+ {
741+ IECore::msg ( IECore::MessageHandler::Level::Warning, " IECoreUSD::PrimitiveAlgo" , " Prim {} has UsdGeomSubsets without familyName - skipping" , pointBased.GetPrim ().GetName ().GetString () );
742+ break ;
743+ }
744+ }
745+
746+ for ( const auto &familyName : pxr::UsdGeomSubset::GetAllGeomSubsetFamilyNames ( pointBased ) )
747+ {
748+ std::string reason;
749+ pxr::TfToken familyType = pxr::UsdGeomSubset::GetFamilyType ( pointBased, familyName );
750+ if ( familyType == pxr::UsdGeomTokens->unrestricted )
751+ {
752+ IECore::msg ( IECore::MessageHandler::Level::Warning, " IECoreUSD::PrimitiveAlgo" , " UsdGeomSubset family: {} familyType: {} not supported - skipping" , familyName.GetString (), familyType.GetString () );
753+ continue ;
754+ }
755+ else if ( !pxr::UsdGeomSubset::ValidateFamily ( pointBased, elementType, familyName, &reason ) )
756+ {
757+ IECore::msg ( IECore::MessageHandler::Level::Warning, " IECoreUSD::PrimitiveAlgo" , " UsdGeomSubset family: {} not supported - {}" , familyName.GetString (), reason );
758+ continue ;
759+ }
760+ else
761+ {
762+ validGeomSubsetFamilyNames.emplace ( familyName );
763+ }
764+ }
765+
766+ // Convert family GeomSubsets to indexed string primitive variables under geomSubset or geomSubset:<familyName>.
767+ for ( const auto &familyName : validGeomSubsetFamilyNames )
768+ {
769+ Canceller::check ( canceller );
770+
771+ std::vector<std::string> geomSubsetNames;
772+ std::vector<int > geomSubsetIndices;
773+ readGeomSubsetNamesAndIndices ( pointBased, elementType, familyName, time, primitive, geomSubsetNames, geomSubsetIndices, canceller );
774+
775+ const std::string geomSubsetPrimvarName = std::string ( " geomSubset:" ) + familyName.GetString ();
776+
777+ const StringVectorDataPtr data = new StringVectorData ( geomSubsetNames );
778+ const IntVectorDataPtr indices = new IntVectorData ( geomSubsetIndices );
779+
780+ // Note: Using the pointsBased points attribute for the error print log.
781+ addPrimitiveVariableIfValid ( primitive, geomSubsetPrimvarName, IECoreScene::PrimitiveVariable ( IECoreUSD::PrimitiveAlgo::elementTypeFromUSD ( elementType ), data, indices ), pointBased.GetPointsAttr () );
782+ }
566783}
567784
568785void IECoreUSD::PrimitiveAlgo::readPrimitiveVariables ( const pxr::UsdGeomPointBased &pointBased, pxr::UsdTimeCode time, IECoreScene::Primitive *primitive, const Canceller *canceller )
@@ -589,6 +806,8 @@ void IECoreUSD::PrimitiveAlgo::readPrimitiveVariables( const pxr::UsdGeomPointBa
589806
590807 Canceller::check ( canceller );
591808 readPrimitiveVariable ( pointBased.GetAccelerationsAttr (), time, primitive, " acceleration" );
809+
810+ readGeomSubsets ( pointBased, time, primitive, canceller );
592811}
593812
594813void IECoreUSD::PrimitiveAlgo::readPrimitiveVariable ( const pxr::UsdAttribute &attribute, pxr::UsdTimeCode timeCode, IECoreScene::Primitive *primitive, const std::string &name, IECoreScene::PrimitiveVariable::Interpolation interpolation )
@@ -619,7 +838,8 @@ bool IECoreUSD::PrimitiveAlgo::primitiveVariablesMightBeTimeVarying( const pxr::
619838 pointBased.GetVelocitiesAttr ().ValueMightBeTimeVarying () ||
620839 pointBased.GetAccelerationsAttr ().ValueMightBeTimeVarying () ||
621840 primitiveVariablesMightBeTimeVarying ( pxr::UsdGeomPrimvarsAPI ( pointBased.GetPrim () ) ) ||
622- skelAnimMightBeTimeVarying ( pointBased.GetPrim () )
841+ skelAnimMightBeTimeVarying ( pointBased.GetPrim () ) ||
842+ geomSubsetsMightBeTimeVarying ( pointBased )
623843 ;
624844}
625845
@@ -648,3 +868,12 @@ IECoreScene::PrimitiveVariable::Interpolation IECoreUSD::PrimitiveAlgo::fromUSD(
648868
649869 return IECoreScene::PrimitiveVariable::Invalid;
650870}
871+
872+ IECoreScene::PrimitiveVariable::Interpolation IECoreUSD::PrimitiveAlgo::elementTypeFromUSD ( pxr::TfToken elementType )
873+ {
874+ if ( elementType == pxr::UsdGeomTokens->face )
875+ {
876+ return IECoreScene::PrimitiveVariable::Uniform;
877+ }
878+ return IECoreScene::PrimitiveVariable::Invalid;
879+ }
0 commit comments