Skip to content

Commit 332ec85

Browse files
authored
USDComposer: Fix reference parsing. (#33294)
1 parent e48228f commit 332ec85

1 file changed

Lines changed: 71 additions & 38 deletions

File tree

examples/jsm/loaders/usd/USDComposer.js

Lines changed: 71 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)