3dtiles-inspector 0.2.6 → 0.2.8

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.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,24 @@ The format is based on Keep a Changelog and this project follows Semantic Versio
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.2.8] - 2026-05-13
10
+
11
+ ### Fixed
12
+
13
+ - Fixed save-time crop memory spikes on large tilesets by limiting concurrent Gaussian Splat resource processing based on CPU parallelism, capped at 8 workers.
14
+ - Fixed viewer stalls when many tiles are loaded by avoiding repeated tileset-wide leaf geometric-error scans during tile preprocessing.
15
+
16
+ ## [0.2.7] - 2026-05-10
17
+
18
+ ### Changed
19
+
20
+ - Changed crop saves to remove local orphaned Gaussian Splat `.glb` / `.gltf` resources and private external buffers after fully cropped content is pruned from the tileset.
21
+ - Updated `3d-tiles-rendererjs-3dgs-plugin` from `0.1.5` to `0.1.7`.
22
+
23
+ ### Fixed
24
+
25
+ - Fixed cropped Gaussian Splat resources keeping stale glTF accessor counts after SPZ data is rewritten, which could make Cesium fail while generating splat textures.
26
+
9
27
  ## [0.2.6] - 2026-05-05
10
28
 
11
29
  ### Added
@@ -43855,6 +43855,12 @@ async function buildGaussianMeshSource(descriptor, abortSignal) {
43855
43855
  }
43856
43856
  return { extSplats };
43857
43857
  }
43858
+ function isXrPresenting(renderer) {
43859
+ return renderer.xr.isPresenting;
43860
+ }
43861
+ function getUpdateSourceCamera(renderer, camera, xrPresenting = isXrPresenting(renderer)) {
43862
+ return xrPresenting ? renderer.xr.getCamera() : camera;
43863
+ }
43858
43864
  function ensureCameraClone(cached, source) {
43859
43865
  if (!cached || cached.constructor !== source.constructor) {
43860
43866
  return source.clone();
@@ -43862,111 +43868,11 @@ function ensureCameraClone(cached, source) {
43862
43868
  cached.copy(source, false);
43863
43869
  return cached;
43864
43870
  }
43865
- function hasGaussianSplatAncestor(node) {
43866
- let ancestor = node.parent;
43867
- while (ancestor) {
43868
- if (ancestor instanceof SplatMesh || isGaussianSplat(ancestor)) {
43869
- return true;
43870
- }
43871
- ancestor = ancestor.parent;
43872
- }
43873
- return false;
43874
- }
43875
- function isGlobalSplatEdit(node) {
43876
- return node instanceof SplatEdit && !hasGaussianSplatAncestor(node);
43877
- }
43878
- function isCameraRelativeNode(node) {
43879
- return isGaussianSplat(node) || isGlobalSplatEdit(node);
43880
- }
43881
- function hasCameraRelativeRootAncestor(node) {
43882
- let ancestor = node.parent;
43883
- while (ancestor) {
43884
- if (isCameraRelativeNode(ancestor)) {
43885
- return true;
43886
- }
43887
- ancestor = ancestor.parent;
43888
- }
43889
- return false;
43890
- }
43891
- function cloneSplatRootSnapshot(node) {
43892
- return {
43893
- kind: "splat",
43894
- opacity: node.opacity,
43895
- matrixWorld: node.matrixWorld.clone()
43896
- };
43897
- }
43898
- function cloneSplatEditSdfSnapshot(sdf) {
43899
- return {
43900
- uuid: sdf.uuid,
43901
- matrixWorld: sdf.matrixWorld.clone(),
43902
- type: sdf.type,
43903
- invert: sdf.invert,
43904
- opacity: sdf.opacity,
43905
- color: sdf.color.clone(),
43906
- radius: sdf.radius,
43907
- displace: sdf.displace.clone(),
43908
- scale: sdf.scale.clone()
43909
- };
43910
- }
43911
- function cloneSplatEditRootSnapshot(edit) {
43912
- edit.updateMatrixWorld(true);
43913
- const sdfs = [];
43914
- const sourceSdfs = edit.sdfs;
43915
- if (sourceSdfs != null) {
43916
- for (const sdf of sourceSdfs) {
43917
- sdf.updateMatrixWorld(true);
43918
- sdfs.push(cloneSplatEditSdfSnapshot(sdf));
43919
- }
43920
- } else {
43921
- edit.traverseVisible((child) => {
43922
- if (child instanceof SplatEditSdf) {
43923
- child.updateMatrixWorld(true);
43924
- sdfs.push(cloneSplatEditSdfSnapshot(child));
43925
- }
43926
- });
43927
- }
43928
- return {
43929
- kind: "edit",
43930
- matrixWorld: edit.matrixWorld.clone(),
43931
- ordering: edit.ordering,
43932
- rgbaBlendMode: edit.rgbaBlendMode,
43933
- sdfSmooth: edit.sdfSmooth,
43934
- softEdge: edit.softEdge,
43935
- invert: edit.invert,
43936
- sdfs
43937
- };
43938
- }
43939
- function cloneCameraRelativeRootSnapshot(node) {
43940
- return node instanceof SplatEdit ? cloneSplatEditRootSnapshot(node) : cloneSplatRootSnapshot(node);
43941
- }
43942
- function areSplatRootStatesEqual(a2, b5) {
43943
- return a2.opacity === b5.opacity && a2.matrixWorld.equals(b5.matrixWorld);
43944
- }
43945
- function areSplatEditSdfStatesEqual(a2, b5) {
43946
- return a2.uuid === b5.uuid && a2.type === b5.type && a2.invert === b5.invert && a2.opacity === b5.opacity && a2.radius === b5.radius && a2.matrixWorld.equals(b5.matrixWorld) && a2.color.equals(b5.color) && a2.displace.equals(b5.displace) && a2.scale.equals(b5.scale);
43871
+ function isGaussianSplatNode(node) {
43872
+ return node instanceof SplatMesh || isGaussianSplat(node);
43947
43873
  }
43948
- function areSplatEditRootStatesEqual(a2, b5) {
43949
- if (!a2.matrixWorld.equals(b5.matrixWorld) || a2.ordering !== b5.ordering || a2.rgbaBlendMode !== b5.rgbaBlendMode || a2.sdfSmooth !== b5.sdfSmooth || a2.softEdge !== b5.softEdge || a2.invert !== b5.invert || a2.sdfs.length !== b5.sdfs.length) {
43950
- return false;
43951
- }
43952
- for (let i = 0; i < a2.sdfs.length; i++) {
43953
- if (!areSplatEditSdfStatesEqual(a2.sdfs[i], b5.sdfs[i])) {
43954
- return false;
43955
- }
43956
- }
43957
- return true;
43958
- }
43959
- function areCameraRelativeRootStatesEqual(a2, b5) {
43960
- if (a2 === b5) {
43961
- return true;
43962
- }
43963
- if (!a2 || !b5 || a2.kind !== b5.kind) {
43964
- return false;
43965
- }
43966
- if (a2.kind === "splat") {
43967
- return b5.kind === "splat" && areSplatRootStatesEqual(a2, b5);
43968
- }
43969
- return b5.kind === "edit" && areSplatEditRootStatesEqual(a2, b5);
43874
+ function isCameraRelativeEdit(node, hasGaussianSplatAncestor) {
43875
+ return node instanceof SplatEdit && !hasGaussianSplatAncestor;
43970
43876
  }
43971
43877
  function normalizeSparkRendererOptions(host, includeCustomDefaults = true) {
43972
43878
  const source = host.sparkRendererOptions ?? {};
@@ -44154,7 +44060,7 @@ function isGaussianSplat(object) {
44154
44060
  function isGaussianSplatScene(scene) {
44155
44061
  return Boolean(scene?.userData?.gaussianSplatScene);
44156
44062
  }
44157
- var _translation, _rotation, _scale, _identityMatrix2, _tempNodeMatrix, _textDecoder, _identityMatrix22, _cameraInverseWorldMatrix, _parentInverseWorldMatrix, _rebasedLocalMatrix, _displayFrameInverseWorldMatrix, _relativeRenderCameraMatrix, _cameraWorldPosition, _cameraWorldDirection, _cameraPositionEpsilonSq, _cameraDirectionDotThreshold, _updateCamera, _renderCamera, _lastCameraPosition, _lastCameraDirection, _hasLastCameraPose, _lastRootStates, _currentRootStates, _rebasedRootsPool, _rebasedRootsCount, _hadRebasedLastFrame, _CameraRelativeSparkRenderer_instances, shouldUpdate_fn, getUpdateCamera_fn, getRenderCamera_fn, rebaseCameraRelativeRoots_fn, restoreCameraRelativeRoots_fn, _a2, CameraRelativeSparkRenderer, _sharedSparkManagersByScene, _sharedSparkManagersByRenderer, CUSTOM_DEFAULT_OPTIONS, _sparkRenderer, _scene, _sparkRendererOptions, _notifyHandle, _disposeHandle, _tilesRenderers, _disposed, _SharedSparkRendererManager_instances, dispose_fn, stopScheduledNotifications_fn, scheduleSortUpdatedNotification_fn, waitForSortAndDispose_fn, _a3, SharedSparkRendererManager, SPARK_RENDERER_OPTION_KEYS, MAX_GAUSSIAN_MESH_INIT_CONCURRENCY, _sceneMatrix, _gaussianFadeValueWatched, _host, _sparkManager, _GaussianSplatPlugin_instances, disposeSplatScene_fn, getSplatMeshes_fn, createMeshForDescriptor_fn, _a4, GaussianSplatPlugin;
44063
+ var _translation, _rotation, _scale, _identityMatrix2, _tempNodeMatrix, _textDecoder, _identityMatrix22, _cameraInverseWorldMatrix, _parentInverseWorldMatrix, _rebasedLocalMatrix, _displayFrameInverseWorldMatrix, _relativeRenderCameraMatrix, _updateCamera, _renderCamera, _cameraWorldSnapshot, _lastXrHandledFrame, _rebasedRootsPool, _rebasedRootsCount, _hadRebasedLastFrame, _CameraRelativeSparkRenderer_instances, updateSparkIfNeeded_fn, getUpdateCamera_fn, getRenderCamera_fn, rebaseCameraRelativeRoots_fn, visitVisibleCameraRelativeRoots_fn, rebaseCameraRelativeRoot_fn, restoreCameraRelativeRoots_fn, _a2, CameraRelativeSparkRenderer, _sharedSparkManagersByScene, _sharedSparkManagersByRenderer, CUSTOM_DEFAULT_OPTIONS, _sparkRenderer, _scene, _sparkRendererOptions, _notifyHandle, _disposeHandle, _tilesRenderers, _disposed, _SharedSparkRendererManager_instances, dispose_fn, stopScheduledNotifications_fn, scheduleSortUpdatedNotification_fn, waitForSortAndDispose_fn, _a3, SharedSparkRendererManager, SPARK_RENDERER_OPTION_KEYS, MAX_GAUSSIAN_MESH_INIT_CONCURRENCY, _sceneMatrix, _gaussianFadeValueWatched, _host, _sparkManager, _GaussianSplatPlugin_instances, disposeSplatScene_fn, getSplatMeshes_fn, createMeshForDescriptor_fn, _a4, GaussianSplatPlugin;
44158
44064
  var init_dist = __esm({
44159
44065
  "node_modules/3d-tiles-rendererjs-3dgs-plugin/dist/index.js"() {
44160
44066
  init_spark_module();
@@ -44175,10 +44081,6 @@ var init_dist = __esm({
44175
44081
  _rebasedLocalMatrix = new Matrix4();
44176
44082
  _displayFrameInverseWorldMatrix = new Matrix4();
44177
44083
  _relativeRenderCameraMatrix = new Matrix4();
44178
- _cameraWorldPosition = new Vector3();
44179
- _cameraWorldDirection = new Vector3();
44180
- _cameraPositionEpsilonSq = 1e-6;
44181
- _cameraDirectionDotThreshold = 1 - 1e-3;
44182
44084
  CameraRelativeSparkRenderer = (_a2 = class extends SparkRenderer {
44183
44085
  constructor(renderer, options = {}) {
44184
44086
  super({
@@ -44190,11 +44092,8 @@ var init_dist = __esm({
44190
44092
  __privateAdd(this, _CameraRelativeSparkRenderer_instances);
44191
44093
  __privateAdd(this, _updateCamera, null);
44192
44094
  __privateAdd(this, _renderCamera, null);
44193
- __privateAdd(this, _lastCameraPosition, new Vector3());
44194
- __privateAdd(this, _lastCameraDirection, new Vector3());
44195
- __privateAdd(this, _hasLastCameraPose, false);
44196
- __privateAdd(this, _lastRootStates, /* @__PURE__ */ new Map());
44197
- __privateAdd(this, _currentRootStates, /* @__PURE__ */ new Map());
44095
+ __privateAdd(this, _cameraWorldSnapshot, new Matrix4());
44096
+ __privateAdd(this, _lastXrHandledFrame, -1);
44198
44097
  __privateAdd(this, _rebasedRootsPool, []);
44199
44098
  __privateAdd(this, _rebasedRootsCount, 0);
44200
44099
  __privateAdd(this, _hadRebasedLastFrame, false);
@@ -44203,56 +44102,64 @@ var init_dist = __esm({
44203
44102
  };
44204
44103
  }
44205
44104
  onBeforeRender(renderer, scene, camera) {
44206
- camera.updateMatrixWorld(true);
44207
- const rebasedCount = __privateMethod(this, _CameraRelativeSparkRenderer_instances, rebaseCameraRelativeRoots_fn).call(this, scene, camera);
44105
+ const xrPresenting = isXrPresenting(renderer);
44106
+ const updateSourceCamera = getUpdateSourceCamera(
44107
+ renderer,
44108
+ camera,
44109
+ xrPresenting
44110
+ );
44111
+ if (!xrPresenting) {
44112
+ camera.updateMatrixWorld(true);
44113
+ }
44114
+ const rebasedCount = __privateMethod(this, _CameraRelativeSparkRenderer_instances, rebaseCameraRelativeRoots_fn).call(this, scene, updateSourceCamera);
44208
44115
  const hasRebased = rebasedCount > 0;
44116
+ const renderFrame = renderer.info.render.frame;
44117
+ const shouldHandleFrameState = !xrPresenting || __privateGet(this, _lastXrHandledFrame) !== renderFrame;
44209
44118
  try {
44210
- if ((hasRebased || __privateGet(this, _hadRebasedLastFrame)) && __privateMethod(this, _CameraRelativeSparkRenderer_instances, shouldUpdate_fn).call(this, camera)) {
44211
- const updateCamera = __privateMethod(this, _CameraRelativeSparkRenderer_instances, getUpdateCamera_fn).call(this, camera);
44119
+ if (shouldHandleFrameState) {
44120
+ __privateSet(this, _lastXrHandledFrame, renderFrame);
44121
+ }
44122
+ if ((hasRebased || __privateGet(this, _hadRebasedLastFrame)) && shouldHandleFrameState) {
44123
+ const updateCamera = __privateMethod(this, _CameraRelativeSparkRenderer_instances, getUpdateCamera_fn).call(this, updateSourceCamera);
44212
44124
  const prevDisplay = this.display;
44213
44125
  const prevCurrent = this.current;
44214
- const cameraWorldSnapshot = camera.matrixWorld.clone();
44215
- void this.update({
44126
+ const cameraWorldSnapshot = __privateGet(this, _cameraWorldSnapshot).copy(
44127
+ updateSourceCamera.matrixWorld
44128
+ );
44129
+ void __privateMethod(this, _CameraRelativeSparkRenderer_instances, updateSparkIfNeeded_fn).call(this, {
44216
44130
  scene,
44217
44131
  camera: updateCamera
44132
+ }).catch((error) => {
44133
+ console.error(
44134
+ "CameraRelativeSparkRenderer: Spark update failed",
44135
+ error
44136
+ );
44218
44137
  });
44219
- const updateAccepted = this.current !== prevCurrent || this.display !== prevDisplay;
44220
44138
  if (this.current !== prevCurrent) {
44221
44139
  this.current.viewToWorld.copy(cameraWorldSnapshot);
44222
44140
  }
44223
44141
  if (this.display !== prevDisplay) {
44224
44142
  this.display.viewToWorld.copy(cameraWorldSnapshot);
44225
44143
  }
44226
- if (updateAccepted) {
44227
- __privateGet(this, _lastCameraPosition).copy(_cameraWorldPosition);
44228
- __privateGet(this, _lastCameraDirection).copy(_cameraWorldDirection);
44229
- __privateSet(this, _hasLastCameraPose, true);
44230
- __privateSet(this, _lastRootStates, new Map(__privateGet(this, _currentRootStates)));
44231
- }
44232
44144
  }
44233
- __privateSet(this, _hadRebasedLastFrame, hasRebased);
44145
+ if (shouldHandleFrameState) {
44146
+ __privateSet(this, _hadRebasedLastFrame, hasRebased);
44147
+ }
44234
44148
  const renderCamera = hasRebased ? __privateMethod(this, _CameraRelativeSparkRenderer_instances, getRenderCamera_fn).call(this, camera) : camera;
44235
44149
  super.onBeforeRender(renderer, scene, renderCamera);
44236
44150
  } finally {
44237
44151
  __privateMethod(this, _CameraRelativeSparkRenderer_instances, restoreCameraRelativeRoots_fn).call(this);
44238
44152
  }
44239
44153
  }
44240
- }, _updateCamera = new WeakMap(), _renderCamera = new WeakMap(), _lastCameraPosition = new WeakMap(), _lastCameraDirection = new WeakMap(), _hasLastCameraPose = new WeakMap(), _lastRootStates = new WeakMap(), _currentRootStates = new WeakMap(), _rebasedRootsPool = new WeakMap(), _rebasedRootsCount = new WeakMap(), _hadRebasedLastFrame = new WeakMap(), _CameraRelativeSparkRenderer_instances = new WeakSet(), shouldUpdate_fn = function(camera) {
44241
- camera.getWorldPosition(_cameraWorldPosition);
44242
- camera.getWorldDirection(_cameraWorldDirection);
44243
- const poseChanged = !__privateGet(this, _hasLastCameraPose) || _cameraWorldPosition.distanceToSquared(__privateGet(this, _lastCameraPosition)) > _cameraPositionEpsilonSq || _cameraWorldDirection.dot(__privateGet(this, _lastCameraDirection)) < _cameraDirectionDotThreshold;
44244
- const current = __privateGet(this, _currentRootStates);
44245
- const last = __privateGet(this, _lastRootStates);
44246
- let rootsChanged = current.size !== last.size;
44247
- if (!rootsChanged) {
44248
- for (const [uuid, state] of current) {
44249
- if (!areCameraRelativeRootStatesEqual(state, last.get(uuid))) {
44250
- rootsChanged = true;
44251
- break;
44252
- }
44253
- }
44254
- }
44255
- return poseChanged || rootsChanged;
44154
+ }, _updateCamera = new WeakMap(), _renderCamera = new WeakMap(), _cameraWorldSnapshot = new WeakMap(), _lastXrHandledFrame = new WeakMap(), _rebasedRootsPool = new WeakMap(), _rebasedRootsCount = new WeakMap(), _hadRebasedLastFrame = new WeakMap(), _CameraRelativeSparkRenderer_instances = new WeakSet(), updateSparkIfNeeded_fn = function({
44155
+ scene,
44156
+ camera
44157
+ }) {
44158
+ return this.updateInternal({
44159
+ scene,
44160
+ camera,
44161
+ autoUpdate: true
44162
+ });
44256
44163
  }, /**
44257
44164
  * Identity camera for the update pass - makes Spark treat
44258
44165
  * the camera's own frame as the reference frame.
@@ -44291,55 +44198,63 @@ var init_dist = __esm({
44291
44198
  return renderCamera;
44292
44199
  }, rebaseCameraRelativeRoots_fn = function(scene, camera) {
44293
44200
  __privateSet(this, _rebasedRootsCount, 0);
44294
- __privateGet(this, _currentRootStates).clear();
44295
44201
  _cameraInverseWorldMatrix.copy(camera.matrixWorld).invert();
44296
- scene.traverseVisible((node) => {
44297
- if (!isCameraRelativeNode(node)) {
44298
- return;
44299
- }
44300
- __privateGet(this, _currentRootStates).set(
44301
- node.uuid,
44302
- cloneCameraRelativeRootSnapshot(node)
44303
- );
44304
- if (hasCameraRelativeRootAncestor(node)) {
44305
- return;
44306
- }
44307
- const idx = __privateWrapper(this, _rebasedRootsCount)._++;
44308
- const pool = __privateGet(this, _rebasedRootsPool);
44309
- if (idx >= pool.length) {
44310
- pool.push({
44311
- target: node,
44312
- originalMatrix: node.matrix.clone(),
44313
- originalMatrixAutoUpdate: node.matrixAutoUpdate
44314
- });
44315
- } else {
44316
- const entry = pool[idx];
44317
- entry.target = node;
44318
- entry.originalMatrix.copy(node.matrix);
44319
- entry.originalMatrixAutoUpdate = node.matrixAutoUpdate;
44320
- }
44321
- const parent2 = node.parent;
44322
- if (!parent2 || parent2 === scene) {
44323
- _rebasedLocalMatrix.copy(_cameraInverseWorldMatrix).multiply(node.matrixWorld);
44324
- } else {
44325
- _rebasedLocalMatrix.copy(_parentInverseWorldMatrix.copy(parent2.matrixWorld).invert()).multiply(_cameraInverseWorldMatrix).multiply(node.matrixWorld);
44326
- }
44327
- node.matrixAutoUpdate = false;
44328
- node.matrix.copy(_rebasedLocalMatrix);
44329
- node.matrixWorldNeedsUpdate = true;
44330
- node.updateMatrixWorld(true);
44331
- });
44202
+ __privateMethod(this, _CameraRelativeSparkRenderer_instances, visitVisibleCameraRelativeRoots_fn).call(this, scene, scene, false, false);
44332
44203
  return __privateGet(this, _rebasedRootsCount);
44204
+ }, visitVisibleCameraRelativeRoots_fn = function(node, scene, hasGaussianSplatAncestor, hasCameraRelativeAncestor) {
44205
+ if (!node.visible) {
44206
+ return;
44207
+ }
44208
+ const isSplatNode = isGaussianSplatNode(node);
44209
+ const isCameraRelativeNode = isSplatNode || isCameraRelativeEdit(node, hasGaussianSplatAncestor);
44210
+ if (isCameraRelativeNode && !hasCameraRelativeAncestor) {
44211
+ __privateMethod(this, _CameraRelativeSparkRenderer_instances, rebaseCameraRelativeRoot_fn).call(this, node);
44212
+ }
44213
+ const nextHasGaussianSplatAncestor = hasGaussianSplatAncestor || isSplatNode;
44214
+ const nextHasCameraRelativeAncestor = hasCameraRelativeAncestor || isCameraRelativeNode;
44215
+ const { children } = node;
44216
+ for (let i = 0, l = children.length; i < l; i++) {
44217
+ __privateMethod(this, _CameraRelativeSparkRenderer_instances, visitVisibleCameraRelativeRoots_fn).call(this, children[i], scene, nextHasGaussianSplatAncestor, nextHasCameraRelativeAncestor);
44218
+ }
44219
+ }, rebaseCameraRelativeRoot_fn = function(node) {
44220
+ const idx = __privateWrapper(this, _rebasedRootsCount)._++;
44221
+ const pool = __privateGet(this, _rebasedRootsPool);
44222
+ if (idx >= pool.length) {
44223
+ pool.push({
44224
+ target: node,
44225
+ originalMatrix: node.matrix.clone(),
44226
+ originalMatrixAutoUpdate: node.matrixAutoUpdate
44227
+ });
44228
+ } else {
44229
+ const entry = pool[idx];
44230
+ entry.target = node;
44231
+ entry.originalMatrix.copy(node.matrix);
44232
+ entry.originalMatrixAutoUpdate = node.matrixAutoUpdate;
44233
+ }
44234
+ const parent2 = node.parent;
44235
+ if (!parent2) {
44236
+ _rebasedLocalMatrix.copy(_cameraInverseWorldMatrix).multiply(node.matrixWorld);
44237
+ } else {
44238
+ _rebasedLocalMatrix.copy(_parentInverseWorldMatrix.copy(parent2.matrixWorld).invert()).multiply(_cameraInverseWorldMatrix).multiply(node.matrixWorld);
44239
+ }
44240
+ node.matrixAutoUpdate = false;
44241
+ node.matrix.copy(_rebasedLocalMatrix);
44242
+ node.matrixWorldNeedsUpdate = true;
44243
+ node.updateMatrixWorld(true);
44333
44244
  }, restoreCameraRelativeRoots_fn = function() {
44334
44245
  const pool = __privateGet(this, _rebasedRootsPool);
44335
44246
  for (let i = __privateGet(this, _rebasedRootsCount) - 1; i >= 0; i--) {
44336
44247
  const { target, originalMatrix, originalMatrixAutoUpdate } = pool[i];
44248
+ if (!target) continue;
44337
44249
  target.matrix.copy(originalMatrix);
44338
44250
  target.matrixAutoUpdate = originalMatrixAutoUpdate;
44339
44251
  target.matrixWorldNeedsUpdate = true;
44340
44252
  }
44341
44253
  for (let i = 0; i < __privateGet(this, _rebasedRootsCount); i++) {
44342
- pool[i].target.updateMatrixWorld(true);
44254
+ pool[i].target?.updateMatrixWorld(true);
44255
+ }
44256
+ for (let i = __privateGet(this, _rebasedRootsCount); i < pool.length; i++) {
44257
+ pool[i].target = null;
44343
44258
  }
44344
44259
  }, _a2);
44345
44260
  _sharedSparkManagersByScene = /* @__PURE__ */ new WeakMap();
@@ -45373,6 +45288,9 @@ function createGeometricErrorController({
45373
45288
  getTiles
45374
45289
  }) {
45375
45290
  const originalTileGeometricErrors = /* @__PURE__ */ new WeakMap();
45291
+ let knownTileChildCounts = /* @__PURE__ */ new WeakMap();
45292
+ let cachedGlobalLeafGeometricError = null;
45293
+ let cachedGlobalLeafGeometricErrorRoot = null;
45376
45294
  let geometricErrorScaleExponent = 0;
45377
45295
  let geometricErrorScale = 1;
45378
45296
  let lastSavedGeometricErrorScale = 1;
@@ -45385,6 +45303,39 @@ function createGeometricErrorController({
45385
45303
  function getEffectiveGeometricErrorLayerScale() {
45386
45304
  return lastSavedGeometricErrorLayerScale * geometricErrorLayerScale;
45387
45305
  }
45306
+ function getTilesRoot() {
45307
+ return getTiles()?.root || null;
45308
+ }
45309
+ function getCachedGlobalLeafGeometricError() {
45310
+ const root = getTilesRoot();
45311
+ if (cachedGlobalLeafGeometricErrorRoot !== root) {
45312
+ cachedGlobalLeafGeometricErrorRoot = root;
45313
+ cachedGlobalLeafGeometricError = null;
45314
+ knownTileChildCounts = /* @__PURE__ */ new WeakMap();
45315
+ }
45316
+ return cachedGlobalLeafGeometricError;
45317
+ }
45318
+ function setCachedGlobalLeafGeometricError(leafGeometricError) {
45319
+ cachedGlobalLeafGeometricErrorRoot = getTilesRoot();
45320
+ cachedGlobalLeafGeometricError = leafGeometricError;
45321
+ }
45322
+ function clearCachedGlobalLeafGeometricError() {
45323
+ cachedGlobalLeafGeometricError = null;
45324
+ }
45325
+ function trackTileChildCount(tile, children) {
45326
+ knownTileChildCounts.set(tile, children.length);
45327
+ }
45328
+ function invalidateLeafCacheIfParentChildrenChanged(parentTile) {
45329
+ if (!parentTile || typeof parentTile !== "object") {
45330
+ return;
45331
+ }
45332
+ const children = Array.isArray(parentTile.children) ? parentTile.children : [];
45333
+ const knownChildCount = knownTileChildCounts.get(parentTile);
45334
+ if (cachedGlobalLeafGeometricError !== null && knownChildCount !== children.length) {
45335
+ clearCachedGlobalLeafGeometricError();
45336
+ }
45337
+ trackTileChildCount(parentTile, children);
45338
+ }
45388
45339
  function updateTilesetErrorTarget() {
45389
45340
  const tiles = getTiles();
45390
45341
  if (!tiles) {
@@ -45423,6 +45374,7 @@ function createGeometricErrorController({
45423
45374
  visited.add(tile);
45424
45375
  let leafGeometricError = null;
45425
45376
  const children = Array.isArray(tile.children) ? tile.children : [];
45377
+ trackTileChildCount(tile, children);
45426
45378
  for (const child of children) {
45427
45379
  const childLeafGeometricError = getKnownTileLeafGeometricError(
45428
45380
  child,
@@ -45435,31 +45387,58 @@ function createGeometricErrorController({
45435
45387
  visited.delete(tile);
45436
45388
  return leafGeometricError === null ? originalGeometricError : leafGeometricError;
45437
45389
  }
45438
- function getGlobalTileLeafGeometricError(tile) {
45390
+ function getGlobalTileLeafGeometricError(tile, { forceRefresh = false } = {}) {
45391
+ if (!forceRefresh) {
45392
+ const cachedLeafGeometricError = getCachedGlobalLeafGeometricError();
45393
+ if (cachedLeafGeometricError !== null) {
45394
+ return cachedLeafGeometricError;
45395
+ }
45396
+ }
45439
45397
  const tiles = getTiles();
45440
45398
  const rootLeafGeometricError = tiles?.root ? getKnownTileLeafGeometricError(tiles.root) : null;
45399
+ if (tiles?.root && tile === tiles.root) {
45400
+ setCachedGlobalLeafGeometricError(rootLeafGeometricError);
45401
+ return rootLeafGeometricError;
45402
+ }
45441
45403
  const tileLeafGeometricError = getKnownTileLeafGeometricError(tile);
45404
+ let leafGeometricError = null;
45442
45405
  if (rootLeafGeometricError === null) {
45443
- return tileLeafGeometricError;
45444
- }
45445
- if (tileLeafGeometricError === null) {
45446
- return rootLeafGeometricError;
45406
+ leafGeometricError = tileLeafGeometricError;
45407
+ } else if (tileLeafGeometricError === null) {
45408
+ leafGeometricError = rootLeafGeometricError;
45409
+ } else {
45410
+ leafGeometricError = Math.min(
45411
+ rootLeafGeometricError,
45412
+ tileLeafGeometricError
45413
+ );
45447
45414
  }
45448
- return Math.min(rootLeafGeometricError, tileLeafGeometricError);
45415
+ setCachedGlobalLeafGeometricError(leafGeometricError);
45416
+ return leafGeometricError;
45449
45417
  }
45450
- function applyLayerScaleToTile(tile, leafGeometricError = getGlobalTileLeafGeometricError(tile)) {
45418
+ function applyLayerScaleToTile(tile, leafGeometricError = null, parentTile = null) {
45419
+ invalidateLeafCacheIfParentChildrenChanged(parentTile);
45451
45420
  const originalGeometricError = getOriginalTileGeometricError(tile);
45452
- if (originalGeometricError === null || leafGeometricError === null) {
45421
+ if (originalGeometricError === null) {
45422
+ return;
45423
+ }
45424
+ const layerScale = getEffectiveGeometricErrorLayerScale();
45425
+ if (layerScale === 1) {
45426
+ tile.geometricError = originalGeometricError;
45427
+ return;
45428
+ }
45429
+ const effectiveLeafGeometricError = leafGeometricError ?? getGlobalTileLeafGeometricError(tile);
45430
+ if (effectiveLeafGeometricError === null) {
45453
45431
  return;
45454
45432
  }
45455
- tile.geometricError = leafGeometricError + (originalGeometricError - leafGeometricError) * getEffectiveGeometricErrorLayerScale();
45433
+ tile.geometricError = effectiveLeafGeometricError + (originalGeometricError - effectiveLeafGeometricError) * layerScale;
45456
45434
  }
45457
45435
  function applyLayerScaleToTileset() {
45458
45436
  const tiles = getTiles();
45459
45437
  if (!tiles) {
45460
45438
  return;
45461
45439
  }
45462
- const leafGeometricError = getGlobalTileLeafGeometricError(tiles.root);
45440
+ const layerScale = getEffectiveGeometricErrorLayerScale();
45441
+ const leafGeometricError = layerScale === 1 ? null : getGlobalTileLeafGeometricError(tiles.root, { forceRefresh: true });
45463
45442
  tiles.traverse(
45464
45443
  (tile) => {
45465
45444
  applyLayerScaleToTile(tile, leafGeometricError);
@@ -63334,8 +63313,8 @@ function createTerrainGlobeTiles(options) {
63334
63313
  function createGeometricErrorLayerScalePlugin(preprocessNode) {
63335
63314
  return {
63336
63315
  name: "GeometricErrorLayerScalePlugin",
63337
- preprocessNode(tile) {
63338
- preprocessNode(tile);
63316
+ preprocessNode(tile, tilesetDir, parentTile) {
63317
+ preprocessNode(tile, null, parentTile);
63339
63318
  }
63340
63319
  };
63341
63320
  }
@@ -71652,10 +71631,11 @@ var require_app = __commonJS({
71652
71631
  const processedSplatResources = Number(
71653
71632
  payload.processedSplatResources || 0
71654
71633
  );
71634
+ const deletedSplatFiles = Number(payload.deletedSplatFiles || 0);
71655
71635
  cropController.clearAll();
71656
71636
  loadTileset(TILESET_URL, { frameOnLoad: false });
71657
71637
  setStatus(
71658
- `Saved transform and deleted ${deletedSplats} cropped splats from ${processedSplatResources} splat resource${processedSplatResources === 1 ? "" : "s"}. Reloading tileset.`
71638
+ `Saved transform and deleted ${deletedSplats} cropped splats from ${processedSplatResources} splat resource${processedSplatResources === 1 ? "" : "s"}${deletedSplatFiles > 0 ? `, removing ${deletedSplatFiles} orphaned file${deletedSplatFiles === 1 ? "" : "s"}` : ""}. Reloading tileset.`
71659
71639
  );
71660
71640
  } else {
71661
71641
  setStatus(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "3dtiles-inspector",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "Inspect, align, and save local 3D Tiles root transforms in an interactive browser session.",
5
5
  "author": "William Liu <lyz15972107087@gmail.com>",
6
6
  "license": "Apache-2.0",
@@ -55,7 +55,7 @@
55
55
  },
56
56
  "devDependencies": {
57
57
  "3d-tiles-renderer": "0.4.24",
58
- "3d-tiles-rendererjs-3dgs-plugin": "0.1.5",
58
+ "3d-tiles-rendererjs-3dgs-plugin": "0.1.7",
59
59
  "cesium": "1.140.0",
60
60
  "esbuild": "^0.25.11"
61
61
  },
@@ -471,6 +471,8 @@ async function saveViewerTransform(
471
471
  return {
472
472
  transform: nextRoot,
473
473
  deletedSplats: cropResult.deletedSplats,
474
+ deletedSplatFiles: cropResult.deletedSplatFiles,
475
+ failedSplatFileDeletes: cropResult.failedSplatFileDeletes,
474
476
  processedSplatResources: cropResult.processedSplatResources,
475
477
  };
476
478
  }
@@ -23,6 +23,8 @@ function createSaveTransformResponsePayload(
23
23
  transform: saveResult.transform,
24
24
  geometricErrorLayerScale: normalizedGeometricErrorLayerScale,
25
25
  geometricErrorScale: normalizedGeometricErrorScale,
26
+ deletedSplatFiles: saveResult.deletedSplatFiles,
27
+ failedSplatFileDeletes: saveResult.failedSplatFileDeletes,
26
28
  deletedSplats: saveResult.deletedSplats,
27
29
  processedSplatResources: saveResult.processedSplatResources,
28
30
  };
@@ -239,6 +239,53 @@ function removeMeshPrimitives(resource, descriptors) {
239
239
  return removed;
240
240
  }
241
241
 
242
+ function updateGaussianPrimitiveAccessorCounts(resource, descriptors, count) {
243
+ if (!Number.isInteger(count) || count < 0) {
244
+ throw new InspectorError('Gaussian accessor count must be a non-negative integer.');
245
+ }
246
+
247
+ const updatedAccessors = new Set();
248
+ descriptors.forEach((descriptor) => {
249
+ if (
250
+ !Number.isInteger(descriptor.meshIndex) ||
251
+ !Number.isInteger(descriptor.primitiveIndex)
252
+ ) {
253
+ return;
254
+ }
255
+
256
+ const primitive =
257
+ resource.json.meshes?.[descriptor.meshIndex]?.primitives?.[
258
+ descriptor.primitiveIndex
259
+ ];
260
+ const attributes = primitive?.attributes;
261
+ if (!attributes || typeof attributes !== 'object') {
262
+ return;
263
+ }
264
+
265
+ Object.values(attributes).forEach((accessorIndex) => {
266
+ if (
267
+ !Number.isInteger(accessorIndex) ||
268
+ accessorIndex < 0 ||
269
+ accessorIndex >= (resource.json.accessors?.length || 0)
270
+ ) {
271
+ return;
272
+ }
273
+
274
+ const accessor = resource.json.accessors[accessorIndex];
275
+ if (!accessor || typeof accessor !== 'object') {
276
+ return;
277
+ }
278
+
279
+ if (accessor.count !== count) {
280
+ accessor.count = count;
281
+ updatedAccessors.add(accessorIndex);
282
+ }
283
+ });
284
+ });
285
+
286
+ return updatedAccessors.size;
287
+ }
288
+
242
289
  module.exports = {
243
290
  collectGaussianPrimitiveDescriptors,
244
291
  getRootUpRotationMatrix,
@@ -247,4 +294,5 @@ module.exports = {
247
294
  hasNonGaussianScenePrimitives,
248
295
  hasScenePrimitives,
249
296
  removeMeshPrimitives,
297
+ updateGaussianPrimitiveAccessorCounts,
250
298
  };
@@ -70,7 +70,7 @@ function parseGlb(filePath) {
70
70
  if (chunkType === GLB_JSON_CHUNK_TYPE) {
71
71
  json = JSON.parse(chunk.toString('utf8').replace(/\0+$/g, '').trimEnd());
72
72
  } else if (chunkType === GLB_BIN_CHUNK_TYPE && !bin) {
73
- bin = Buffer.from(chunk);
73
+ bin = chunk;
74
74
  }
75
75
 
76
76
  offset = chunkEnd;
@@ -190,7 +190,7 @@ function loadResourceBuffer(resource, bufferIndex) {
190
190
  `${resource.filePath}.buffers[${bufferIndex}].uri`,
191
191
  );
192
192
  resource.dataUriMetadata.set(bufferIndex, metadata);
193
- record = { bytes: Buffer.from(bytes), dataUri: true };
193
+ record = { bytes, dataUri: true };
194
194
  } else {
195
195
  const bufferPath = resolveLocalUri(
196
196
  path.dirname(resource.filePath),
@@ -205,7 +205,7 @@ function loadResourceBuffer(resource, bufferIndex) {
205
205
  }
206
206
  } else if (resource.type === 'glb' && bufferIndex === 0) {
207
207
  record = {
208
- bytes: Buffer.from(resource.embeddedBin || Buffer.alloc(0)),
208
+ bytes: resource.embeddedBin || Buffer.alloc(0),
209
209
  embedded: true,
210
210
  };
211
211
  } else {
@@ -322,7 +322,7 @@ function applyBufferReplacements(resource, bufferIndex, replacements) {
322
322
 
323
323
  replacements.forEach((replacement) => {
324
324
  parts.push(record.bytes.subarray(cursor, replacement.start));
325
- parts.push(Buffer.from(replacement.bytes));
325
+ parts.push(replacement.bytes);
326
326
  cursor = replacement.end;
327
327
  });
328
328