@needle-tools/gltf-progressive 3.6.0-alpha.3 → 3.6.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +42 -8
  3. package/examples/modelviewer-multiple.html +4 -4
  4. package/examples/modelviewer.html +4 -4
  5. package/examples/offscreen/index.html +15 -0
  6. package/examples/offscreen/main.js +98 -0
  7. package/examples/react-three-fiber/index.html +2 -2
  8. package/examples/react-three-fiber/package-lock.json +482 -484
  9. package/examples/react-three-fiber/package.json +4 -4
  10. package/examples/react-three-fiber/src/App.tsx +76 -21
  11. package/examples/react-three-fiber/src/styles.css +2 -4
  12. package/examples/react-three-fiber/vite.config.js +2 -2
  13. package/examples/shared/example-utils.js +297 -0
  14. package/examples/shared/example.css +44 -0
  15. package/examples/shared/runtime.js +34 -0
  16. package/examples/threejs/index.html +6 -10
  17. package/examples/threejs/main.js +5 -23
  18. package/examples/webgpu/index.html +15 -0
  19. package/examples/webgpu/main.js +105 -0
  20. package/examples/worker-rendering/index.html +15 -0
  21. package/examples/worker-rendering/main.js +109 -0
  22. package/examples/worker-rendering/worker.js +166 -0
  23. package/gltf-progressive.js +429 -355
  24. package/gltf-progressive.min.js +9 -9
  25. package/gltf-progressive.umd.cjs +9 -9
  26. package/lib/extension.js +5 -7
  27. package/lib/loaders.d.ts +1 -8
  28. package/lib/loaders.js +15 -2
  29. package/lib/lods.debug.js +1 -1
  30. package/lib/lods.manager.d.ts +3 -0
  31. package/lib/lods.manager.js +62 -18
  32. package/lib/utils.d.ts +1 -1
  33. package/lib/utils.internal.d.ts +27 -0
  34. package/lib/utils.internal.js +68 -25
  35. package/lib/version.js +1 -1
  36. package/lib/worker/loader.mainthread.js +6 -4
  37. package/package.json +8 -3
@@ -4,17 +4,17 @@
4
4
  "main": "src/index.tsx",
5
5
  "dependencies": {
6
6
  "@needle-tools/gltf-progressive": "file:../..",
7
- "@react-three/drei": "^9.0.1",
8
- "@react-three/fiber": "^8.0.6",
7
+ "@react-three/drei": "9.122.0",
8
+ "@react-three/fiber": "8.18.0",
9
9
  "react": "^18.0.0",
10
10
  "react-dom": "^18.0.0",
11
- "three": "0.162.0",
11
+ "three": "0.184.0",
12
12
  "typescript": "4.7.4"
13
13
  },
14
14
  "devDependencies": {
15
15
  "@types/react": "18.0.15",
16
16
  "@types/react-dom": "17.0.0",
17
- "@types/three": "0.162.0",
17
+ "@types/three": "0.184.0",
18
18
  "@vitejs/plugin-basic-ssl": "^1.0.1",
19
19
  "@vitejs/plugin-react": "^1.3.0",
20
20
  "typescript": "4.1.3",
@@ -2,37 +2,92 @@
2
2
 
3
3
  /* eslint-disable */
4
4
  import * as React from 'react'
5
+ import * as THREE from 'three'
5
6
  import { Canvas, useThree } from '@react-three/fiber'
7
+ import { OrbitControls, useGLTF } from '@react-three/drei'
8
+ import { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment.js'
6
9
 
7
10
  import { useNeedleProgressive } from '@needle-tools/gltf-progressive'
8
- import { Environment, OrbitControls, useGLTF } from '@react-three/drei'
11
+ // @ts-ignore shared plain JS example helpers
12
+ import { MODEL_URLS } from '../../shared/example-utils.js'
9
13
 
10
- function MyModel() {
11
- const { gl } = useThree()
12
- const url = 'https://engine.needle.tools/demos/gltf-progressive/assets/church/model.glb'
14
+ function RoomEnvironmentSetup() {
15
+ const { gl, scene } = useThree()
16
+
17
+ React.useEffect(() => {
18
+ const pmrem = new THREE.PMREMGenerator(gl)
19
+ const environment = pmrem.fromScene(new RoomEnvironment(), 0.04).texture
20
+ scene.environment = environment
21
+
22
+ return () => {
23
+ scene.environment = null
24
+ environment.dispose()
25
+ pmrem.dispose()
26
+ }
27
+ }, [gl, scene])
28
+
29
+ return null
30
+ }
31
+
32
+ function Model({ controlsRef, url }: { controlsRef: React.MutableRefObject<any>, url: string }) {
33
+ const { gl, camera } = useThree()
13
34
  const { scene } = useGLTF(url, false, false, (loader) => {
14
- useNeedleProgressive(loader as any, gl as any);
35
+ useNeedleProgressive(loader as any, gl as any)
15
36
  })
37
+
38
+ React.useLayoutEffect(() => {
39
+ const box = new THREE.Box3().setFromObject(scene)
40
+ const size = box.getSize(new THREE.Vector3())
41
+ const center = box.getCenter(new THREE.Vector3())
42
+ const maxSize = Math.max(size.x, size.y, size.z, 0.0001)
43
+ const perspectiveCamera = camera as THREE.PerspectiveCamera
44
+ const distance = maxSize / (2 * Math.tan(THREE.MathUtils.degToRad(perspectiveCamera.fov) / 2)) * 1.55
45
+ const direction = new THREE.Vector3(0.45, 0.35, 1).normalize()
46
+
47
+ perspectiveCamera.position.copy(center).addScaledVector(direction, distance)
48
+ perspectiveCamera.near = Math.max(0.01, distance / 100)
49
+ perspectiveCamera.far = Math.max(100, distance * 100)
50
+ perspectiveCamera.updateProjectionMatrix()
51
+
52
+ const controls = controlsRef.current
53
+ if (controls) {
54
+ controls.target.copy(center)
55
+ controls.minDistance = distance * 0.1
56
+ controls.maxDistance = distance * 10
57
+ controls.update()
58
+ }
59
+ }, [camera, controlsRef, scene])
60
+
16
61
  return <primitive object={scene} />
17
62
  }
18
63
 
19
-
20
64
  export default function App() {
65
+ const controlsRef = React.useRef<any>(null)
66
+ const [sceneIndex, setSceneIndex] = React.useState(0)
67
+ const modelUrl = MODEL_URLS[sceneIndex % MODEL_URLS.length]
68
+
69
+ React.useEffect(() => {
70
+ ;(globalThis as any).__GLTF_PROGRESSIVE_R3F_EXAMPLE__ = {
71
+ currentUrl: modelUrl,
72
+ sceneIndex,
73
+ }
74
+ }, [modelUrl, sceneIndex])
75
+
21
76
  return (
22
- <Canvas
23
- frameloop="demand"
24
- camera={{ position: [25, 15, 25] }}>
25
- <OrbitControls target={[0 , 10, 0]} />
26
- <ambientLight intensity={1} />
27
- <spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} />
28
- <pointLight position={[-10, -10, -10]} />
29
- <Environment
30
- files="https://dl.polyhaven.org/file/ph-assets/HDRIs/hdr/2k/evening_road_01_2k.hdr"
31
- // ground={{ height: 5, radius: 40, scale: 10 }}
32
- />
33
- <MyModel />
34
- </Canvas>
77
+ <>
78
+ <div className="example-toolbar">
79
+ <button type="button" onClick={() => setSceneIndex((sceneIndex + 1) % MODEL_URLS.length)}>Change scene</button>
80
+ </div>
81
+ <Canvas
82
+ camera={{ position: [0.5, 1.3, 2], fov: 60, near: 0.01, far: 200 }}>
83
+ <RoomEnvironmentSetup />
84
+ <gridHelper args={[50, 50, 0x444444, 0x666666]} />
85
+ <directionalLight position={[-50, 20, 50]} intensity={1} />
86
+ <OrbitControls ref={controlsRef} enableDamping dampingFactor={0.08} target={[0, 0.5, 0]} />
87
+ <React.Suspense fallback={null}>
88
+ <Model key={modelUrl} controlsRef={controlsRef} url={modelUrl} />
89
+ </React.Suspense>
90
+ </Canvas>
91
+ </>
35
92
  )
36
93
  }
37
-
38
-
@@ -1,3 +1,5 @@
1
+ @import '../../shared/example.css';
2
+
1
3
  * {
2
4
  box-sizing: border-box;
3
5
  }
@@ -10,7 +12,3 @@ body,
10
12
  margin: 0;
11
13
  padding: 0;
12
14
  }
13
-
14
- body {
15
- background: #f0f0f0;
16
- }
@@ -14,7 +14,7 @@ export default defineConfig(async (command) => {
14
14
  plugins: [
15
15
  react(),
16
16
  basicSsl(),
17
- viteCompression({ deleteOriginFile: true }),
17
+ viteCompression({ deleteOriginFile: false }),
18
18
  ],
19
19
 
20
20
  server: {
@@ -36,4 +36,4 @@ export default defineConfig(async (command) => {
36
36
  }
37
37
  }
38
38
  }
39
- });
39
+ });
@@ -0,0 +1,297 @@
1
+ export const MODEL_URLS = [
2
+ "https://engine.needle.tools/demos/gltf-progressive/assets/church/model.glb",
3
+ "https://engine.needle.tools/demos/gltf-progressive/assets/putti gruppe/model.glb",
4
+ "https://engine.needle.tools/demos/gltf-progressive/assets/cyberpunk/model.glb",
5
+ "https://engine.needle.tools/demos/gltf-progressive/assets/robot/model.glb",
6
+ "https://engine.needle.tools/demos/gltf-progressive/assets/vase/model.glb",
7
+ "https://engine.needle.tools/demos/gltf-progressive/assets/jupiter_und_ganymed/model.glb",
8
+ ];
9
+
10
+ export const DEFAULT_MODEL_URL = MODEL_URLS[0];
11
+
12
+ export function createExampleState(label) {
13
+ const state = {
14
+ label,
15
+ done: false,
16
+ ok: false,
17
+ errors: [],
18
+ frames: 0,
19
+ loaded: false,
20
+ renderer: "",
21
+ currentUrl: "",
22
+ sceneIndex: 0,
23
+ sceneLoads: 0,
24
+ progressiveObjects: 0,
25
+ lodChanges: 0,
26
+ lodChangeTypes: [],
27
+ };
28
+ globalThis.__GLTF_PROGRESSIVE_EXAMPLE__ = state;
29
+
30
+ globalThis.addEventListener?.("error", event => {
31
+ state.errors.push(event.message || String(event.error || event));
32
+ });
33
+ globalThis.addEventListener?.("unhandledrejection", event => {
34
+ state.errors.push(event.reason?.message || String(event.reason || event));
35
+ });
36
+
37
+ return state;
38
+ }
39
+
40
+ export function updateStatus(message) {
41
+ const element = globalThis.document?.getElementById("status");
42
+ if (element) element.textContent = message;
43
+ }
44
+
45
+ export function markReady(state, rendererLabel) {
46
+ state.loaded = true;
47
+ state.ok = true;
48
+ state.done = true;
49
+ state.renderer = rendererLabel;
50
+ updateStatus(rendererLabel);
51
+ }
52
+
53
+ export function markError(state, error) {
54
+ const message = error?.stack || error?.message || String(error);
55
+ state.errors.push(message);
56
+ state.ok = false;
57
+ state.done = true;
58
+ updateStatus(message);
59
+ }
60
+
61
+ export function getModelUrl(params = new URLSearchParams(globalThis.location?.search || ""), sceneIndex = 0) {
62
+ const asset = params.get("asset");
63
+ if (asset === "minimal") return createMinimalGltfUrl();
64
+ if (asset) return new URL(asset, globalThis.location?.href).href;
65
+ return MODEL_URLS[normalizeSceneIndex(sceneIndex)];
66
+ }
67
+
68
+ export function normalizeSceneIndex(sceneIndex) {
69
+ return ((sceneIndex % MODEL_URLS.length) + MODEL_URLS.length) % MODEL_URLS.length;
70
+ }
71
+
72
+ export function getInitialSceneIndex(params = new URLSearchParams(globalThis.location?.search || "")) {
73
+ const scene = Number(params.get("scene"));
74
+ return Number.isFinite(scene) ? normalizeSceneIndex(scene) : 0;
75
+ }
76
+
77
+ export function createSceneChangeButton(onChange) {
78
+ const document = globalThis.document;
79
+ if (!document) return null;
80
+
81
+ const toolbar = document.createElement("div");
82
+ toolbar.className = "example-toolbar";
83
+ const button = document.createElement("button");
84
+ button.type = "button";
85
+ button.textContent = "Change scene";
86
+ button.addEventListener("click", () => onChange());
87
+ toolbar.append(button);
88
+ document.body.append(toolbar);
89
+ return button;
90
+ }
91
+
92
+ export function markSceneLoading(state, sceneIndex, url) {
93
+ state.sceneIndex = normalizeSceneIndex(sceneIndex);
94
+ state.currentUrl = url;
95
+ updateStatus("loading");
96
+ }
97
+
98
+ export function markSceneLoaded(state, runtime, root) {
99
+ state.loaded = true;
100
+ state.sceneLoads += 1;
101
+ state.progressiveObjects = countProgressiveObjects(runtime, root);
102
+ }
103
+
104
+ export function trackLODChanges(lodsManager, state, notify) {
105
+ return lodsManager.addEventListener?.("changed", event => {
106
+ state.lodChanges += 1;
107
+ state.lodChangeTypes.push(event.type);
108
+ if (notify) notify(event);
109
+ });
110
+ }
111
+
112
+ export function countProgressiveObjects(runtime, root) {
113
+ const progressive = runtime.NEEDLE_progressive;
114
+ if (!progressive || !root) return 0;
115
+
116
+ let count = 0;
117
+ root.traverse?.(object => {
118
+ if (object?.isMesh && progressive.hasLODLevelAvailable(object)) count += 1;
119
+ const material = object?.material;
120
+ if (material && progressive.hasLODLevelAvailable(material)) count += 1;
121
+ });
122
+ return count;
123
+ }
124
+
125
+ export function createMinimalGltfUrl() {
126
+ const positions = new Float32Array([
127
+ -0.8, -0.5, 0,
128
+ 0.8, -0.5, 0,
129
+ 0, 0.8, 0,
130
+ ]);
131
+ const normals = new Float32Array([
132
+ 0, 0, 1,
133
+ 0, 0, 1,
134
+ 0, 0, 1,
135
+ ]);
136
+ const indices = new Uint16Array([0, 1, 2]);
137
+ const bytes = concatBytes(
138
+ new Uint8Array(positions.buffer),
139
+ new Uint8Array(normals.buffer),
140
+ new Uint8Array(indices.buffer),
141
+ );
142
+ const gltf = {
143
+ asset: { version: "2.0", generator: "gltf-progressive example" },
144
+ scene: 0,
145
+ scenes: [{ nodes: [0] }],
146
+ nodes: [{ mesh: 0, name: "MinimalTriangle" }],
147
+ meshes: [{
148
+ primitives: [{
149
+ attributes: { POSITION: 0, NORMAL: 1 },
150
+ indices: 2,
151
+ material: 0,
152
+ }],
153
+ }],
154
+ materials: [{
155
+ pbrMetallicRoughness: {
156
+ baseColorFactor: [0.2, 0.55, 1, 1],
157
+ roughnessFactor: 0.55,
158
+ metallicFactor: 0,
159
+ },
160
+ }],
161
+ buffers: [{
162
+ uri: `data:application/octet-stream;base64,${base64(bytes)}`,
163
+ byteLength: bytes.byteLength,
164
+ }],
165
+ bufferViews: [
166
+ { buffer: 0, byteOffset: 0, byteLength: positions.byteLength, target: 34962 },
167
+ { buffer: 0, byteOffset: positions.byteLength, byteLength: normals.byteLength, target: 34962 },
168
+ { buffer: 0, byteOffset: positions.byteLength + normals.byteLength, byteLength: indices.byteLength, target: 34963 },
169
+ ],
170
+ accessors: [
171
+ { bufferView: 0, componentType: 5126, count: 3, type: "VEC3", min: [-0.8, -0.5, 0], max: [0.8, 0.8, 0] },
172
+ { bufferView: 1, componentType: 5126, count: 3, type: "VEC3" },
173
+ { bufferView: 2, componentType: 5123, count: 3, type: "SCALAR" },
174
+ ],
175
+ };
176
+ return `data:model/gltf+json;charset=utf-8,${encodeURIComponent(JSON.stringify(gltf))}`;
177
+ }
178
+
179
+ export function setupScene(THREE, width, height) {
180
+ const scene = new THREE.Scene();
181
+ scene.background = new THREE.Color(0x555555);
182
+
183
+ const camera = new THREE.PerspectiveCamera(60, width / height, 0.01, 200);
184
+ camera.position.set(0.5, 1.3, 2);
185
+
186
+ const grid = new THREE.GridHelper(50, 50, 0x444444, 0x666666);
187
+ scene.add(grid);
188
+
189
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
190
+ directionalLight.position.set(-50, 20, 50);
191
+ scene.add(directionalLight);
192
+
193
+ return { scene, camera };
194
+ }
195
+
196
+ export function setupRoomEnvironment(THREE, RoomEnvironment, renderer, scene, options = {}) {
197
+ if (!RoomEnvironment) return () => { };
198
+ const PMREMGenerator = options.PMREMGenerator || THREE.PMREMGenerator;
199
+ const pmremGenerator = new PMREMGenerator(renderer);
200
+ const environment = pmremGenerator.fromScene(new RoomEnvironment(), 0.04).texture;
201
+ scene.environment = environment;
202
+ return () => {
203
+ environment.dispose?.();
204
+ pmremGenerator.dispose();
205
+ };
206
+ }
207
+
208
+ export function createOrbitControls(OrbitControls, camera, domElement) {
209
+ const controls = new OrbitControls(camera, domElement);
210
+ controls.enableDamping = true;
211
+ controls.dampingFactor = 0.08;
212
+ controls.target.set(0, 0.5, 0);
213
+ controls.update();
214
+ return controls;
215
+ }
216
+
217
+ export function addLoadedScene(THREE, scene, gltf, options = {}) {
218
+ const root = gltf.scene;
219
+ scene.add(root);
220
+ const fit = fitObjectToView(THREE, options.camera, root, options.controls);
221
+ if (options.onFit) options.onFit(fit);
222
+ return root;
223
+ }
224
+
225
+ export function fitObjectToView(THREE, camera, root, controls) {
226
+ if (!camera) return null;
227
+ const box = new THREE.Box3().setFromObject(root);
228
+ const size = box.getSize(new THREE.Vector3());
229
+ const center = box.getCenter(new THREE.Vector3());
230
+ const maxSize = Math.max(size.x, size.y, size.z, 0.0001);
231
+ const distance = maxSize / (2 * Math.tan(THREE.MathUtils.degToRad(camera.fov) / 2)) * 1.55;
232
+ const direction = new THREE.Vector3(0.45, 0.35, 1).normalize();
233
+
234
+ camera.position.copy(center).addScaledVector(direction, distance);
235
+ camera.near = Math.max(0.01, distance / 100);
236
+ camera.far = Math.max(100, distance * 100);
237
+ camera.updateProjectionMatrix();
238
+
239
+ if (controls) {
240
+ controls.target.copy(center);
241
+ controls.minDistance = distance * 0.1;
242
+ controls.maxDistance = distance * 10;
243
+ controls.update();
244
+ }
245
+
246
+ return {
247
+ target: center.toArray(),
248
+ minDistance: distance * 0.1,
249
+ maxDistance: distance * 10,
250
+ };
251
+ }
252
+
253
+ export function resizeRenderer(renderer, camera, width, height, pixelRatio = 1) {
254
+ renderer.setPixelRatio?.(pixelRatio);
255
+ renderer.setSize(width, height, false);
256
+ camera.aspect = width / height;
257
+ camera.updateProjectionMatrix();
258
+ }
259
+
260
+ export function serializeCamera(camera) {
261
+ return {
262
+ position: camera.position.toArray(),
263
+ quaternion: camera.quaternion.toArray(),
264
+ near: camera.near,
265
+ far: camera.far,
266
+ fov: camera.fov,
267
+ aspect: camera.aspect,
268
+ };
269
+ }
270
+
271
+ export function applyCameraState(camera, state) {
272
+ if (!state) return;
273
+ camera.position.fromArray(state.position);
274
+ camera.quaternion.fromArray(state.quaternion);
275
+ camera.near = state.near;
276
+ camera.far = state.far;
277
+ camera.fov = state.fov;
278
+ camera.aspect = state.aspect;
279
+ camera.updateProjectionMatrix();
280
+ }
281
+
282
+ function concatBytes(...arrays) {
283
+ const total = arrays.reduce((sum, array) => sum + array.byteLength, 0);
284
+ const result = new Uint8Array(total);
285
+ let offset = 0;
286
+ for (const array of arrays) {
287
+ result.set(array, offset);
288
+ offset += array.byteLength;
289
+ }
290
+ return result;
291
+ }
292
+
293
+ function base64(bytes) {
294
+ let binary = "";
295
+ for (const byte of bytes) binary += String.fromCharCode(byte);
296
+ return btoa(binary);
297
+ }
@@ -0,0 +1,44 @@
1
+ html,
2
+ body {
3
+ height: 100%;
4
+ margin: 0;
5
+ overflow: hidden;
6
+ background: #20242a;
7
+ color: #f5f7fa;
8
+ font-family: system-ui, sans-serif;
9
+ }
10
+
11
+ canvas {
12
+ display: block;
13
+ width: 100vw;
14
+ height: 100vh;
15
+ }
16
+
17
+ .example-status {
18
+ position: fixed;
19
+ left: 12px;
20
+ top: 12px;
21
+ z-index: 10;
22
+ padding: 6px 8px;
23
+ background: rgba(18, 22, 28, 0.78);
24
+ color: #f5f7fa;
25
+ font-size: 12px;
26
+ line-height: 1.3;
27
+ }
28
+
29
+ .example-toolbar {
30
+ position: fixed;
31
+ right: 12px;
32
+ top: 12px;
33
+ z-index: 10;
34
+ }
35
+
36
+ .example-toolbar button {
37
+ padding: 6px 9px;
38
+ border: 1px solid rgba(245, 247, 250, 0.35);
39
+ background: rgba(18, 22, 28, 0.78);
40
+ color: #f5f7fa;
41
+ font: inherit;
42
+ font-size: 12px;
43
+ cursor: pointer;
44
+ }
@@ -0,0 +1,34 @@
1
+ const DEFAULT_THREE_VERSION = "0.184.0";
2
+ const DEFAULT_PROGRESSIVE_VERSION = "3.6.0-canary.5401de9";
3
+
4
+ export async function loadRuntime(options = {}) {
5
+ const params = options.params || new URLSearchParams(options.search || globalThis.location?.search || "");
6
+ const runtimeUrl = params.get("runtime");
7
+ if (runtimeUrl) {
8
+ return await import(new URL(runtimeUrl, globalThis.location?.href).href);
9
+ }
10
+
11
+ const threeVersion = params.get("three") || DEFAULT_THREE_VERSION;
12
+ const threeBase = `https://esm.sh/three@${threeVersion}`;
13
+ const progressiveUrl = params.get("progressive") || `https://esm.sh/@needle-tools/gltf-progressive@${DEFAULT_PROGRESSIVE_VERSION}?deps=three@${threeVersion}`;
14
+
15
+ const [THREE, THREE_WEBGPU, gltfLoaderModule, orbitControlsModule, roomEnvironmentModule, progressiveModule] = await Promise.all([
16
+ import(threeBase),
17
+ import(`${threeBase}/webgpu`),
18
+ import(`${threeBase}/examples/jsm/loaders/GLTFLoader.js`),
19
+ import(`${threeBase}/examples/jsm/controls/OrbitControls.js`),
20
+ import(`${threeBase}/examples/jsm/environments/RoomEnvironment.js`),
21
+ import(progressiveUrl),
22
+ ]);
23
+
24
+ return {
25
+ THREE,
26
+ THREE_WEBGPU,
27
+ GLTFLoader: gltfLoaderModule.GLTFLoader,
28
+ OrbitControls: orbitControlsModule.OrbitControls,
29
+ RoomEnvironment: roomEnvironmentModule.RoomEnvironment,
30
+ useNeedleProgressive: progressiveModule.useNeedleProgressive,
31
+ LODsManager: progressiveModule.LODsManager,
32
+ NEEDLE_progressive: progressiveModule.NEEDLE_progressive,
33
+ };
34
+ }
@@ -5,18 +5,14 @@
5
5
  <meta charset="utf-8">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1">
7
7
  <title>Threejs Progressive Loading</title>
8
- <style>
9
- body {
10
- margin: 0;
11
- }
12
- </style>
8
+ <link rel="stylesheet" href="../shared/example.css">
13
9
  <script type="importmap">
14
10
  {
15
11
  "imports": {
16
- "three": "https://cdn.jsdelivr.net/npm/three@latest/build/three.module.js",
17
- "three/addons/": "https://cdn.jsdelivr.net/npm/three@latest/examples/jsm/",
18
- "three/examples/": "https://cdn.jsdelivr.net/npm/three@latest/examples/",
19
- "@needle-tools/gltf-progressive": "https://cdn.jsdelivr.net/npm/@needle-tools/gltf-progressive/gltf-progressive.min.js"
12
+ "three": "https://cdn.jsdelivr.net/npm/three@0.184.0/build/three.module.js",
13
+ "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.184.0/examples/jsm/",
14
+ "three/examples/": "https://cdn.jsdelivr.net/npm/three@0.184.0/examples/",
15
+ "@needle-tools/gltf-progressive": "https://cdn.jsdelivr.net/npm/@needle-tools/gltf-progressive@3.6.0-canary.5401de9/gltf-progressive.min.js"
20
16
  }
21
17
  }
22
18
  </script>
@@ -49,4 +45,4 @@
49
45
  });
50
46
  </script>
51
47
 
52
- </html>
48
+ </html>
@@ -1,8 +1,9 @@
1
1
  import * as THREE from 'three';
2
- import { EXRLoader } from 'three/addons/loaders/EXRLoader.js';
3
2
  import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
3
+ import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';
4
4
  import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
5
5
  import { useNeedleProgressive, getRaycastMesh, useRaycastMeshes } from "@needle-tools/gltf-progressive";
6
+ import { fitObjectToView, setupRoomEnvironment } from "../shared/example-utils.js";
6
7
  import { Pane } from 'https://cdn.jsdelivr.net/npm/tweakpane@4.0.3/dist/tweakpane.min.js';
7
8
 
8
9
 
@@ -34,6 +35,8 @@ const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
34
35
  directionalLight.position.set(-50, 20, 50);
35
36
  scene.add(directionalLight);
36
37
 
38
+ setupRoomEnvironment(THREE, RoomEnvironment, renderer, scene);
39
+
37
40
 
38
41
  // Animate the scene
39
42
  function animate() {
@@ -43,19 +46,6 @@ function animate() {
43
46
  }
44
47
  animate();
45
48
 
46
- const environmentTextureUrl = "https://dl.polyhaven.org/file/ph-assets/HDRIs/exr/1k/studio_small_09_1k.exr";
47
- const pmremGenerator = new THREE.PMREMGenerator(renderer);
48
- pmremGenerator.compileEquirectangularShader();
49
- new EXRLoader().load(environmentTextureUrl, texture => {
50
- const envMap = pmremGenerator.fromEquirectangular(texture).texture;
51
- scene.environment = envMap;
52
- texture.dispose();
53
- pmremGenerator.dispose();
54
- });
55
-
56
-
57
-
58
-
59
49
  const modelUrls = [
60
50
  "https://engine.needle.tools/demos/gltf-progressive/assets/putti gruppe/model.glb",
61
51
  "https://engine.needle.tools/demos/gltf-progressive/assets/cyberpunk/model.glb",
@@ -100,15 +90,7 @@ function loadScene() {
100
90
  currentScene?.removeFromParent();
101
91
  currentScene = gltf.scene;
102
92
  scene.add(gltf.scene)
103
- gltf.scene.position.y += .01;
104
-
105
- // the church is huge - scaling it down so we don't have a big difference between the models
106
- if (url.includes("church")) {
107
- gltf.scene.scale.multiplyScalar(.1);
108
- }
109
- else if (url.includes("cyberpunk")) {
110
- gltf.scene.scale.multiplyScalar(15);
111
- }
93
+ fitObjectToView(THREE, camera, gltf.scene, orbit);
112
94
 
113
95
  if (gltf.animations?.length) {
114
96
  console.log("Playing animation", gltf.animations)
@@ -0,0 +1,15 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <link rel="icon" href="data:,">
7
+ <link rel="stylesheet" href="../shared/example.css">
8
+ <title>glTF Progressive WebGPU</title>
9
+ </head>
10
+ <body>
11
+ <canvas id="view"></canvas>
12
+ <div id="status" class="example-status">loading</div>
13
+ <script type="module" src="./main.js"></script>
14
+ </body>
15
+ </html>