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

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 (35) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +47 -11
  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.js +15 -2
  28. package/lib/lods.debug.js +1 -1
  29. package/lib/lods.manager.d.ts +3 -0
  30. package/lib/lods.manager.js +62 -18
  31. package/lib/utils.internal.d.ts +27 -0
  32. package/lib/utils.internal.js +68 -25
  33. package/lib/version.js +1 -1
  34. package/lib/worker/loader.mainthread.js +6 -4
  35. package/package.json +7 -2
package/CHANGELOG.md CHANGED
@@ -4,8 +4,12 @@ All notable changes to this package will be documented in this file.
4
4
  The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5
5
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [3.6.0-beta.1] - 2026-06-09
8
+ - Fix: texture LOD selection regressed for meshes that only have texture LODs (no mesh LODs); screen coverage is now computed unconditionally so texture LODs update with camera distance again.
9
+
7
10
  ## [3.6.0-alpha.3] - 2026-05-27
8
11
  - Fix: stale progressive mesh and texture LOD requests no longer overwrite newer explicit targets, while still allowing useful intermediate LODs to apply during rapid target changes.
12
+ - Fix: progressive worker/debug imports no longer assume `window` exists, and texture LOD selection now uses the renderer pixel ratio so main-thread and worker/offscreen renderers can make matching LOD decisions.
9
13
 
10
14
  ## [3.6.0-alpha.2] - 2026-05-26
11
15
  - Add: mesh LOD selection can now be reused by renderers that manage their own batching, keeping progressive instanced meshes on independent LOD levels.
package/README.md CHANGED
@@ -26,6 +26,8 @@ useNeedleProgressive(gltf_loader, webgl_renderer)
26
26
 
27
27
  Examples are in the `/examples` directory. Live versions can be found in the links below.
28
28
 
29
+ To view the examples locally with the current checkout's runtime, run `npm run examples:serve` and open the printed examples index URL.
30
+
29
31
  - [Loading comparisons](https://stackblitz.com/edit/gltf-progressive-comparison?file=package.json,index.html)
30
32
  - [Vanilla three.js](https://engine.needle.tools/demos/gltf-progressive/threejs/) - multiple models and animations
31
33
  - [React Three Fiber](https://engine.needle.tools/demos/gltf-progressive/r3f/)
@@ -34,6 +36,9 @@ Examples are in the `/examples` directory. Live versions can be found in the lin
34
36
  - [multiple \<model-viewer> elements](https://engine.needle.tools/demos/gltf-progressive/modelviewer-multiple)
35
37
  - [Needle Engine](https://stackblitz.com/edit/needle-engine-gltf-progressive?file=src%2Fmain.ts)
36
38
  - [Needle Cloud](https://cloud.needle.tools/view?file=Z23hmXBZN45qJ-ZN45qJ-world)
39
+ - `examples/webgpu` - WebGPU renderer
40
+ - `examples/offscreen` - main-thread OffscreenCanvas rendering
41
+ - `examples/worker-rendering` - OffscreenCanvas rendering in a Worker
37
42
 
38
43
  **Interactive Examples**:
39
44
  - [Stackblitz](https://stackblitz.com/@marwie/collections/gltf-progressive)
@@ -74,10 +79,10 @@ gltfLoader.load(url, gltf => {
74
79
  <script type="importmap">
75
80
  {
76
81
  "imports": {
77
- "three": "https://cdn.jsdelivr.net/npm/three@latest/build/three.module.js",
78
- "three/addons/": "https://cdn.jsdelivr.net/npm/three@latest/examples/jsm/",
79
- "three/examples/": "https://cdn.jsdelivr.net/npm/three@latest/examples/",
80
- "@needle-tools/gltf-progressive": "https://cdn.jsdelivr.net/npm/@needle-tools/gltf-progressive/gltf-progressive.min.js"
82
+ "three": "https://cdn.jsdelivr.net/npm/three@0.184.0/build/three.module.js",
83
+ "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.184.0/examples/jsm/",
84
+ "three/examples/": "https://cdn.jsdelivr.net/npm/three@0.184.0/examples/",
85
+ "@needle-tools/gltf-progressive": "https://cdn.jsdelivr.net/npm/@needle-tools/gltf-progressive@3.6.0-canary.5401de9/gltf-progressive.min.js"
81
86
  }
82
87
  }
83
88
  </script>
@@ -85,7 +90,7 @@ gltfLoader.load(url, gltf => {
85
90
  ```
86
91
 
87
92
 
88
- ## react three fiber
93
+ ## React Three Fiber
89
94
 
90
95
  Full react-three-fiber example at: `examples/react-three-fiber`
91
96
 
@@ -100,7 +105,7 @@ function MyModel() {
100
105
  }
101
106
  ```
102
107
 
103
- ## google \<model-viewer\>
108
+ ## Google \<model-viewer\>
104
109
 
105
110
  Full model-viewer example at: `examples/modelviewer.html`
106
111
 
@@ -110,13 +115,13 @@ Full model-viewer example at: `examples/modelviewer.html`
110
115
  <script type="importmap">
111
116
  {
112
117
  "imports": {
113
- "three": "https://unpkg.com/three/build/three.module.js",
114
- "three/": "https://unpkg.com/three/"
118
+ "three": "https://unpkg.com/three@0.184.0/build/three.module.js",
119
+ "three/": "https://unpkg.com/three@0.184.0/"
115
120
  }
116
121
  }
117
122
  </script>
118
123
  <!-- Include gltf-progressive -->
119
- <script type="module" src="https://cdn.jsdelivr.net/npm/@needle-tools/gltf-progressive/gltf-progressive.min.js"></script>
124
+ <script type="module" src="https://cdn.jsdelivr.net/npm/@needle-tools/gltf-progressive@3.6.0-canary.5401de9/gltf-progressive.min.js"></script>
120
125
  <!-- Include model-viewer -->
121
126
  <script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/3.4.0/model-viewer.min.js"></script>
122
127
  </head>
@@ -129,7 +134,7 @@ Full model-viewer example at: `examples/modelviewer.html`
129
134
 
130
135
  ## Needle Engine
131
136
 
132
- [Needle Engine](https://needle.tools) natively supports progressive loading of these glTF files! See [docs.needle.tools](https://docs.needle.tools) for more information.
137
+ [Needle Engine](https://needle.tools) natively supports progressive loading of these glTF files! See [docs.needle.tools/gltf-progressive](https://engine.needle.tools/docs/gltf-progressive/) for more information.
133
138
 
134
139
 
135
140
  # How can I generate assets for progressive loading
@@ -144,6 +149,36 @@ Create a new class extending `NEEDLE_progressive_plugin` and add your plugin by
144
149
  ### Wait for LODs being loaded
145
150
  Call `lodsManager.awaitLoading(<opts?>)` to receive a promise that will resolve when all object LODs that start loading during the next frame have finished to update. Use the optional options parameter to e.g. wait for more frames.
146
151
 
152
+ ### Worker loading and device pixel ratio
153
+ Progressive mesh and texture LOD files can be loaded in a worker by enabling the `gltf-progressive-worker` URL parameter.
154
+
155
+ ```txt
156
+ https://example.test/viewer?gltf-progressive-worker
157
+ ```
158
+
159
+ LOD selection uses the renderer pixel ratio. For matching results across main-thread, worker, or offscreen rendering, configure the renderer with the same device pixel ratio before calling `useNeedleProgressive` or `LODsManager.get`.
160
+
161
+ ```ts
162
+ const renderer = new WebGLRenderer({ canvas });
163
+ renderer.setPixelRatio(devicePixelRatio);
164
+
165
+ useNeedleProgressive(gltfLoader, renderer);
166
+ ```
167
+
168
+ In a worker or offscreen renderer, pass the display DPR from the main thread and apply it to the renderer there as well.
169
+
170
+ ```ts
171
+ // main thread
172
+ worker.postMessage({ type: "init", devicePixelRatio: window.devicePixelRatio });
173
+
174
+ // worker / offscreen renderer
175
+ const renderer = new WebGLRenderer({ canvas: offscreenCanvas });
176
+ renderer.setPixelRatio(message.devicePixelRatio);
177
+ useNeedleProgressive(gltfLoader, renderer);
178
+ ```
179
+
180
+ If the renderer does not expose a pixel ratio, gltf-progressive falls back to `globalThis.devicePixelRatio` and then `1`.
181
+
147
182
  ### Global LOD level override
148
183
 
149
184
  ### LOD Manager settings
@@ -174,10 +209,11 @@ Call `getRaycastMesh(<your_mesh_object>)`
174
209
  # Extension
175
210
  Read more about the [NEEDLE_progressive extension](./NEEDLE_progressive/README.md)
176
211
 
212
+ # Documentation
213
+ Read more about Needle Progressive GLTF loading in the [Needle Engine documentation](https://docs.needle.tools/engine/gltf-progressive/)
177
214
 
178
215
  # Contact ✒️
179
216
  <b>[🌵 needle — tools for creators](https://needle.tools)</b> •
180
217
  [Twitter](https://twitter.com/NeedleTools) •
181
218
  [Discord](https://discord.needle.tools) •
182
219
  [Forum](https://forum.needle.tools)
183
-
@@ -13,13 +13,13 @@
13
13
  <script type="importmap">
14
14
  {
15
15
  "imports": {
16
- "three": "https://unpkg.com/three/build/three.module.js",
17
- "three/": "https://unpkg.com/three/"
16
+ "three": "https://unpkg.com/three@0.184.0/build/three.module.js",
17
+ "three/": "https://unpkg.com/three@0.184.0/"
18
18
  }
19
19
  }
20
20
  </script>
21
21
  <script type="module"
22
- src="https://cdn.jsdelivr.net/npm/@needle-tools/gltf-progressive/gltf-progressive.min.js"></script>
22
+ src="https://cdn.jsdelivr.net/npm/@needle-tools/gltf-progressive@3.6.0-canary.5401de9/gltf-progressive.min.js"></script>
23
23
  <script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/3.4.0/model-viewer.min.js"></script>
24
24
  </head>
25
25
 
@@ -123,4 +123,4 @@
123
123
  }
124
124
  </style>
125
125
 
126
- </html>
126
+ </html>
@@ -7,14 +7,14 @@
7
7
  <script type="importmap">
8
8
  {
9
9
  "imports": {
10
- "three": "https://unpkg.com/three/build/three.module.js",
11
- "three/": "https://unpkg.com/three/"
10
+ "three": "https://unpkg.com/three@0.184.0/build/three.module.js",
11
+ "three/": "https://unpkg.com/three@0.184.0/"
12
12
  }
13
13
  }
14
14
  </script>
15
15
  <script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/3.4.0/model-viewer.min.js"></script>
16
16
  <script type="module"
17
- src="https://cdn.jsdelivr.net/npm/@needle-tools/gltf-progressive/gltf-progressive.min.js"></script>
17
+ src="https://cdn.jsdelivr.net/npm/@needle-tools/gltf-progressive@3.6.0-canary.5401de9/gltf-progressive.min.js"></script>
18
18
  </head>
19
19
 
20
20
  <model-viewer src="https://engine.needle.tools/demos/gltf-progressive/assets/church/model.glb" ar shadow-intensity="1"
@@ -31,4 +31,4 @@
31
31
  }
32
32
  </style>
33
33
 
34
- </html>
34
+ </html>
@@ -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 Offscreen Rendering</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>
@@ -0,0 +1,98 @@
1
+ import { loadRuntime } from "../shared/runtime.js";
2
+ import {
3
+ addLoadedScene,
4
+ createOrbitControls,
5
+ createExampleState,
6
+ createSceneChangeButton,
7
+ getInitialSceneIndex,
8
+ getModelUrl,
9
+ markError,
10
+ markReady,
11
+ markSceneLoaded,
12
+ markSceneLoading,
13
+ normalizeSceneIndex,
14
+ resizeRenderer,
15
+ setupScene,
16
+ setupRoomEnvironment,
17
+ trackLODChanges,
18
+ } from "../shared/example-utils.js";
19
+
20
+ const state = createExampleState("offscreen");
21
+ const params = new URLSearchParams(location.search);
22
+
23
+ try {
24
+ if (!globalThis.OffscreenCanvas) throw new Error("OffscreenCanvas is not available in this browser.");
25
+
26
+ const runtime = await loadRuntime();
27
+ const THREE = runtime.THREE;
28
+ const visibleCanvas = document.getElementById("view");
29
+ const visibleContext = visibleCanvas.getContext("bitmaprenderer");
30
+ if (!visibleContext) throw new Error("bitmaprenderer is not available in this browser.");
31
+
32
+ let width = window.innerWidth;
33
+ let height = window.innerHeight;
34
+ let pixelRatio = window.devicePixelRatio || 1;
35
+ const offscreenCanvas = new OffscreenCanvas(Math.max(1, width * pixelRatio), Math.max(1, height * pixelRatio));
36
+ const renderer = new THREE.WebGLRenderer({ canvas: offscreenCanvas, antialias: true });
37
+ const { scene, camera } = setupScene(THREE, width, height);
38
+ setupRoomEnvironment(THREE, runtime.RoomEnvironment, renderer, scene);
39
+ const controls = createOrbitControls(runtime.OrbitControls, camera, visibleCanvas);
40
+ let root = null;
41
+ let sceneIndex = getInitialSceneIndex(params);
42
+ let loadToken = 0;
43
+
44
+ function resize() {
45
+ width = window.innerWidth;
46
+ height = window.innerHeight;
47
+ pixelRatio = window.devicePixelRatio || 1;
48
+ visibleCanvas.width = Math.max(1, Math.floor(width * pixelRatio));
49
+ visibleCanvas.height = Math.max(1, Math.floor(height * pixelRatio));
50
+ resizeRenderer(renderer, camera, width, height, pixelRatio);
51
+ }
52
+ resize();
53
+
54
+ const firstLoader = new runtime.GLTFLoader();
55
+ const lodsManager = runtime.useNeedleProgressive(firstLoader, renderer);
56
+ trackLODChanges(lodsManager, state);
57
+
58
+ async function loadScene(nextIndex) {
59
+ const token = ++loadToken;
60
+ sceneIndex = normalizeSceneIndex(nextIndex);
61
+ const url = getModelUrl(params, sceneIndex);
62
+ markSceneLoading(state, sceneIndex, url);
63
+
64
+ const loader = token === 1 ? firstLoader : new runtime.GLTFLoader();
65
+ if (token !== 1) runtime.useNeedleProgressive(loader, renderer);
66
+
67
+ const nextRoot = await new Promise((resolve, reject) => {
68
+ loader.load(url, gltf => resolve(addLoadedScene(THREE, scene, gltf, { camera, controls })), undefined, reject);
69
+ });
70
+
71
+ if (token !== loadToken) {
72
+ nextRoot.removeFromParent();
73
+ return;
74
+ }
75
+
76
+ root?.removeFromParent();
77
+ root = nextRoot;
78
+ markSceneLoaded(state, runtime, root);
79
+ markReady(state, "offscreen-webgl");
80
+ }
81
+
82
+ window.addEventListener("resize", resize);
83
+ createSceneChangeButton(() => loadScene(sceneIndex + 1).catch(error => markError(state, error)));
84
+
85
+ function render() {
86
+ state.frames += 1;
87
+ controls.update();
88
+ renderer.render(scene, camera);
89
+ visibleContext.transferFromImageBitmap(offscreenCanvas.transferToImageBitmap());
90
+ requestAnimationFrame(render);
91
+ }
92
+
93
+ await loadScene(sceneIndex);
94
+ render();
95
+ }
96
+ catch (error) {
97
+ markError(state, error);
98
+ }
@@ -14,11 +14,11 @@
14
14
  <meta property="og:image" content="" />
15
15
 
16
16
  <meta name="robots" content="index,follow">
17
- <link rel="stylesheet" href="./src/style.css">
17
+ <link rel="stylesheet" href="./src/styles.css">
18
18
  </head>
19
19
 
20
20
  <body>
21
21
  <script type="module" src="./src/index.tsx"></script>
22
22
  <div id="root" style="width:100vw; height:100vh;"></div>
23
23
  </body>
24
- </html>
24
+ </html>