diff --git a/examples/jsm/exporters/USDZExporter.js b/examples/jsm/exporters/USDZExporter.js index 6f3744429819d9..52a9ead3d961b3 100644 --- a/examples/jsm/exporters/USDZExporter.js +++ b/examples/jsm/exporters/USDZExporter.js @@ -567,35 +567,49 @@ function buildNode( object, parentNode, materials, usedNames, files, options ) { if ( object.isMesh ) { const geometry = object.geometry; - const material = object.material; + const isMultiMaterial = Array.isArray( object.material ); - if ( ! material.isMeshStandardMaterial ) { + const meshMaterials = isMultiMaterial ? object.material : [ object.material ]; - console.warn( 'THREE.USDZExporter: Use MeshStandardMaterial for best results.' ); + for ( let i = 0; i < meshMaterials.length; i ++ ) { - } + const material = meshMaterials[ i ]; - const geometryFileName = 'geometries/Geometry_' + geometry.id + '.usda'; + if ( ! material.isMeshStandardMaterial ) { - if ( ! ( geometryFileName in files ) ) { + console.warn( 'THREE.USDZExporter: Use MeshStandardMaterial for best results.' ); - const meshObject = buildMeshObject( geometry ); - files[ geometryFileName ] = strToU8( - buildHeader() + '\n' + meshObject.toString() - ); + } + + if ( ! ( material.uuid in materials ) ) { + + materials[ material.uuid ] = material; + + } } - if ( ! ( material.uuid in materials ) ) { + const resolvedMaterials = meshMaterials.map( ( m ) => materials[ m.uuid ] ); + + if ( isMultiMaterial === false ) { + + const geometryFileName = `geometries/Geometry_${geometry.id}.usda`; + + if ( ! ( geometryFileName in files ) ) { - materials[ material.uuid ] = material; + const meshObject = buildMeshObject( geometry ); + files[ geometryFileName ] = strToU8( + buildHeader() + '\n' + meshObject.toString() + ); + + } } childNode = buildMesh( object, geometry, - materials[ material.uuid ], + resolvedMaterials, usedNames, options ); @@ -706,19 +720,26 @@ function buildXform( object, usedNames, options ) { } -function buildMesh( object, geometry, material, usedNames, options ) { +function buildMesh( object, geometry, materials, usedNames, options ) { const node = buildXform( object, usedNames, options ); - node.addMetadata( - 'prepend references', - `@./geometries/Geometry_${geometry.id}.usda@` - ); - node.addMetadata( 'prepend apiSchemas', '["MaterialBindingAPI"]' ); + if ( materials.length === 1 ) { - node.addProperty( - `rel material:binding = ` - ); + node.addMetadata( + 'prepend references', + `@./geometries/Geometry_${geometry.id}.usda@` + ); + node.addMetadata( 'prepend apiSchemas', '["MaterialBindingAPI"]' ); + node.addProperty( + `rel material:binding = ` + ); + + } else { + + node.addChild( buildMeshNode( geometry, materials ) ); + + } return node; @@ -756,7 +777,7 @@ function buildMeshObject( geometry ) { } -function buildMeshNode( geometry ) { +function buildMeshNode( geometry, materials = null ) { const name = 'Geometry'; const attributes = geometry.attributes; @@ -808,6 +829,41 @@ function buildMeshNode( geometry ) { node.addProperty( 'uniform token subdivisionScheme = "none"' ); + if ( materials !== null ) { + + const groups = geometry.groups; + + const totalFaces = ( geometry.index !== null + ? geometry.index.count + : attributes.position.count ) / 3; + + for ( let i = 0; i < groups.length; i ++ ) { + + const group = groups[ i ]; + const material = materials[ group.materialIndex ]; + + if ( material === undefined ) continue; + + const startFace = Math.floor( group.start / 3 ); + const endFace = Math.min( startFace + Math.floor( group.count / 3 ), totalFaces ); + + const indices = []; + for ( let j = startFace; j < endFace; j ++ ) indices.push( j ); + + const subsetNode = new USDNode( `subset_${i}`, 'GeomSubset' ); + subsetNode.addMetadata( 'prepend apiSchemas', '["MaterialBindingAPI"]' ); + subsetNode.addProperty( 'uniform token elementType = "face"' ); + subsetNode.addProperty( 'uniform token familyName = "materialBind"' ); + subsetNode.addProperty( `int[] indices = [${indices.join( ', ' )}]` ); + subsetNode.addProperty( + `rel material:binding = ` + ); + node.addChild( subsetNode ); + + } + + } + return node; } diff --git a/manual/en/installation.html b/manual/en/installation.html index 95696390532d55..558bb8b9466959 100644 --- a/manual/en/installation.html +++ b/manual/en/installation.html @@ -109,21 +109,10 @@
- Place a jsconfig.json (or tsconfig.json for TypeScript projects) in your project's root. Adding the configuration below helps your editor locate three.js files for enhanced auto-completion. + Community-maintained TypeScript type definitions for three.js are available at [link:https://github.com/three-types/three-ts-types three-types/three-ts-types].
-
-{
- "compilerOptions": {
- // other options...
- "paths": {
- "three/webgpu": ["node_modules/three/build/three.webgpu.js"],
- "three/tsl": ["node_modules/three/build/three.tsl.js"],
- },
- }
-}
-
- Placez un fichier jsconfig.json (ou tsconfig.json pour les projets TypeScript) à la racine de votre projet. L'ajout de la configuration ci-dessous aide votre éditeur à localiser les fichiers three.js pour une auto-complétion améliorée. + Community-maintained TypeScript type definitions for three.js are available at [link:https://github.com/three-types/three-ts-types three-types/three-ts-types].
-
-{
- "compilerOptions": {
- // other options...
- "paths": {
- "three/webgpu": ["node_modules/three/build/three.webgpu.js"],
- "three/tsl": ["node_modules/three/build/three.tsl.js"],
- },
- }
-}
-
- 在项目根目录放置一个 jsconfig.json(TypeScript 项目则使用 tsconfig.json)。添加以下配置可以帮助编辑器定位 three.js 文件,从而增强自动补全功能。 + Community-maintained TypeScript type definitions for three.js are available at [link:https://github.com/three-types/three-ts-types three-types/three-ts-types].
-
-{
- "compilerOptions": {
- // other options...
- "paths": {
- "three/webgpu": ["node_modules/three/build/three.webgpu.js"],
- "three/tsl": ["node_modules/three/build/three.tsl.js"],
- },
- }
-}
-