Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 71 additions & 38 deletions examples/jsm/loaders/usd/USDComposer.js
Original file line number Diff line number Diff line change
Expand Up @@ -625,56 +625,71 @@ class USDComposer {
const typeName = spec.fields.typeName;

// Check for references/payloads
const refValue = this._getReference( spec );
if ( refValue ) {
const refValues = this._getReferences( spec );
if ( refValues.length > 0 ) {

// Get local variant selections from this prim
const localVariants = this._getLocalVariantSelections( spec.fields );

// Resolve the reference
const referencedGroup = this._resolveReference( refValue, localVariants );
if ( referencedGroup ) {
// Resolve all references
const resolvedGroups = [];
for ( const refValue of refValues ) {

const referencedGroup = this._resolveReference( refValue, localVariants );
if ( referencedGroup ) resolvedGroups.push( referencedGroup );

}

if ( resolvedGroups.length > 0 ) {

const attrs = this._getAttributes( path );

// Check if the referenced content is a single mesh (or container with single mesh)
// Single reference with single mesh: use optimized path
// This handles the USDZExporter pattern: Xform references geometry file
const singleMesh = this._findSingleMesh( referencedGroup );
if ( resolvedGroups.length === 1 ) {

if ( singleMesh && ( typeName === 'Xform' || ! typeName ) ) {
const singleMesh = this._findSingleMesh( resolvedGroups[ 0 ] );

// Merge the mesh into this prim
singleMesh.name = name;
this.applyTransform( singleMesh, spec.fields, attrs );
if ( singleMesh && ( typeName === 'Xform' || ! typeName ) ) {

// Apply material binding from the referencing prim if present
this._applyMaterialBinding( singleMesh, path );
// Merge the mesh into this prim
singleMesh.name = name;
this.applyTransform( singleMesh, spec.fields, attrs );

parent.add( singleMesh );
// Apply material binding from the referencing prim if present
this._applyMaterialBinding( singleMesh, path );

// Still build local children (overrides)
this._buildHierarchy( singleMesh, path );
parent.add( singleMesh );

} else {
// Still build local children (overrides)
this._buildHierarchy( singleMesh, path );

continue;

}

}

// Create a container for the referenced content
const obj = new Object3D();
obj.name = name;
this.applyTransform( obj, spec.fields, attrs );

// Create a container for the referenced content
const obj = new Object3D();
obj.name = name;
this.applyTransform( obj, spec.fields, attrs );
// Add all children from all resolved references
for ( const referencedGroup of resolvedGroups ) {

// Add all children from the referenced group
while ( referencedGroup.children.length > 0 ) {

obj.add( referencedGroup.children[ 0 ] );

}

parent.add( obj );
}

// Still build local children (overrides)
this._buildHierarchy( obj, path );
parent.add( obj );

}
// Still build local children (overrides)
this._buildHierarchy( obj, path );

continue;

Expand Down Expand Up @@ -1023,27 +1038,44 @@ class USDComposer {
}

/**
* Get reference value from a prim spec.
* Get all reference values from a prim spec.
* @returns {string[]} Array of reference strings like "@path@" or "@path@<prim>"
*/
_getReference( spec ) {
_getReferences( spec ) {

const results = [];

if ( spec.fields.references && spec.fields.references.length > 0 ) {

const ref = spec.fields.references[ 0 ];
if ( typeof ref === 'string' ) return ref;
if ( ref.assetPath ) return '@' + ref.assetPath + '@';

if ( typeof ref === 'string' ) {

// Extract all @...@ references (handles both single and array values)
const matches = ref.matchAll( /@([^@]+)@(?:<([^>]+)>)?/g );
for ( const match of matches ) {

results.push( match[ 0 ] );

}

} else if ( ref.assetPath ) {

results.push( '@' + ref.assetPath + '@' );

}

}

if ( spec.fields.payload ) {
if ( results.length === 0 && spec.fields.payload ) {

const payload = spec.fields.payload;
if ( typeof payload === 'string' ) return payload;
if ( payload.assetPath ) return '@' + payload.assetPath + '@';
if ( typeof payload === 'string' ) results.push( payload );
else if ( payload.assetPath ) results.push( '@' + payload.assetPath + '@' );

}

return null;
return results;

}

Expand Down Expand Up @@ -1585,7 +1617,7 @@ class USDComposer {
if ( ! indices || indices.length === 0 ) continue;

// Get material binding - check direct path and variant paths
let materialPath = this._getMaterialBindingTarget( p );
const materialPath = this._getMaterialBindingTarget( p );

subsets.push( {
name: p.split( '/' ).pop(),
Expand Down Expand Up @@ -2751,7 +2783,7 @@ class USDComposer {
_getMaterialPath( meshPath, fields ) {

let materialPath = null;
let materialBinding = fields[ 'material:binding' ];
const materialBinding = fields[ 'material:binding' ];

if ( materialBinding ) {

Expand All @@ -2775,7 +2807,7 @@ class USDComposer {
const material = new MeshPhysicalMaterial();

let materialPath = null;
let materialBinding = fields[ 'material:binding' ];
const materialBinding = fields[ 'material:binding' ];

if ( materialBinding ) {

Expand Down Expand Up @@ -3846,6 +3878,7 @@ class USDComposer {
}

};

image.src = url;

return texture;
Expand Down Expand Up @@ -4075,7 +4108,7 @@ class USDComposer {
// Use geomBindTransform if available, otherwise fall back to identity.
// Estimating bind transforms from vertex/joint samples is not robust and can
// produce severe skinning distortion for valid assets.
let bindMatrix = new Matrix4();
const bindMatrix = new Matrix4();

if ( geomBindTransform && geomBindTransform.length === 16 ) {

Expand Down
Binary file modified examples/screenshots/webgl_loader_3dtiles.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading