@needle-tools/gltf-progressive 1.2.4-beta → 1.2.5-beta

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.
@@ -3,6 +3,7 @@ import { NEEDLE_progressive } from "./extension.js";
3
3
  import { createLoaders } from "./loaders.js";
4
4
  import { getParam, isMobileDevice } from "./utils.internal.js";
5
5
  import { plugins } from "./plugins/plugin.js";
6
+ import { getRaycastMesh } from "./utils.js";
6
7
  const debugProgressiveLoading = getParam("debugprogressive");
7
8
  const suppressProgressiveLoading = getParam("noprogressive");
8
9
  const $lodsManager = Symbol("Needle:LODSManager");
@@ -84,19 +85,34 @@ export class LODsManager {
84
85
  targetTriangleDensity = 200_000;
85
86
  /**
86
87
  * The update interval in frames. If set to 0, the LODs will be updated every frame. If set to 2, the LODs will be updated every second frame, etc.
88
+ * @default "auto"
87
89
  */
88
90
  updateInterval = "auto";
89
91
  #updateInterval = 1;
90
92
  /**
91
93
  * If set to true, the LODsManager will not update the LODs.
94
+ * @default false
92
95
  */
93
96
  pause = false;
97
+ /**
98
+ * When set to true the LODsManager will not update the LODs. This can be used to manually update the LODs using the `update` method.
99
+ * Otherwise the LODs will be updated automatically when the renderer renders the scene.
100
+ * @default false
101
+ */
102
+ manual = false;
94
103
  _lodchangedlisteners = [];
95
104
  addEventListener(evt, listener) {
96
105
  if (evt === "changed") {
97
106
  this._lodchangedlisteners.push(listener);
98
107
  }
99
108
  }
109
+ removeEventListener(evt, listener) {
110
+ if (evt === "changed") {
111
+ const index = this._lodchangedlisteners.indexOf(listener);
112
+ if (index >= 0)
113
+ this._lodchangedlisteners.splice(index, 1);
114
+ }
115
+ }
100
116
  // readonly plugins: NEEDLE_progressive_plugin[] = [];
101
117
  constructor(renderer, context) {
102
118
  this.renderer = renderer;
@@ -126,7 +142,7 @@ export class LODsManager {
126
142
  // if it's rendering to a texture we don't want to update the LODs
127
143
  // This might need to be loosened later - e.g. we might want to update LODs for a render texture - but then we need to store the last LOD level differently and we also might not want to perform all the plugin calls?
128
144
  const renderTarget = self.renderer.getRenderTarget();
129
- if (renderTarget == null) {
145
+ if (renderTarget == null || ("isXRRenderTarget" in renderTarget && renderTarget.isXRRenderTarget)) {
130
146
  stack = 0;
131
147
  self.#frame += 1;
132
148
  self.#delta = self.#clock.getDelta();
@@ -137,11 +153,9 @@ export class LODsManager {
137
153
  if (debugProgressiveLoading && self.#frame % 30 === 0)
138
154
  console.log("FPS", Math.round(self.#fps), "Interval:", self.#updateInterval);
139
155
  }
140
- const frame = self.#frame;
141
156
  const stack_level = stack++;
142
- self.onBeforeRender(scene, camera, stack_level, frame);
143
157
  self.#originalRender.call(this, scene, camera);
144
- self.onAfterRender(scene, camera, stack_level, frame);
158
+ self.onAfterRender(scene, camera, stack_level);
145
159
  };
146
160
  }
147
161
  disable() {
@@ -150,9 +164,10 @@ export class LODsManager {
150
164
  this.renderer.render = this.#originalRender;
151
165
  this.#originalRender = undefined;
152
166
  }
153
- onBeforeRender(_scene, _camera, _stack, _frame) {
167
+ update(scene, camera) {
168
+ this.internalUpdate(scene, camera);
154
169
  }
155
- onAfterRender(scene, camera, _stack, frame) {
170
+ onAfterRender(scene, camera, _stack) {
156
171
  if (this.pause)
157
172
  return;
158
173
  const renderList = this.renderer.renderLists.get(scene, 0);
@@ -201,77 +216,75 @@ export class LODsManager {
201
216
  this.#updateInterval = this.updateInterval;
202
217
  }
203
218
  // Check if we should update LODs this frame
204
- if (this.#updateInterval > 0 && frame % this.#updateInterval != 0) {
219
+ if (this.#updateInterval > 0 && this.#frame % this.#updateInterval != 0) {
205
220
  return;
206
221
  }
207
- this.projectionScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
208
- this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix, this.renderer.coordinateSystem);
209
- const desiredDensity = this.targetTriangleDensity;
210
- // const isLowPerformanceDevice = false;// isMobileDevice();
211
- // Experiment: quick & dirty performance-adaptive LODs
212
- /*
213
- if (this.context.time.smoothedFps < 59) {
214
- currentAllowedDensity *= 0.5;
215
- }
216
- else if (this.context.time.smoothedFps >= 59) {
217
- currentAllowedDensity *= 1.25;
218
- }
219
- */
220
- for (const entry of opaque) {
221
- if (entry.material && (entry.geometry?.type === "BoxGeometry" || entry.geometry?.type === "BufferGeometry")) {
222
- // Ignore the skybox
223
- if (entry.material.name === "SphericalGaussianBlur" || entry.material.name == "BackgroundCubeMaterial" || entry.material.name === "CubemapFromEquirect" || entry.material.name === "EquirectangularToCubeUV") {
224
- if (debugProgressiveLoading) {
225
- if (!entry.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]) {
226
- entry.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"] = true;
227
- console.warn("Ignoring skybox or BLIT object", entry, entry.material.name, entry.material.type);
228
- }
222
+ this.internalUpdate(scene, camera);
223
+ }
224
+ }
225
+ /**
226
+ * Update LODs in a scene
227
+ */
228
+ internalUpdate(scene, camera) {
229
+ const renderList = this.renderer.renderLists.get(scene, 0);
230
+ const opaque = renderList.opaque;
231
+ this.projectionScreenMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
232
+ this.cameraFrustrum.setFromProjectionMatrix(this.projectionScreenMatrix, this.renderer.coordinateSystem);
233
+ const desiredDensity = this.targetTriangleDensity;
234
+ for (const entry of opaque) {
235
+ if (entry.material && (entry.geometry?.type === "BoxGeometry" || entry.geometry?.type === "BufferGeometry")) {
236
+ // Ignore the skybox
237
+ if (entry.material.name === "SphericalGaussianBlur" || entry.material.name == "BackgroundCubeMaterial" || entry.material.name === "CubemapFromEquirect" || entry.material.name === "EquirectangularToCubeUV") {
238
+ if (debugProgressiveLoading) {
239
+ if (!entry.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"]) {
240
+ entry.material["NEEDLE_PROGRESSIVE:IGNORE-WARNING"] = true;
241
+ console.warn("Ignoring skybox or BLIT object", entry, entry.material.name, entry.material.type);
229
242
  }
230
- continue;
231
243
  }
244
+ continue;
232
245
  }
233
- switch (entry.material.type) {
234
- case "LineBasicMaterial":
235
- case "LineDashedMaterial":
236
- case "PointsMaterial":
237
- case "ShadowMaterial":
238
- case "MeshDistanceMaterial":
239
- case "MeshDepthMaterial":
240
- continue;
241
- }
242
- if (debugProgressiveLoading === "color") {
243
- if (entry.material) {
244
- if (!entry.object["progressive_debug_color"]) {
245
- entry.object["progressive_debug_color"] = true;
246
- const randomColor = Math.random() * 0xffffff;
247
- const newMaterial = new MeshStandardMaterial({ color: randomColor });
248
- entry.object.material = newMaterial;
249
- }
246
+ }
247
+ switch (entry.material.type) {
248
+ case "LineBasicMaterial":
249
+ case "LineDashedMaterial":
250
+ case "PointsMaterial":
251
+ case "ShadowMaterial":
252
+ case "MeshDistanceMaterial":
253
+ case "MeshDepthMaterial":
254
+ continue;
255
+ }
256
+ if (debugProgressiveLoading === "color") {
257
+ if (entry.material) {
258
+ if (!entry.object["progressive_debug_color"]) {
259
+ entry.object["progressive_debug_color"] = true;
260
+ const randomColor = Math.random() * 0xffffff;
261
+ const newMaterial = new MeshStandardMaterial({ color: randomColor });
262
+ entry.object.material = newMaterial;
250
263
  }
251
264
  }
252
- const object = entry.object;
253
- if (object instanceof Mesh || (object.isMesh)) {
254
- this.updateLODs(scene, camera, object, desiredDensity, frame);
255
- }
256
265
  }
257
- const transparent = renderList.transparent;
258
- for (const entry of transparent) {
259
- const object = entry.object;
260
- if (object instanceof Mesh || (object.isMesh)) {
261
- this.updateLODs(scene, camera, object, desiredDensity, frame);
262
- }
266
+ const object = entry.object;
267
+ if (object instanceof Mesh || (object.isMesh)) {
268
+ this.updateLODs(scene, camera, object, desiredDensity);
263
269
  }
264
- const transmissive = renderList.transmissive;
265
- for (const entry of transmissive) {
266
- const object = entry.object;
267
- if (object instanceof Mesh || (object.isMesh)) {
268
- this.updateLODs(scene, camera, object, desiredDensity, frame);
269
- }
270
+ }
271
+ const transparent = renderList.transparent;
272
+ for (const entry of transparent) {
273
+ const object = entry.object;
274
+ if (object instanceof Mesh || (object.isMesh)) {
275
+ this.updateLODs(scene, camera, object, desiredDensity);
276
+ }
277
+ }
278
+ const transmissive = renderList.transmissive;
279
+ for (const entry of transmissive) {
280
+ const object = entry.object;
281
+ if (object instanceof Mesh || (object.isMesh)) {
282
+ this.updateLODs(scene, camera, object, desiredDensity);
270
283
  }
271
284
  }
272
285
  }
273
286
  /** Update the LOD levels for the renderer. */
274
- updateLODs(scene, camera, object, desiredDensity, _frame) {
287
+ updateLODs(scene, camera, object, desiredDensity) {
275
288
  if (!object.userData) {
276
289
  object.userData = {};
277
290
  }
@@ -426,8 +439,8 @@ export class LODsManager {
426
439
  }
427
440
  if (!this.cameraFrustrum?.intersectsObject(mesh)) {
428
441
  // the object is not visible by the camera
429
- result.mesh_lod = 99;
430
- result.texture_lod = 99;
442
+ result.mesh_lod = 100;
443
+ result.texture_lod = 100;
431
444
  return;
432
445
  }
433
446
  const canvasHeight = this.renderer.domElement.clientHeight || this.renderer.domElement.height;
@@ -437,6 +450,17 @@ export class LODsManager {
437
450
  if (!skinnedMesh.boundingBox) {
438
451
  skinnedMesh.computeBoundingBox();
439
452
  }
453
+ // Fix: https://linear.app/needle/issue/NE-5264
454
+ else if (state.frames % 30 === 0) {
455
+ // use lowres geometry for bounding box calculation
456
+ const raycastmesh = getRaycastMesh(skinnedMesh);
457
+ const originalGeometry = skinnedMesh.geometry;
458
+ if (raycastmesh) {
459
+ skinnedMesh.geometry = raycastmesh;
460
+ }
461
+ skinnedMesh.computeBoundingBox();
462
+ skinnedMesh.geometry = originalGeometry;
463
+ }
440
464
  boundingBox = skinnedMesh.boundingBox;
441
465
  }
442
466
  if (boundingBox && camera.isPerspectiveCamera) {
package/lib/version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  // replaced at build time
2
- export const version = "1.2.4-beta";
2
+ export const version = "1.2.5-beta";
3
3
  globalThis["GLTF_PROGRESSIVE_VERSION"] = version;
4
4
  console.debug(`[gltf-progressive] version ${version}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/gltf-progressive",
3
- "version": "1.2.4-beta",
3
+ "version": "1.2.5-beta",
4
4
  "description": "three.js support for loading glTF or GLB files that contain progressive loading data",
5
5
  "homepage": "https://needle.tools",
6
6
  "author": {