diff --git a/examples/js/loaders/GLTFLoader.js b/examples/js/loaders/GLTFLoader.js index 1027db54830cd4..d76a0cc8b4260b 100644 --- a/examples/js/loaders/GLTFLoader.js +++ b/examples/js/loaders/GLTFLoader.js @@ -243,7 +243,11 @@ THREE.GLTFLoader = ( function () { // in addUnknownExtensionsToUserData(). // Remove this workaround if we move all the existing // extension handlers to plugin system - extensions[ plugin.name ] = true; + if ( plugin.addExtensionsToUserData !== true ) { + + extensions[ plugin.name ] = true; + + } } @@ -1898,13 +1902,21 @@ THREE.GLTFLoader = ( function () { } ); - Promise.all( [ + Promise.all( this._invokeAll( function ( ext ) { - this.getDependencies( 'scene' ), - this.getDependencies( 'animation' ), - this.getDependencies( 'camera' ), + return ext.beforeRoot && ext.beforeRoot(); - ] ).then( function ( dependencies ) { + } ) ).then( function () { + + return Promise.all( [ + + parser.getDependencies( 'scene' ), + parser.getDependencies( 'animation' ), + parser.getDependencies( 'camera' ), + + ] ); + + } ).then( function ( dependencies ) { var result = { scene: dependencies[ 0 ][ json.scene || 0 ], @@ -1920,7 +1932,15 @@ THREE.GLTFLoader = ( function () { assignExtrasToUserData( result, json ); - onLoad( result ); + Promise.all( parser._invokeAll( function ( ext ) { + + return ext.afterRoot && ext.afterRoot( result ); + + } ) ).then( function () { + + onLoad( result ); + + } ); } ).catch( onError ); diff --git a/examples/jsm/loaders/GLTFLoader.js b/examples/jsm/loaders/GLTFLoader.js index 6fc32b44bbdf43..0aad6eee5a9fd0 100644 --- a/examples/jsm/loaders/GLTFLoader.js +++ b/examples/jsm/loaders/GLTFLoader.js @@ -308,7 +308,11 @@ var GLTFLoader = ( function () { // in addUnknownExtensionsToUserData(). // Remove this workaround if we move all the existing // extension handlers to plugin system - extensions[ plugin.name ] = true; + if ( plugin.addExtensionsToUserData !== true ) { + + extensions[ plugin.name ] = true; + + } } @@ -1963,13 +1967,21 @@ var GLTFLoader = ( function () { } ); - Promise.all( [ + Promise.all( this._invokeAll( function ( ext ) { - this.getDependencies( 'scene' ), - this.getDependencies( 'animation' ), - this.getDependencies( 'camera' ), + return ext.beforeRoot && ext.beforeRoot(); - ] ).then( function ( dependencies ) { + } ) ).then( function () { + + return Promise.all( [ + + parser.getDependencies( 'scene' ), + parser.getDependencies( 'animation' ), + parser.getDependencies( 'camera' ), + + ] ); + + } ).then( function ( dependencies ) { var result = { scene: dependencies[ 0 ][ json.scene || 0 ], @@ -1985,7 +1997,15 @@ var GLTFLoader = ( function () { assignExtrasToUserData( result, json ); - onLoad( result ); + Promise.all( parser._invokeAll( function ( ext ) { + + return ext.afterRoot && ext.afterRoot( result ); + + } ) ).then( function () { + + onLoad( result ); + + } ); } ).catch( onError ); diff --git a/examples/jsm/loaders/gltf_plugins/KHR_materials_variants.js b/examples/jsm/loaders/gltf_plugins/KHR_materials_variants.js new file mode 100644 index 00000000000000..81ec1b5eee5891 --- /dev/null +++ b/examples/jsm/loaders/gltf_plugins/KHR_materials_variants.js @@ -0,0 +1,104 @@ +/** + * Materials variants extension + * + * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_variants + */ +export default function GLTFMaterialsVariantsExtension( parser ) { + + this.parser = parser; + this.name = 'KHR_materials_variants'; + this.addExtensionsToUserData = true; + +} + +GLTFMaterialsVariantsExtension.prototype.afterRoot = function ( gltf ) { + + var parser = this.parser; + var json = parser.json; + var name = this.name; + + if ( ! json.extensions || ! json.extensions[ name ] ) return null; + + var extensionDef = json.extensions[ name ]; + var variantsDef = extensionDef.variants || []; + var variants = []; + + for ( var i = 0, il = variantsDef.length; i < il; i ++ ) { + + variants.push( variantsDef[ i ].name ); + + } + + for ( var i = 0, il = gltf.scenes.length; i < il; i ++ ) { + + gltf.scenes[ i ].userData.variants = variants; + + } + + gltf.userData.variants = variants; + + gltf.userData.selectVariant = function selectVariant ( variantName ) { + + var scenes = gltf.scenes; + var pending = []; + + for ( var i = 0, il = scenes.length; i < il; i ++ ) { + + var scene = scenes[ i ]; + + var variantIndex = variants.indexOf( variantName ); + + if ( variantIndex === - 1 ) continue; + + scene.traverse( function ( object ) { + + if ( ! object.isMesh || ! object.userData.gltfExtensions || + ! object.userData.gltfExtensions[ name ] ) return; + + var meshVariantDef = object.userData.gltfExtensions[ name ]; + var mappings = meshVariantDef.mappings || []; + var materialIndex = - 1; + + for ( var j = 0, jl = mappings.length; j < jl; j ++ ) { + + var mapping = mappings[ j ]; + + if ( mapping.variants.indexOf( variantIndex ) !== - 1 ) { + + materialIndex = mapping.material; + break; + + } + + } + + if ( materialIndex >= 0 ) { + + if ( ! object.userData.originalMaterial ) { + + object.userData.originalMaterial = object.material; + + } + + pending.push( parser.getDependency( 'material', materialIndex ).then( function ( material ) { + + object.material = material; + parser.assignFinalMaterial( object ); + + } ) ); + + } else if ( object.userData.originalMaterial ) { + + object.material = object.userData.originalMaterial; + + } + + } ); + + } + + return Promise.all( pending ); + + }; + +}; diff --git a/examples/webgl_loader_gltf_variants.html b/examples/webgl_loader_gltf_variants.html index 1e591dcb3fb4e7..2a5107fa91c26c 100644 --- a/examples/webgl_loader_gltf_variants.html +++ b/examples/webgl_loader_gltf_variants.html @@ -24,6 +24,8 @@ import { GLTFLoader } from './jsm/loaders/GLTFLoader.js'; import { RGBELoader } from './jsm/loaders/RGBELoader.js'; + import GLTFMaterialsVariantsExtension from './jsm/loaders/gltf_plugins/KHR_materials_variants.js'; + let camera, scene, renderer; let gui; @@ -59,7 +61,12 @@ // model - const loader = new GLTFLoader().setPath( 'models/gltf/MaterialsVariantsShoe/glTF/' ); + const loader = new GLTFLoader().register( function ( parser ) { + + return new GLTFMaterialsVariantsExtension( parser ); + + } ).setPath( 'models/gltf/MaterialsVariantsShoe/glTF/' ); + loader.load( 'MaterialsVariantsShoe.gltf', function ( gltf ) { gltf.scene.scale.set( 10.0, 10.0, 10.0 ); @@ -69,16 +76,15 @@ // GUI gui = new GUI(); - // Details of the KHR_materials_variants extension used here can be found below - // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_variants - const parser = gltf.parser; - const variantsExtension = gltf.userData.gltfExtensions[ 'KHR_materials_variants' ]; - const variants = variantsExtension.variants.map( ( variant ) => variant.name ); + const variants = gltf.userData.variants; const variantsCtrl = gui.add( state, 'variant', variants ).name( 'Variant' ); - selectVariant( scene, parser, variantsExtension, state.variant ); + variantsCtrl.onChange( async ( value ) => { + + await gltf.userData.selectVariant( value ); + render(); - variantsCtrl.onChange( ( value ) => selectVariant( scene, parser, variantsExtension, value ) ); + } ); render(); @@ -108,44 +114,6 @@ } - function selectVariant( scene, parser, extension, variantName ) { - - const variantIndex = extension.variants.findIndex( ( v ) => v.name.includes( variantName ) ); - - scene.traverse( async ( object ) => { - - if ( ! object.isMesh || ! object.userData.gltfExtensions ) return; - - const meshVariantDef = object.userData.gltfExtensions[ 'KHR_materials_variants' ]; - - if ( ! meshVariantDef ) return; - - if ( ! object.userData.originalMaterial ) { - - object.userData.originalMaterial = object.material; - - } - - const mapping = meshVariantDef.mappings - .find( ( mapping ) => mapping.variants.includes( variantIndex ) ); - - if ( mapping ) { - - object.material = await parser.getDependency( 'material', mapping.material ); - parser.assignFinalMaterial(object); - - } else { - - object.material = object.userData.originalMaterial; - - } - - render(); - - } ); - - } - function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight;