@@ -625,56 +625,71 @@ class USDComposer {
625625 const typeName = spec . fields . typeName ;
626626
627627 // Check for references/payloads
628- const refValue = this . _getReference ( spec ) ;
629- if ( refValue ) {
628+ const refValues = this . _getReferences ( spec ) ;
629+ if ( refValues . length > 0 ) {
630630
631631 // Get local variant selections from this prim
632632 const localVariants = this . _getLocalVariantSelections ( spec . fields ) ;
633633
634- // Resolve the reference
635- const referencedGroup = this . _resolveReference ( refValue , localVariants ) ;
636- if ( referencedGroup ) {
634+ // Resolve all references
635+ const resolvedGroups = [ ] ;
636+ for ( const refValue of refValues ) {
637+
638+ const referencedGroup = this . _resolveReference ( refValue , localVariants ) ;
639+ if ( referencedGroup ) resolvedGroups . push ( referencedGroup ) ;
640+
641+ }
642+
643+ if ( resolvedGroups . length > 0 ) {
637644
638645 const attrs = this . _getAttributes ( path ) ;
639646
640- // Check if the referenced content is a single mesh (or container with single mesh)
647+ // Single reference with single mesh: use optimized path
641648 // This handles the USDZExporter pattern: Xform references geometry file
642- const singleMesh = this . _findSingleMesh ( referencedGroup ) ;
649+ if ( resolvedGroups . length === 1 ) {
643650
644- if ( singleMesh && ( typeName === 'Xform' || ! typeName ) ) {
651+ const singleMesh = this . _findSingleMesh ( resolvedGroups [ 0 ] ) ;
645652
646- // Merge the mesh into this prim
647- singleMesh . name = name ;
648- this . applyTransform ( singleMesh , spec . fields , attrs ) ;
653+ if ( singleMesh && ( typeName === 'Xform' || ! typeName ) ) {
649654
650- // Apply material binding from the referencing prim if present
651- this . _applyMaterialBinding ( singleMesh , path ) ;
655+ // Merge the mesh into this prim
656+ singleMesh . name = name ;
657+ this . applyTransform ( singleMesh , spec . fields , attrs ) ;
652658
653- parent . add ( singleMesh ) ;
659+ // Apply material binding from the referencing prim if present
660+ this . _applyMaterialBinding ( singleMesh , path ) ;
654661
655- // Still build local children (overrides)
656- this . _buildHierarchy ( singleMesh , path ) ;
662+ parent . add ( singleMesh ) ;
657663
658- } else {
664+ // Still build local children (overrides)
665+ this . _buildHierarchy ( singleMesh , path ) ;
666+
667+ continue ;
668+
669+ }
670+
671+ }
672+
673+ // Create a container for the referenced content
674+ const obj = new Object3D ( ) ;
675+ obj . name = name ;
676+ this . applyTransform ( obj , spec . fields , attrs ) ;
659677
660- // Create a container for the referenced content
661- const obj = new Object3D ( ) ;
662- obj . name = name ;
663- this . applyTransform ( obj , spec . fields , attrs ) ;
678+ // Add all children from all resolved references
679+ for ( const referencedGroup of resolvedGroups ) {
664680
665- // Add all children from the referenced group
666681 while ( referencedGroup . children . length > 0 ) {
667682
668683 obj . add ( referencedGroup . children [ 0 ] ) ;
669684
670685 }
671686
672- parent . add ( obj ) ;
687+ }
673688
674- // Still build local children (overrides)
675- this . _buildHierarchy ( obj , path ) ;
689+ parent . add ( obj ) ;
676690
677- }
691+ // Still build local children (overrides)
692+ this . _buildHierarchy ( obj , path ) ;
678693
679694 continue ;
680695
@@ -1023,27 +1038,44 @@ class USDComposer {
10231038 }
10241039
10251040 /**
1026- * Get reference value from a prim spec.
1041+ * Get all reference values from a prim spec.
1042+ * @returns {string[] } Array of reference strings like "@path@" or "@path@<prim>"
10271043 */
1028- _getReference ( spec ) {
1044+ _getReferences ( spec ) {
1045+
1046+ const results = [ ] ;
10291047
10301048 if ( spec . fields . references && spec . fields . references . length > 0 ) {
10311049
10321050 const ref = spec . fields . references [ 0 ] ;
1033- if ( typeof ref === 'string' ) return ref ;
1034- if ( ref . assetPath ) return '@' + ref . assetPath + '@' ;
1051+
1052+ if ( typeof ref === 'string' ) {
1053+
1054+ // Extract all @...@ references (handles both single and array values)
1055+ const matches = ref . matchAll ( / @ ( [ ^ @ ] + ) @ (?: < ( [ ^ > ] + ) > ) ? / g ) ;
1056+ for ( const match of matches ) {
1057+
1058+ results . push ( match [ 0 ] ) ;
1059+
1060+ }
1061+
1062+ } else if ( ref . assetPath ) {
1063+
1064+ results . push ( '@' + ref . assetPath + '@' ) ;
1065+
1066+ }
10351067
10361068 }
10371069
1038- if ( spec . fields . payload ) {
1070+ if ( results . length === 0 && spec . fields . payload ) {
10391071
10401072 const payload = spec . fields . payload ;
1041- if ( typeof payload === 'string' ) return payload ;
1042- if ( payload . assetPath ) return '@' + payload . assetPath + '@' ;
1073+ if ( typeof payload === 'string' ) results . push ( payload ) ;
1074+ else if ( payload . assetPath ) results . push ( '@' + payload . assetPath + '@' ) ;
10431075
10441076 }
10451077
1046- return null ;
1078+ return results ;
10471079
10481080 }
10491081
@@ -1585,7 +1617,7 @@ class USDComposer {
15851617 if ( ! indices || indices . length === 0 ) continue ;
15861618
15871619 // Get material binding - check direct path and variant paths
1588- let materialPath = this . _getMaterialBindingTarget ( p ) ;
1620+ const materialPath = this . _getMaterialBindingTarget ( p ) ;
15891621
15901622 subsets . push ( {
15911623 name : p . split ( '/' ) . pop ( ) ,
@@ -2751,7 +2783,7 @@ class USDComposer {
27512783 _getMaterialPath ( meshPath , fields ) {
27522784
27532785 let materialPath = null ;
2754- let materialBinding = fields [ 'material:binding' ] ;
2786+ const materialBinding = fields [ 'material:binding' ] ;
27552787
27562788 if ( materialBinding ) {
27572789
@@ -2775,7 +2807,7 @@ class USDComposer {
27752807 const material = new MeshPhysicalMaterial ( ) ;
27762808
27772809 let materialPath = null ;
2778- let materialBinding = fields [ 'material:binding' ] ;
2810+ const materialBinding = fields [ 'material:binding' ] ;
27792811
27802812 if ( materialBinding ) {
27812813
@@ -3846,6 +3878,7 @@ class USDComposer {
38463878 }
38473879
38483880 } ;
3881+
38493882 image . src = url ;
38503883
38513884 return texture ;
@@ -4075,7 +4108,7 @@ class USDComposer {
40754108 // Use geomBindTransform if available, otherwise fall back to identity.
40764109 // Estimating bind transforms from vertex/joint samples is not robust and can
40774110 // produce severe skinning distortion for valid assets.
4078- let bindMatrix = new Matrix4 ( ) ;
4111+ const bindMatrix = new Matrix4 ( ) ;
40794112
40804113 if ( geomBindTransform && geomBindTransform . length === 16 ) {
40814114
0 commit comments