Преглед изворни кода

Examples: Add model selector to glTF loader examples (#32364)

mrdoob пре 1 месец
родитељ
комит
505e6be910

BIN
examples/screenshots/webgl_loader_gltf.jpg


BIN
examples/screenshots/webgpu_loader_gltf.jpg


+ 93 - 13
examples/webgl_loader_gltf.html

@@ -10,8 +10,6 @@
 	<body>
 		<div id="info">
 			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - GLTFLoader<br />
-			Battle Damaged Sci-fi Helmet by
-			<a href="https://sketchfab.com/theblueturtle_" target="_blank" rel="noopener">theblueturtle_</a><br />
 			<a href="https://hdrihaven.com/hdri/?h=royal_esplanade" target="_blank" rel="noopener">Royal Esplanade</a> from <a href="https://hdrihaven.com/" target="_blank" rel="noopener">HDRI Haven</a>
 		</div>
 
@@ -31,8 +29,10 @@
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 			import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
 			import { HDRLoader } from 'three/addons/loaders/HDRLoader.js';
+			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
 
-			let camera, scene, renderer;
+			let camera, scene, renderer, controls;
+			let currentModel;
 
 			init();
 
@@ -59,20 +59,31 @@
 
 						// model
 
-						const loader = new GLTFLoader().setPath( 'models/gltf/DamagedHelmet/glTF/' );
-						loader.load( 'DamagedHelmet.gltf', async function ( gltf ) {
+						fetch( 'https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/main/Models/model-index.json' )
+							.then( response => response.json() )
+							.then( models => {
 
-							const model = gltf.scene;
+								const gui = new GUI();
+								const modelNames = models.map( m => m.name );
+								const params = { model: 'DamagedHelmet' };
 
-							// wait until the model can be added to the scene without blocking due to shader compilation
+								if ( ! modelNames.includes( params.model ) && modelNames.length > 0 ) {
 
-							await renderer.compileAsync( model, camera, scene );
+									params.model = modelNames[ 0 ];
 
-							scene.add( model );
+								}
 
-							render();
-			
-						} );
+								gui.add( params, 'model', modelNames ).onChange( name => {
+
+									const modelInfo = models.find( m => m.name === name );
+									loadModel( modelInfo );
+
+								} );
+
+								const initialModel = models.find( m => m.name === params.model );
+								if ( initialModel ) loadModel( initialModel );
+
+							} );
 
 					} );
 
@@ -83,7 +94,7 @@
 				renderer.toneMappingExposure = 1;
 				container.appendChild( renderer.domElement );
 
-				const controls = new OrbitControls( camera, renderer.domElement );
+				controls = new OrbitControls( camera, renderer.domElement );
 				controls.addEventListener( 'change', render ); // use if there is no animation loop
 				controls.minDistance = 2;
 				controls.maxDistance = 10;
@@ -94,6 +105,75 @@
 
 			}
 
+			function loadModel( modelInfo ) {
+
+				const variants = modelInfo.variants;
+				const variant = variants[ 'glTF-Binary' ] || variants[ 'glTF' ];
+				const url = `https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/main/Models/${ modelInfo.name }/${ variant.endsWith( '.glb' ) ? 'glTF-Binary' : 'glTF' }/${ variant }`;
+
+				if ( currentModel ) {
+
+					scene.remove( currentModel );
+					currentModel = null;
+					render();
+
+				}
+
+				const loader = new GLTFLoader();
+				loader.load( url, async function ( gltf ) {
+
+					currentModel = gltf.scene;
+
+					// wait until the model can be added to the scene without blocking due to shader compilation
+
+					await renderer.compileAsync( currentModel, camera, scene );
+
+					scene.add( currentModel );
+
+					// scale to 1.0
+
+					const box = new THREE.Box3().setFromObject( currentModel );
+					const size = box.getSize( new THREE.Vector3() );
+					const maxSize = Math.max( size.x, size.y, size.z );
+					currentModel.scale.multiplyScalar( 1.0 / maxSize );
+
+					fitCameraToSelection( camera, controls, currentModel );
+
+					render();
+
+				} );
+
+			}
+
+			function fitCameraToSelection( camera, controls, selection, fitOffset = 1.3 ) {
+
+				const box = new THREE.Box3();
+				box.setFromObject( selection );
+
+				const size = box.getSize( new THREE.Vector3() );
+				const center = box.getCenter( new THREE.Vector3() );
+
+				const maxSize = Math.max( size.x, size.y, size.z );
+				const fitHeightDistance = maxSize / ( 2 * Math.atan( Math.PI * camera.fov / 360 ) );
+				const fitWidthDistance = fitHeightDistance / camera.aspect;
+				const distance = fitOffset * Math.max( fitHeightDistance, fitWidthDistance );
+
+				const direction = controls.target.clone().sub( camera.position ).normalize().multiplyScalar( distance );
+
+				controls.maxDistance = distance * 10;
+				controls.minDistance = distance / 10;
+				controls.target.copy( center );
+
+				camera.near = distance / 100;
+				camera.far = distance * 100;
+				camera.updateProjectionMatrix();
+
+				camera.position.copy( controls.target ).sub( direction );
+
+				controls.update();
+
+			}
+
 			function onWindowResize() {
 
 				camera.aspect = window.innerWidth / window.innerHeight;

+ 98 - 9
examples/webgpu_loader_gltf.html

@@ -16,8 +16,6 @@
 			</div>
 
 			<small>
-				Battle Damaged Sci-fi Helmet by
-				<a href="https://sketchfab.com/theblueturtle_" target="_blank" rel="noopener">theblueturtle_</a><br />
 				<a href="https://hdrihaven.com/hdri/?h=royal_esplanade" target="_blank" rel="noopener">Royal Esplanade</a> by <a href="https://hdrihaven.com/" target="_blank" rel="noopener">HDRI Haven</a>
 			</small>
 		</div>
@@ -42,7 +40,10 @@
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 			import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
 
-			let camera, scene, renderer;
+			import { Inspector } from 'three/addons/inspector/Inspector.js';
+
+			let camera, scene, renderer, controls;
+			let currentModel;
 
 			init().then( render );
 
@@ -67,25 +68,47 @@
 						scene.background = texture;
 						scene.environment = texture;
 
-					} );
+						// model
 
-				const loader = new GLTFLoader().setPath( 'models/gltf/DamagedHelmet/glTF/' );
-				loader.load( 'DamagedHelmet.gltf', function ( gltf ) {
+						fetch( 'https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/main/Models/model-index.json' )
+							.then( response => response.json() )
+							.then( models => {
 
-					scene.add( gltf.scene );
+								const gui = renderer.inspector.createParameters( 'Model' );
+								const modelNames = models.map( m => m.name );
+								const params = { model: 'DamagedHelmet' };
 
-				} );
+								if ( ! modelNames.includes( params.model ) && modelNames.length > 0 ) {
+
+									params.model = modelNames[ 0 ];
+
+								}
+
+								gui.add( params, 'model', modelNames ).onChange( name => {
+
+									const modelInfo = models.find( m => m.name === name );
+									loadModel( modelInfo );
+
+								} );
+
+								const initialModel = models.find( m => m.name === params.model );
+								if ( initialModel ) loadModel( initialModel );
+
+							} );
+
+					} );
 
 				renderer = new THREE.WebGPURenderer( { antialias: true/*, compatibilityMode: true*/ } );
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				renderer.setAnimationLoop( render );
 				renderer.toneMapping = THREE.ACESFilmicToneMapping;
+				renderer.inspector = new Inspector();
 				container.appendChild( renderer.domElement );
 
 				await renderer.init();
 
-				const controls = new OrbitControls( camera, renderer.domElement );
+				controls = new OrbitControls( camera, renderer.domElement );
 				controls.minDistance = 2;
 				controls.maxDistance = 10;
 				controls.target.set( 0, 0, - 0.2 );
@@ -95,6 +118,72 @@
 
 			}
 
+			function loadModel( modelInfo ) {
+
+				const variants = modelInfo.variants;
+				const variant = variants[ 'glTF-Binary' ] || variants[ 'glTF' ];
+				const url = `https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Assets/main/Models/${ modelInfo.name }/${ variant.endsWith( '.glb' ) ? 'glTF-Binary' : 'glTF' }/${ variant }`;
+
+				if ( currentModel ) {
+
+					scene.remove( currentModel );
+					currentModel = null;
+
+				}
+
+				const loader = new GLTFLoader();
+				loader.load( url, async function ( gltf ) {
+
+					currentModel = gltf.scene;
+
+					// wait until the model can be added to the scene without blocking due to shader compilation
+
+					await renderer.compileAsync( currentModel, camera, scene );
+
+					scene.add( currentModel );
+
+					// scale to 1.0
+
+					const box = new THREE.Box3().setFromObject( currentModel );
+					const size = box.getSize( new THREE.Vector3() );
+					const maxSize = Math.max( size.x, size.y, size.z );
+					currentModel.scale.multiplyScalar( 1.0 / maxSize );
+
+					fitCameraToSelection( camera, controls, currentModel );
+
+				} );
+
+			}
+
+			function fitCameraToSelection( camera, controls, selection, fitOffset = 1.3 ) {
+
+				const box = new THREE.Box3();
+				box.setFromObject( selection );
+
+				const size = box.getSize( new THREE.Vector3() );
+				const center = box.getCenter( new THREE.Vector3() );
+
+				const maxSize = Math.max( size.x, size.y, size.z );
+				const fitHeightDistance = maxSize / ( 2 * Math.atan( Math.PI * camera.fov / 360 ) );
+				const fitWidthDistance = fitHeightDistance / camera.aspect;
+				const distance = fitOffset * Math.max( fitHeightDistance, fitWidthDistance );
+
+				const direction = controls.target.clone().sub( camera.position ).normalize().multiplyScalar( distance );
+
+				controls.maxDistance = distance * 10;
+				controls.minDistance = distance / 10;
+				controls.target.copy( center );
+
+				camera.near = distance / 100;
+				camera.far = distance * 100;
+				camera.updateProjectionMatrix();
+
+				camera.position.copy( controls.target ).sub( direction );
+
+				controls.update();
+
+			}
+
 			function onWindowResize() {
 
 				camera.aspect = window.innerWidth / window.innerHeight;

粤ICP备19079148号