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
6 changes: 6 additions & 0 deletions examples/jsm/loaders/usd/USDAParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,12 @@ class USDAParser {

}

if ( header.metersPerUnit !== undefined ) {

rootFields.metersPerUnit = parseFloat( header.metersPerUnit );

}

}

specsByPath[ '/' ] = { specType: SpecType.Prim, fields: rootFields };
Expand Down
225 changes: 204 additions & 21 deletions examples/jsm/loaders/usd/USDComposer.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import {
AnimationClip,
BoxGeometry,
BufferAttribute,
BufferGeometry,
CapsuleGeometry,
ClampToEdgeWrapping,
ConeGeometry,
CylinderGeometry,
Euler,
Group,
Matrix4,
Expand All @@ -20,6 +24,7 @@ import {
SkinnedMesh,
Skeleton,
Bone,
SphereGeometry,
SRGBColorSpace,
Texture,
Vector2,
Expand Down Expand Up @@ -110,6 +115,15 @@ class USDComposer {
// Build animations
group.animations = this._buildAnimations();

// Handle metersPerUnit scaling
const metersPerUnit = rootFields.metersPerUnit;

if ( metersPerUnit !== undefined && metersPerUnit !== 1 ) {

group.scale.setScalar( metersPerUnit );

}

// Handle Z-up to Y-up conversion
if ( rootSpec && rootSpec.fields && rootSpec.fields.upAxis === 'Z' ) {

Expand Down Expand Up @@ -438,18 +452,37 @@ class USDComposer {

}

// Build shader index (shaders are children of materials)
// Build shader index (shaders are children or descendants of materials)
if ( typeName === 'Shader' && lastSlash > 0 ) {

const materialPath = path.slice( 0, lastSlash );
// Walk up ancestors to find the nearest Material prim.
// Shaders may be direct children of a Material, or nested
// inside a NodeGraph (common with MaterialX materials).

if ( ! this.shadersByMaterialPath.has( materialPath ) ) {
let ancestorPath = path.slice( 0, lastSlash );

this.shadersByMaterialPath.set( materialPath, [] );
while ( ancestorPath.length > 0 ) {

}
const ancestorSpec = this.specsByPath[ ancestorPath ];

if ( ancestorSpec && ancestorSpec.specType === SpecType.Prim && ancestorSpec.fields.typeName === 'Material' ) {

if ( ! this.shadersByMaterialPath.has( ancestorPath ) ) {

this.shadersByMaterialPath.set( ancestorPath, [] );

}

this.shadersByMaterialPath.get( ancestorPath ).push( path );
break;

}

this.shadersByMaterialPath.get( materialPath ).push( path );
const slash = ancestorPath.lastIndexOf( '/' );
if ( slash <= 0 ) break;
ancestorPath = ancestorPath.slice( 0, slash );

}

}

Expand Down Expand Up @@ -681,6 +714,16 @@ class USDComposer {
parent.add( obj );
this._buildHierarchy( obj, path );

} else if ( typeName === 'Cube' || typeName === 'Sphere' || typeName === 'Cylinder' || typeName === 'Cone' || typeName === 'Capsule' ) {

const obj = this._buildGeomPrimitive( path, spec, typeName );
if ( obj ) {

parent.add( obj );
this._buildHierarchy( obj, path );

}

} else if ( typeName === 'Material' || typeName === 'Shader' || typeName === 'GeomSubset' ) {

// Skip materials/shaders/subsets, they're referenced by meshes
Expand Down Expand Up @@ -1073,6 +1116,86 @@ class USDComposer {

}

/**
* Build a mesh from a USD geometric primitive (Cube, Sphere, Cylinder, Cone, Capsule).
*/
_buildGeomPrimitive( path, spec, typeName ) {

const attrs = this._getAttributes( path );
const name = path.split( '/' ).pop();

let geometry;

switch ( typeName ) {

case 'Cube': {

const size = attrs[ 'size' ] || 2;
geometry = new BoxGeometry( size, size, size );
break;

}

case 'Sphere': {

const radius = attrs[ 'radius' ] || 1;
geometry = new SphereGeometry( radius, 32, 16 );
break;

}

case 'Cylinder': {

const height = attrs[ 'height' ] || 2;
const radius = attrs[ 'radius' ] || 1;
geometry = new CylinderGeometry( radius, radius, height, 32 );
break;

}

case 'Cone': {

const height = attrs[ 'height' ] || 2;
const radius = attrs[ 'radius' ] || 1;
geometry = new ConeGeometry( radius, height, 32 );
break;

}

case 'Capsule': {

const height = attrs[ 'height' ] || 1;
const radius = attrs[ 'radius' ] || 0.5;
geometry = new CapsuleGeometry( radius, height, 16, 32 );
break;

}

}

// USD defaults axis to "Z", Three.js uses Y
const axis = attrs[ 'axis' ] || 'Z';

if ( axis === 'X' ) {

geometry.rotateZ( - Math.PI / 2 );

} else if ( axis === 'Z' ) {

geometry.rotateX( Math.PI / 2 );

}

const material = this._buildMaterial( path, spec.fields );
const mesh = new Mesh( geometry, material );
mesh.name = name;

this.applyTransform( mesh, spec.fields, attrs );

return mesh;

}

/**
* Build a mesh from a Mesh spec.
*/
Expand Down Expand Up @@ -1451,7 +1574,12 @@ class USDComposer {

} else {

geometry.computeVertexNormals();
// Compute vertex normals from the original indexed topology where
// vertices are shared, then expand them like positions.
const vertexNormals = this._computeVertexNormals( points, indices );
geometry.setAttribute( 'normal', new BufferAttribute( new Float32Array(
this._expandAttribute( vertexNormals, indices, 3 )
), 3 ) );

}

Expand Down Expand Up @@ -1727,12 +1855,18 @@ class USDComposer {
? this._applyTriangulationPattern( Array.from( { length: numFaceVertices }, ( _, i ) => i ), triPattern )
: null );

// When no normals are provided, compute vertex normals from
// the indexed topology so that shared vertices produce averaged normals.
const vertexNormals = ( ! normals && origIndices.length > 0 )
? this._computeVertexNormals( points, origIndices )
: null;

// Build reordered vertex data
const vertexCount = triangleCount * 3;
const positions = new Float32Array( vertexCount * 3 );
const uvData = uvs ? new Float32Array( vertexCount * 2 ) : null;
const uv1Data = uvs2 ? new Float32Array( vertexCount * 2 ) : null;
const normalData = normals ? new Float32Array( vertexCount * 3 ) : null;
const normalData = ( normals || vertexNormals ) ? new Float32Array( vertexCount * 3 ) : null;
const skinIndexData = jointIndices ? new Uint16Array( vertexCount * 4 ) : null;
const skinWeightData = jointWeights ? new Float32Array( vertexCount * 4 ) : null;

Expand Down Expand Up @@ -1784,21 +1918,27 @@ class USDComposer {

}

if ( normalData && normals ) {
if ( normalData ) {

if ( origNormalIndices ) {
if ( normals && origNormalIndices ) {

const normalIdx = origNormalIndices[ origIdx ];
normalData[ newIdx * 3 ] = normals[ normalIdx * 3 ];
normalData[ newIdx * 3 + 1 ] = normals[ normalIdx * 3 + 1 ];
normalData[ newIdx * 3 + 2 ] = normals[ normalIdx * 3 + 2 ];

} else if ( normals.length === points.length ) {
} else if ( normals && normals.length === points.length ) {

normalData[ newIdx * 3 ] = normals[ pointIdx * 3 ];
normalData[ newIdx * 3 + 1 ] = normals[ pointIdx * 3 + 1 ];
normalData[ newIdx * 3 + 2 ] = normals[ pointIdx * 3 + 2 ];

} else if ( vertexNormals ) {

normalData[ newIdx * 3 ] = vertexNormals[ pointIdx * 3 ];
normalData[ newIdx * 3 + 1 ] = vertexNormals[ pointIdx * 3 + 1 ];
normalData[ newIdx * 3 + 2 ] = vertexNormals[ pointIdx * 3 + 2 ];

}

}
Expand Down Expand Up @@ -1841,15 +1981,7 @@ class USDComposer {

}

if ( normalData ) {

geometry.setAttribute( 'normal', new BufferAttribute( normalData, 3 ) );

} else {

geometry.computeVertexNormals();

}
geometry.setAttribute( 'normal', new BufferAttribute( normalData, 3 ) );

if ( skinIndexData ) {

Expand Down Expand Up @@ -2352,6 +2484,57 @@ class USDComposer {

}

/**
* Compute per-vertex normals from indexed triangle data.
* Accumulates area-weighted face normals at each shared vertex and normalizes.
*/
_computeVertexNormals( points, indices ) {

const numVertices = points.length / 3;
const normals = new Float32Array( numVertices * 3 );

for ( let i = 0; i < indices.length; i += 3 ) {

const a = indices[ i ];
const b = indices[ i + 1 ];
const c = indices[ i + 2 ];

const ax = points[ a * 3 ], ay = points[ a * 3 + 1 ], az = points[ a * 3 + 2 ];
const bx = points[ b * 3 ], by = points[ b * 3 + 1 ], bz = points[ b * 3 + 2 ];
const cx = points[ c * 3 ], cy = points[ c * 3 + 1 ], cz = points[ c * 3 + 2 ];

const e1x = bx - ax, e1y = by - ay, e1z = bz - az;
const e2x = cx - ax, e2y = cy - ay, e2z = cz - az;

const nx = e1y * e2z - e1z * e2y;
const ny = e1z * e2x - e1x * e2z;
const nz = e1x * e2y - e1y * e2x;

normals[ a * 3 ] += nx; normals[ a * 3 + 1 ] += ny; normals[ a * 3 + 2 ] += nz;
normals[ b * 3 ] += nx; normals[ b * 3 + 1 ] += ny; normals[ b * 3 + 2 ] += nz;
normals[ c * 3 ] += nx; normals[ c * 3 + 1 ] += ny; normals[ c * 3 + 2 ] += nz;

}

for ( let i = 0; i < numVertices; i ++ ) {

const x = normals[ i * 3 ], y = normals[ i * 3 + 1 ], z = normals[ i * 3 + 2 ];
const len = Math.sqrt( x * x + y * y + z * z );

if ( len > 0 ) {

normals[ i * 3 ] /= len;
normals[ i * 3 + 1 ] /= len;
normals[ i * 3 + 2 ] /= len;

}

}

return normals;

}

/**
* Get the material path for a mesh, checking various binding sources.
*/
Expand Down Expand Up @@ -2550,7 +2733,7 @@ class USDComposer {
const shaderAttrs = this._getAttributes( path );
const infoId = shaderAttrs[ 'info:id' ] || spec.fields[ 'info:id' ];

if ( infoId === 'UsdPreviewSurface' ) {
if ( infoId === 'UsdPreviewSurface' || infoId === 'ND_UsdPreviewSurface_surfaceshader' ) {

this._applyPreviewSurface( material, path );

Expand Down
Binary file modified examples/screenshots/webgl_loader_imagebitmap.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions examples/webgl_loader_imagebitmap.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
function addImageBitmap() {

new THREE.ImageBitmapLoader()
.setOptions( { imageOrientation: 'flipY' } )
.load( 'textures/planets/earth_atmos_2048.jpg?' + performance.now(), function ( imageBitmap ) {

const texture = new THREE.CanvasTexture( imageBitmap );
Expand Down
18 changes: 17 additions & 1 deletion src/objects/BatchedMesh.js
Original file line number Diff line number Diff line change
Expand Up @@ -1133,7 +1133,23 @@ class BatchedMesh extends Mesh {
getColorAt( instanceId, color ) {

this.validateInstanceId( instanceId );
return color.fromArray( this._colorsTexture.image.data, instanceId * 4 );
if ( this._colorsTexture === null ) {

if ( color.isVector4 ) {

return color.set( 1, 1, 1, 1 );

} else {

return color.setRGB( 1, 1, 1 );

}

} else {

return color.fromArray( this._colorsTexture.image.data, instanceId * 4 );

}

}

Expand Down
10 changes: 9 additions & 1 deletion src/objects/InstancedMesh.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,15 @@ class InstancedMesh extends Mesh {
*/
getColorAt( index, color ) {

return color.fromArray( this.instanceColor.array, index * 3 );
if ( this.instanceColor === null ) {

return color.setRGB( 1, 1, 1 );

} else {

return color.fromArray( this.instanceColor.array, index * 3 );

}

}

Expand Down
Loading