3dtiles-inspector 0.1.6 → 0.1.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 +17 -0
- package/README.md +10 -1
- package/dist/inspector-assets/viewer/app.js +147 -6
- package/package.json +1 -1
- package/src/viewer/app.js +46 -4
- package/src/viewer/cameraController.js +129 -3
- package/src/viewer/session.js +109 -34
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,23 @@ The format is based on Keep a Changelog and this project follows Semantic Versio
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.1.8] - 2026-05-03
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Added a camera pivot indicator for rotate, pan, and zoom interactions.
|
|
14
|
+
- Added bottom-right tile runtime stats for downloading, parsing, loaded, and visible tile counts.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- Moved the `Canvas` toolbar section above `Transform`.
|
|
19
|
+
|
|
20
|
+
## [0.1.7] - 2026-04-27
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- Changed `Layer Multiplier` to use the tileset-wide minimum leaf geometric error as the baseline, so non-minimum leaf tiles are scaled too.
|
|
25
|
+
|
|
9
26
|
## [0.1.6] - 2026-04-27
|
|
10
27
|
|
|
11
28
|
### Changed
|
package/README.md
CHANGED
|
@@ -14,6 +14,13 @@
|
|
|
14
14
|
|
|
15
15
|
Requires Node.js 18 or newer.
|
|
16
16
|
|
|
17
|
+
## Built On
|
|
18
|
+
|
|
19
|
+
This project is based on and integrates work from:
|
|
20
|
+
|
|
21
|
+
- [WilliamLiu-1997/3D-Tiles-RendererJS-3DGS-Plugin](https://github.com/WilliamLiu-1997/3D-Tiles-RendererJS-3DGS-Plugin)
|
|
22
|
+
- [NASA-AMMOS/3DTilesRendererJS](https://github.com/NASA-AMMOS/3DTilesRendererJS)
|
|
23
|
+
|
|
17
24
|
## Install
|
|
18
25
|
|
|
19
26
|
```bash
|
|
@@ -76,13 +83,15 @@ const {
|
|
|
76
83
|
|
|
77
84
|
## Inspector Features
|
|
78
85
|
|
|
86
|
+
<img src="https://raw.githubusercontent.com/WilliamLiu-1997/3DTiles-Inspector/main/screenshot.png" alt="screenshot" width="960" />
|
|
87
|
+
|
|
79
88
|
- `Translate`, `Rotate`, and `Reset` for root transform edits
|
|
80
89
|
- `Move Camera` to a WGS84 latitude / longitude / height
|
|
81
90
|
- `Move Tiles` to relocate the tileset root with an ENU-aligned transform
|
|
82
91
|
- `Set Position` to click the globe, terrain, or loaded tiles and place the tileset there
|
|
83
92
|
- `Terrain` to toggle Cesium World Terrain while keeping satellite imagery
|
|
84
93
|
- `Geometric Error` scaling from `1/16x` to `16x`
|
|
85
|
-
- `Layer Multiplier` scaling from `1/8x` to `8x` for each tile's geometric-error difference from
|
|
94
|
+
- `Layer Multiplier` scaling from `1/8x` to `8x` for each tile's geometric-error difference from the tileset's global leaf baseline
|
|
86
95
|
- `Save` to persist the updated root transform and geometric-error scale back to disk
|
|
87
96
|
|
|
88
97
|
If `build_summary.json` exists next to the root tileset, `Save` also updates:
|
|
@@ -64485,6 +64485,88 @@ var PointerTracker = class {
|
|
|
64485
64485
|
return Boolean(this.buttons & 4);
|
|
64486
64486
|
}
|
|
64487
64487
|
};
|
|
64488
|
+
var PivotPointMesh = class extends Mesh {
|
|
64489
|
+
constructor(size = 15, thickness = 3) {
|
|
64490
|
+
super(new PlaneGeometry(0, 0), new PivotMaterial(size, thickness));
|
|
64491
|
+
this.renderOrder = Infinity;
|
|
64492
|
+
}
|
|
64493
|
+
set focus(value) {
|
|
64494
|
+
this.material.uniforms.opacity.value = value ? 1 : 0.5;
|
|
64495
|
+
}
|
|
64496
|
+
onBeforeRender(renderer2) {
|
|
64497
|
+
renderer2.getSize(this.material.uniforms.resolution.value);
|
|
64498
|
+
}
|
|
64499
|
+
updateMatrixWorld() {
|
|
64500
|
+
this.matrixWorld.makeTranslation(this.position);
|
|
64501
|
+
}
|
|
64502
|
+
dispose() {
|
|
64503
|
+
this.geometry.dispose();
|
|
64504
|
+
this.material.dispose();
|
|
64505
|
+
}
|
|
64506
|
+
};
|
|
64507
|
+
var PivotMaterial = class extends ShaderMaterial {
|
|
64508
|
+
constructor(size, thickness) {
|
|
64509
|
+
const coreD = size + thickness;
|
|
64510
|
+
const planeD = coreD + 3 * thickness;
|
|
64511
|
+
const normThk = thickness / coreD;
|
|
64512
|
+
const ringR = (coreD - 0.4 * thickness - 4) / coreD;
|
|
64513
|
+
const hw = 0.4 * normThk;
|
|
64514
|
+
super({
|
|
64515
|
+
depthWrite: false,
|
|
64516
|
+
depthTest: false,
|
|
64517
|
+
transparent: true,
|
|
64518
|
+
uniforms: {
|
|
64519
|
+
resolution: { value: new Vector2() },
|
|
64520
|
+
opacity: { value: 1 },
|
|
64521
|
+
planeD: { value: planeD },
|
|
64522
|
+
hw: { value: hw },
|
|
64523
|
+
ringR: { value: ringR },
|
|
64524
|
+
shadowW: { value: hw * 5 },
|
|
64525
|
+
uvScale: { value: planeD / coreD }
|
|
64526
|
+
},
|
|
64527
|
+
vertexShader: `
|
|
64528
|
+
uniform float planeD;
|
|
64529
|
+
uniform vec2 resolution;
|
|
64530
|
+
varying vec2 vUv;
|
|
64531
|
+
|
|
64532
|
+
void main() {
|
|
64533
|
+
vUv = uv;
|
|
64534
|
+
float aspect = resolution.x / resolution.y;
|
|
64535
|
+
vec2 offset = uv * 2.0 - vec2(1.0);
|
|
64536
|
+
offset.y *= aspect;
|
|
64537
|
+
vec4 screenPoint = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
64538
|
+
screenPoint.xy += offset * planeD * screenPoint.w / resolution.x;
|
|
64539
|
+
gl_Position = screenPoint;
|
|
64540
|
+
}
|
|
64541
|
+
`,
|
|
64542
|
+
fragmentShader: `
|
|
64543
|
+
uniform float hw;
|
|
64544
|
+
uniform float ringR;
|
|
64545
|
+
uniform float shadowW;
|
|
64546
|
+
uniform float opacity;
|
|
64547
|
+
uniform float uvScale;
|
|
64548
|
+
varying vec2 vUv;
|
|
64549
|
+
|
|
64550
|
+
void main() {
|
|
64551
|
+
vec2 uv = (vUv * 2.0 - 1.0) * uvScale;
|
|
64552
|
+
float len = length(uv);
|
|
64553
|
+
float fw = fwidth(len) * 0.5;
|
|
64554
|
+
float d = abs(len - ringR);
|
|
64555
|
+
|
|
64556
|
+
float ring = 1.0 - smoothstep(hw - fw, hw + fw, d);
|
|
64557
|
+
|
|
64558
|
+
float shadow = (1.0 - smoothstep(hw, shadowW, d)) * (1.0 - smoothstep(ringR - fw, ringR + fw, len)) * 0.5;
|
|
64559
|
+
|
|
64560
|
+
float white = ring;
|
|
64561
|
+
float black = shadow * (1.0 - white);
|
|
64562
|
+
float alpha = (white + black) * opacity;
|
|
64563
|
+
if (alpha < 0.001) discard;
|
|
64564
|
+
gl_FragColor = vec4(vec3(white / max(alpha / opacity, 0.001)), alpha);
|
|
64565
|
+
}
|
|
64566
|
+
`
|
|
64567
|
+
});
|
|
64568
|
+
}
|
|
64569
|
+
};
|
|
64488
64570
|
var _matrix2 = new Matrix4();
|
|
64489
64571
|
function setRaycasterFromCamera(raycaster, coords, camera2) {
|
|
64490
64572
|
const ray = raycaster instanceof Ray ? raycaster : raycaster.ray;
|
|
@@ -64522,6 +64604,8 @@ var UPDATE_EVENT = { type: "update" };
|
|
|
64522
64604
|
var FINISH_EVENT = { type: "finish" };
|
|
64523
64605
|
var THRESHOLD = 1e-3;
|
|
64524
64606
|
var MAX = 1e8;
|
|
64607
|
+
var PIVOT_SIZE = 22;
|
|
64608
|
+
var PIVOT_THICKNESS = 2.5;
|
|
64525
64609
|
var VIRTUAL_HIT_DISTANCE = 50;
|
|
64526
64610
|
var ZOOM_OUT_TRANSITION_COS_THRESHOLD = Math.cos(105 * Math.PI / 180);
|
|
64527
64611
|
var ZOOM_OUT_TRANSITION_COS_MAX_THRESHOLD = Math.cos(95 * Math.PI / 180);
|
|
@@ -64550,7 +64634,7 @@ var _quaternion2 = new Quaternion();
|
|
|
64550
64634
|
var _plane = new Plane();
|
|
64551
64635
|
var _ray2 = new Ray();
|
|
64552
64636
|
var _zoomOutMetrics = { distanceScale: 1, transitionWeight: 0 };
|
|
64553
|
-
var _pointerTracker, _domElement, _camera3, _scene2, _raycaster2, _zoomDelta, _zoomInertia, _rotateInertia, _dragInertia, _dragAnchorPoint, _dragStartPosition, _dragStartQuaternion, _dragPlaneNormal, _inertiaValue, _enabled, _ellipsoid, _ellipsoidMaxRadius, _lastTime, _hit, _contextMenuEvent, _pointerDownEvent, _pointerMoveEvent, _pointerUpEvent, _wheelEvent, _pointerEnterEvent, _zoomTimeout, _CameraController_instances, setState_fn, setZooming_fn, resetState_fn, bindEvents_fn, _contextMenu, _pointerDown, _pointerMove, _pointerUp, _wheel, _pointerEnter, finalizeCamera_fn, alignCameraRightToXYPlane_fn, rotateNearAnchor_fn, clampVerticalRotateAngle_fn, rotate_fn, initializeDragAnchor_fn, restoreDragStartCamera_fn, intersectDragPlane_fn, modifiedDrag_fn, keepCameraUpAtFixedPoint_fn, getZoomOutMetrics_fn, getScaledZoomTarget_fn, getZoomOutTransitionTarget_fn, getZoomPosition_fn, applyZoom_fn, reachCameraMaxDistance_fn, isCameraCenterMode_fn, limitCameraDistance_fn, shouldDragModified_fn, keepCameraUp_fn, normalRaycastClosest_fn, raycast_fn;
|
|
64637
|
+
var _pointerTracker, _domElement, _camera3, _scene2, _raycaster2, _pivotMesh, _zoomDelta, _zoomInertia, _rotateInertia, _dragInertia, _dragAnchorPoint, _dragStartPosition, _dragStartQuaternion, _dragPlaneNormal, _inertiaValue, _enabled, _ellipsoid, _ellipsoidMaxRadius, _lastTime, _hit, _contextMenuEvent, _pointerDownEvent, _pointerMoveEvent, _pointerUpEvent, _wheelEvent, _pointerEnterEvent, _zoomTimeout, _CameraController_instances, setState_fn, setZooming_fn, resetState_fn, bindEvents_fn, _contextMenu, updateIndicatorFromHit_fn, _pointerDown, _pointerMove, _pointerUp, _wheel, _pointerEnter, finalizeCamera_fn, alignCameraRightToXYPlane_fn, rotateNearAnchor_fn, clampVerticalRotateAngle_fn, rotate_fn, initializeDragAnchor_fn, restoreDragStartCamera_fn, intersectDragPlane_fn, modifiedDrag_fn, keepCameraUpAtFixedPoint_fn, getZoomOutMetrics_fn, getScaledZoomTarget_fn, getZoomOutTransitionTarget_fn, getZoomPosition_fn, applyZoom_fn, reachCameraMaxDistance_fn, isCameraCenterMode_fn, limitCameraDistance_fn, shouldDragModified_fn, keepCameraUp_fn, normalRaycastClosest_fn, raycast_fn;
|
|
64554
64638
|
var CameraController = class extends EventDispatcher {
|
|
64555
64639
|
constructor(renderer2, scene2, camera2, options = {}) {
|
|
64556
64640
|
super();
|
|
@@ -64567,6 +64651,7 @@ var CameraController = class extends EventDispatcher {
|
|
|
64567
64651
|
__privateAdd(this, _camera3);
|
|
64568
64652
|
__privateAdd(this, _scene2);
|
|
64569
64653
|
__privateAdd(this, _raycaster2);
|
|
64654
|
+
__privateAdd(this, _pivotMesh);
|
|
64570
64655
|
__privateAdd(this, _zoomDelta);
|
|
64571
64656
|
__privateAdd(this, _zoomInertia);
|
|
64572
64657
|
__privateAdd(this, _rotateInertia);
|
|
@@ -64624,6 +64709,7 @@ var CameraController = class extends EventDispatcher {
|
|
|
64624
64709
|
mouseToCoords(_pointer1.x, _pointer1.y, __privateGet(this, _domElement), _pointer1);
|
|
64625
64710
|
setRaycasterFromCamera(__privateGet(this, _raycaster2), _pointer1, __privateGet(this, _camera3));
|
|
64626
64711
|
__privateSet(this, _hit, __privateMethod(this, _CameraController_instances, raycast_fn).call(this, __privateGet(this, _raycaster2)));
|
|
64712
|
+
__privateMethod(this, _CameraController_instances, updateIndicatorFromHit_fn).call(this);
|
|
64627
64713
|
if (this.state === DRAG && __privateGet(this, _hit).distance > 0) {
|
|
64628
64714
|
__privateMethod(this, _CameraController_instances, initializeDragAnchor_fn).call(this);
|
|
64629
64715
|
}
|
|
@@ -64660,7 +64746,7 @@ var CameraController = class extends EventDispatcher {
|
|
|
64660
64746
|
if (!__privateGet(this, _enabled)) {
|
|
64661
64747
|
return;
|
|
64662
64748
|
}
|
|
64663
|
-
const tooClose = __privateGet(this,
|
|
64749
|
+
const tooClose = __privateGet(this, _pivotMesh).position.distanceTo(__privateGet(this, _camera3).position) <= __privateGet(this, _camera3).near;
|
|
64664
64750
|
if (!this.zooming || tooClose) {
|
|
64665
64751
|
__privateGet(this, _rotateInertia).set(0, 0);
|
|
64666
64752
|
__privateGet(this, _dragInertia).set(0, 0, 0);
|
|
@@ -64674,6 +64760,7 @@ var CameraController = class extends EventDispatcher {
|
|
|
64674
64760
|
setRaycasterFromCamera(__privateGet(this, _raycaster2), _pointer1, __privateGet(this, _camera3));
|
|
64675
64761
|
if (!this.zooming && this.state === NONE || tooClose) {
|
|
64676
64762
|
__privateSet(this, _hit, __privateMethod(this, _CameraController_instances, raycast_fn).call(this, __privateGet(this, _raycaster2)));
|
|
64763
|
+
__privateMethod(this, _CameraController_instances, updateIndicatorFromHit_fn).call(this);
|
|
64677
64764
|
}
|
|
64678
64765
|
let delta = 0;
|
|
64679
64766
|
switch (e.deltaMode) {
|
|
@@ -64721,6 +64808,8 @@ var CameraController = class extends EventDispatcher {
|
|
|
64721
64808
|
__privateSet(this, _pointerTracker, new PointerTracker());
|
|
64722
64809
|
__privateSet(this, _raycaster2, new Raycaster());
|
|
64723
64810
|
__privateGet(this, _raycaster2).params.Points.threshold = 0.1;
|
|
64811
|
+
__privateSet(this, _pivotMesh, new PivotPointMesh(PIVOT_SIZE, PIVOT_THICKNESS));
|
|
64812
|
+
__privateGet(this, _pivotMesh).visible = false;
|
|
64724
64813
|
__privateSet(this, _zoomDelta, 0);
|
|
64725
64814
|
__privateSet(this, _zoomInertia, 0);
|
|
64726
64815
|
__privateSet(this, _rotateInertia, new Vector2());
|
|
@@ -64759,6 +64848,9 @@ var CameraController = class extends EventDispatcher {
|
|
|
64759
64848
|
get camera() {
|
|
64760
64849
|
return __privateGet(this, _camera3);
|
|
64761
64850
|
}
|
|
64851
|
+
get indicator() {
|
|
64852
|
+
return __privateGet(this, _pivotMesh);
|
|
64853
|
+
}
|
|
64762
64854
|
setCamera(camera2) {
|
|
64763
64855
|
__privateSet(this, _camera3, camera2);
|
|
64764
64856
|
__privateMethod(this, _CameraController_instances, resetState_fn).call(this);
|
|
@@ -64772,6 +64864,9 @@ var CameraController = class extends EventDispatcher {
|
|
|
64772
64864
|
}
|
|
64773
64865
|
init() {
|
|
64774
64866
|
__privateGet(this, _domElement).style.touchAction = "none";
|
|
64867
|
+
__privateGet(this, _pivotMesh).raycast = () => {
|
|
64868
|
+
};
|
|
64869
|
+
__privateGet(this, _scene2).add(__privateGet(this, _pivotMesh));
|
|
64775
64870
|
__privateSet(this, _contextMenuEvent, __privateGet(this, _contextMenu).bind(this));
|
|
64776
64871
|
__privateSet(this, _pointerDownEvent, __privateGet(this, _pointerDown).bind(this));
|
|
64777
64872
|
__privateSet(this, _pointerMoveEvent, __privateGet(this, _pointerMove).bind(this));
|
|
@@ -64945,6 +65040,8 @@ var CameraController = class extends EventDispatcher {
|
|
|
64945
65040
|
"pointerenter",
|
|
64946
65041
|
__privateGet(this, _pointerEnterEvent)
|
|
64947
65042
|
);
|
|
65043
|
+
__privateGet(this, _pivotMesh).removeFromParent();
|
|
65044
|
+
__privateGet(this, _pivotMesh).dispose();
|
|
64948
65045
|
__privateGet(this, _domElement).style.touchAction = "";
|
|
64949
65046
|
__privateSet(this, _enabled, false);
|
|
64950
65047
|
__privateSet(this, _ellipsoid, null);
|
|
@@ -64955,6 +65052,7 @@ _domElement = new WeakMap();
|
|
|
64955
65052
|
_camera3 = new WeakMap();
|
|
64956
65053
|
_scene2 = new WeakMap();
|
|
64957
65054
|
_raycaster2 = new WeakMap();
|
|
65055
|
+
_pivotMesh = new WeakMap();
|
|
64958
65056
|
_zoomDelta = new WeakMap();
|
|
64959
65057
|
_zoomInertia = new WeakMap();
|
|
64960
65058
|
_rotateInertia = new WeakMap();
|
|
@@ -64982,8 +65080,14 @@ setState_fn = function(state = this.state) {
|
|
|
64982
65080
|
return;
|
|
64983
65081
|
}
|
|
64984
65082
|
this.state = state;
|
|
65083
|
+
if (state !== NONE) {
|
|
65084
|
+
__privateGet(this, _pivotMesh).visible = true;
|
|
65085
|
+
}
|
|
64985
65086
|
};
|
|
64986
65087
|
setZooming_fn = function(zooming, touchZooming = false) {
|
|
65088
|
+
if (!this.zooming && this.state === NONE && zooming) {
|
|
65089
|
+
__privateGet(this, _pivotMesh).visible = true;
|
|
65090
|
+
}
|
|
64987
65091
|
this.zooming = zooming;
|
|
64988
65092
|
this.touchZooming = touchZooming;
|
|
64989
65093
|
};
|
|
@@ -65000,6 +65104,7 @@ resetState_fn = function() {
|
|
|
65000
65104
|
__privateGet(this, _dragPlaneNormal).set(0, 0, 0);
|
|
65001
65105
|
__privateSet(this, _zoomInertia, 0);
|
|
65002
65106
|
__privateSet(this, _hit, null);
|
|
65107
|
+
__privateGet(this, _pivotMesh).visible = false;
|
|
65003
65108
|
};
|
|
65004
65109
|
bindEvents_fn = function() {
|
|
65005
65110
|
__privateGet(this, _domElement).addEventListener("contextmenu", __privateGet(this, _contextMenuEvent));
|
|
@@ -65010,6 +65115,15 @@ bindEvents_fn = function() {
|
|
|
65010
65115
|
__privateGet(this, _domElement).addEventListener("pointerenter", __privateGet(this, _pointerEnterEvent));
|
|
65011
65116
|
};
|
|
65012
65117
|
_contextMenu = new WeakMap();
|
|
65118
|
+
updateIndicatorFromHit_fn = function() {
|
|
65119
|
+
if (__privateGet(this, _hit) && __privateGet(this, _hit).distance > 0) {
|
|
65120
|
+
__privateGet(this, _pivotMesh).visible = true;
|
|
65121
|
+
__privateGet(this, _pivotMesh).position.copy(__privateGet(this, _hit).point);
|
|
65122
|
+
__privateGet(this, _pivotMesh).focus = !__privateGet(this, _hit).onGlobe;
|
|
65123
|
+
} else {
|
|
65124
|
+
__privateGet(this, _pivotMesh).visible = false;
|
|
65125
|
+
}
|
|
65126
|
+
};
|
|
65013
65127
|
_pointerDown = new WeakMap();
|
|
65014
65128
|
_pointerMove = new WeakMap();
|
|
65015
65129
|
_pointerUp = new WeakMap();
|
|
@@ -65291,6 +65405,7 @@ applyZoom_fn = function(zoomAmount) {
|
|
|
65291
65405
|
_forward.subVectors(hit.point, __privateGet(this, _camera3).position).normalize();
|
|
65292
65406
|
hit.point.copy(__privateGet(this, _camera3).position).addScaledVector(_forward, VIRTUAL_HIT_DISTANCE);
|
|
65293
65407
|
hit.distance = VIRTUAL_HIT_DISTANCE;
|
|
65408
|
+
__privateGet(this, _pivotMesh).position.copy(hit.point);
|
|
65294
65409
|
}
|
|
65295
65410
|
let zoomFactor = Math.exp(-zoomAmount * 1e-3);
|
|
65296
65411
|
if (this.minDistance > 0 && zoomFactor < 1) {
|
|
@@ -65463,6 +65578,12 @@ var MOVE_TO_COORDINATE_RADIUS = 10;
|
|
|
65463
65578
|
var statusEl = document.getElementById("status");
|
|
65464
65579
|
var cacheBytesValueEl = document.getElementById("cache-bytes-value");
|
|
65465
65580
|
var splatsCountValueEl = document.getElementById("splats-count-value");
|
|
65581
|
+
var tilesDownloadingValueEl = document.getElementById(
|
|
65582
|
+
"tiles-downloading-value"
|
|
65583
|
+
);
|
|
65584
|
+
var tilesParsingValueEl = document.getElementById("tiles-parsing-value");
|
|
65585
|
+
var tilesLoadedValueEl = document.getElementById("tiles-loaded-value");
|
|
65586
|
+
var tilesVisibleValueEl = document.getElementById("tiles-visible-value");
|
|
65466
65587
|
var toolbarEl = document.getElementById("toolbar");
|
|
65467
65588
|
var toolbarDockEl = toolbarEl.parentElement;
|
|
65468
65589
|
var toolbarToggleButton = document.getElementById("toolbar-toggle");
|
|
@@ -65888,9 +66009,19 @@ function getKnownTileLeafGeometricError(tile, visited = /* @__PURE__ */ new Set(
|
|
|
65888
66009
|
visited.delete(tile);
|
|
65889
66010
|
return leafGeometricError === null ? originalGeometricError : leafGeometricError;
|
|
65890
66011
|
}
|
|
65891
|
-
function
|
|
66012
|
+
function getGlobalTileLeafGeometricError(tile) {
|
|
66013
|
+
const rootLeafGeometricError = tiles?.root ? getKnownTileLeafGeometricError(tiles.root) : null;
|
|
66014
|
+
const tileLeafGeometricError = getKnownTileLeafGeometricError(tile);
|
|
66015
|
+
if (rootLeafGeometricError === null) {
|
|
66016
|
+
return tileLeafGeometricError;
|
|
66017
|
+
}
|
|
66018
|
+
if (tileLeafGeometricError === null) {
|
|
66019
|
+
return rootLeafGeometricError;
|
|
66020
|
+
}
|
|
66021
|
+
return Math.min(rootLeafGeometricError, tileLeafGeometricError);
|
|
66022
|
+
}
|
|
66023
|
+
function applyGeometricErrorLayerScaleToTile(tile, leafGeometricError = getGlobalTileLeafGeometricError(tile)) {
|
|
65892
66024
|
const originalGeometricError = getOriginalTileGeometricError(tile);
|
|
65893
|
-
const leafGeometricError = getKnownTileLeafGeometricError(tile);
|
|
65894
66025
|
if (originalGeometricError === null || leafGeometricError === null) {
|
|
65895
66026
|
return;
|
|
65896
66027
|
}
|
|
@@ -65900,9 +66031,10 @@ function applyGeometricErrorLayerScaleToTileset() {
|
|
|
65900
66031
|
if (!tiles) {
|
|
65901
66032
|
return;
|
|
65902
66033
|
}
|
|
66034
|
+
const leafGeometricError = getGlobalTileLeafGeometricError(tiles.root);
|
|
65903
66035
|
tiles.traverse(
|
|
65904
66036
|
(tile) => {
|
|
65905
|
-
applyGeometricErrorLayerScaleToTile(tile);
|
|
66037
|
+
applyGeometricErrorLayerScaleToTile(tile, leafGeometricError);
|
|
65906
66038
|
return false;
|
|
65907
66039
|
},
|
|
65908
66040
|
null,
|
|
@@ -65952,7 +66084,7 @@ function getActiveSparkSplatsCount() {
|
|
|
65952
66084
|
return count;
|
|
65953
66085
|
}
|
|
65954
66086
|
function updateRuntimeStats(force = false) {
|
|
65955
|
-
if (!cacheBytesValueEl || !splatsCountValueEl) {
|
|
66087
|
+
if (!cacheBytesValueEl || !splatsCountValueEl || !tilesDownloadingValueEl || !tilesParsingValueEl || !tilesLoadedValueEl || !tilesVisibleValueEl) {
|
|
65956
66088
|
return;
|
|
65957
66089
|
}
|
|
65958
66090
|
const now = performance.now();
|
|
@@ -65961,10 +66093,19 @@ function updateRuntimeStats(force = false) {
|
|
|
65961
66093
|
}
|
|
65962
66094
|
lastRuntimeStatsUpdateTime = now;
|
|
65963
66095
|
const cacheBytes = tiles?.lruCache?.cachedBytes ?? 0;
|
|
66096
|
+
const tilesStats = tiles?.stats;
|
|
66097
|
+
const downloadingTiles = tilesStats?.downloading ?? 0;
|
|
66098
|
+
const parsingTiles = tilesStats?.parsing ?? 0;
|
|
66099
|
+
const loadedTiles = tilesStats?.loaded ?? 0;
|
|
66100
|
+
const visibleTiles = tiles?.visibleTiles?.size ?? tilesStats?.visible ?? 0;
|
|
65964
66101
|
const activeSparkSplats = getActiveSparkSplatsCount();
|
|
65965
66102
|
const splatCount = activeSparkSplats !== null ? activeSparkSplats : getLoadedGaussianSplatCount();
|
|
65966
66103
|
cacheBytesValueEl.textContent = formatBytes(cacheBytes);
|
|
65967
66104
|
splatsCountValueEl.textContent = formatInteger(splatCount);
|
|
66105
|
+
tilesDownloadingValueEl.textContent = formatInteger(downloadingTiles);
|
|
66106
|
+
tilesParsingValueEl.textContent = formatInteger(parsingTiles);
|
|
66107
|
+
tilesLoadedValueEl.textContent = formatInteger(loadedTiles);
|
|
66108
|
+
tilesVisibleValueEl.textContent = formatInteger(visibleTiles);
|
|
65968
66109
|
}
|
|
65969
66110
|
function setGeometricErrorScaleExponent(exponent) {
|
|
65970
66111
|
geometricErrorScaleExponent = clamp2(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "3dtiles-inspector",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.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",
|
package/src/viewer/app.js
CHANGED
|
@@ -79,6 +79,12 @@ const MOVE_TO_COORDINATE_RADIUS = 10;
|
|
|
79
79
|
const statusEl = document.getElementById('status');
|
|
80
80
|
const cacheBytesValueEl = document.getElementById('cache-bytes-value');
|
|
81
81
|
const splatsCountValueEl = document.getElementById('splats-count-value');
|
|
82
|
+
const tilesDownloadingValueEl = document.getElementById(
|
|
83
|
+
'tiles-downloading-value',
|
|
84
|
+
);
|
|
85
|
+
const tilesParsingValueEl = document.getElementById('tiles-parsing-value');
|
|
86
|
+
const tilesLoadedValueEl = document.getElementById('tiles-loaded-value');
|
|
87
|
+
const tilesVisibleValueEl = document.getElementById('tiles-visible-value');
|
|
82
88
|
const toolbarEl = document.getElementById('toolbar');
|
|
83
89
|
const toolbarDockEl = toolbarEl.parentElement;
|
|
84
90
|
const toolbarToggleButton = document.getElementById('toolbar-toggle');
|
|
@@ -580,9 +586,28 @@ function getKnownTileLeafGeometricError(tile, visited = new Set()) {
|
|
|
580
586
|
: leafGeometricError;
|
|
581
587
|
}
|
|
582
588
|
|
|
583
|
-
function
|
|
589
|
+
function getGlobalTileLeafGeometricError(tile) {
|
|
590
|
+
const rootLeafGeometricError = tiles?.root
|
|
591
|
+
? getKnownTileLeafGeometricError(tiles.root)
|
|
592
|
+
: null;
|
|
593
|
+
const tileLeafGeometricError = getKnownTileLeafGeometricError(tile);
|
|
594
|
+
|
|
595
|
+
if (rootLeafGeometricError === null) {
|
|
596
|
+
return tileLeafGeometricError;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
if (tileLeafGeometricError === null) {
|
|
600
|
+
return rootLeafGeometricError;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
return Math.min(rootLeafGeometricError, tileLeafGeometricError);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
function applyGeometricErrorLayerScaleToTile(
|
|
607
|
+
tile,
|
|
608
|
+
leafGeometricError = getGlobalTileLeafGeometricError(tile),
|
|
609
|
+
) {
|
|
584
610
|
const originalGeometricError = getOriginalTileGeometricError(tile);
|
|
585
|
-
const leafGeometricError = getKnownTileLeafGeometricError(tile);
|
|
586
611
|
if (originalGeometricError === null || leafGeometricError === null) {
|
|
587
612
|
return;
|
|
588
613
|
}
|
|
@@ -598,9 +623,10 @@ function applyGeometricErrorLayerScaleToTileset() {
|
|
|
598
623
|
return;
|
|
599
624
|
}
|
|
600
625
|
|
|
626
|
+
const leafGeometricError = getGlobalTileLeafGeometricError(tiles.root);
|
|
601
627
|
tiles.traverse(
|
|
602
628
|
(tile) => {
|
|
603
|
-
applyGeometricErrorLayerScaleToTile(tile);
|
|
629
|
+
applyGeometricErrorLayerScaleToTile(tile, leafGeometricError);
|
|
604
630
|
return false;
|
|
605
631
|
},
|
|
606
632
|
null,
|
|
@@ -672,7 +698,14 @@ function getActiveSparkSplatsCount() {
|
|
|
672
698
|
}
|
|
673
699
|
|
|
674
700
|
function updateRuntimeStats(force = false) {
|
|
675
|
-
if (
|
|
701
|
+
if (
|
|
702
|
+
!cacheBytesValueEl ||
|
|
703
|
+
!splatsCountValueEl ||
|
|
704
|
+
!tilesDownloadingValueEl ||
|
|
705
|
+
!tilesParsingValueEl ||
|
|
706
|
+
!tilesLoadedValueEl ||
|
|
707
|
+
!tilesVisibleValueEl
|
|
708
|
+
) {
|
|
676
709
|
return;
|
|
677
710
|
}
|
|
678
711
|
|
|
@@ -687,6 +720,11 @@ function updateRuntimeStats(force = false) {
|
|
|
687
720
|
lastRuntimeStatsUpdateTime = now;
|
|
688
721
|
|
|
689
722
|
const cacheBytes = tiles?.lruCache?.cachedBytes ?? 0;
|
|
723
|
+
const tilesStats = tiles?.stats;
|
|
724
|
+
const downloadingTiles = tilesStats?.downloading ?? 0;
|
|
725
|
+
const parsingTiles = tilesStats?.parsing ?? 0;
|
|
726
|
+
const loadedTiles = tilesStats?.loaded ?? 0;
|
|
727
|
+
const visibleTiles = tiles?.visibleTiles?.size ?? tilesStats?.visible ?? 0;
|
|
690
728
|
const activeSparkSplats = getActiveSparkSplatsCount();
|
|
691
729
|
const splatCount =
|
|
692
730
|
activeSparkSplats !== null
|
|
@@ -695,6 +733,10 @@ function updateRuntimeStats(force = false) {
|
|
|
695
733
|
|
|
696
734
|
cacheBytesValueEl.textContent = formatBytes(cacheBytes);
|
|
697
735
|
splatsCountValueEl.textContent = formatInteger(splatCount);
|
|
736
|
+
tilesDownloadingValueEl.textContent = formatInteger(downloadingTiles);
|
|
737
|
+
tilesParsingValueEl.textContent = formatInteger(parsingTiles);
|
|
738
|
+
tilesLoadedValueEl.textContent = formatInteger(loadedTiles);
|
|
739
|
+
tilesVisibleValueEl.textContent = formatInteger(visibleTiles);
|
|
698
740
|
}
|
|
699
741
|
|
|
700
742
|
function setGeometricErrorScaleExponent(exponent) {
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
EventDispatcher,
|
|
3
3
|
Matrix4,
|
|
4
|
+
Mesh,
|
|
4
5
|
Plane,
|
|
6
|
+
PlaneGeometry,
|
|
5
7
|
Quaternion,
|
|
6
8
|
Ray,
|
|
7
9
|
Raycaster,
|
|
10
|
+
ShaderMaterial,
|
|
8
11
|
Vector2,
|
|
9
12
|
Vector3,
|
|
10
13
|
} from 'three';
|
|
@@ -191,6 +194,98 @@ class PointerTracker {
|
|
|
191
194
|
return Boolean(this.buttons & 4);
|
|
192
195
|
}
|
|
193
196
|
}
|
|
197
|
+
|
|
198
|
+
class PivotPointMesh extends Mesh {
|
|
199
|
+
constructor(size = 15, thickness = 3) {
|
|
200
|
+
super(new PlaneGeometry(0, 0), new PivotMaterial(size, thickness));
|
|
201
|
+
this.renderOrder = Infinity;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
set focus(value) {
|
|
205
|
+
this.material.uniforms.opacity.value = value ? 1 : 0.5;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
onBeforeRender(renderer) {
|
|
209
|
+
renderer.getSize(this.material.uniforms.resolution.value);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
updateMatrixWorld() {
|
|
213
|
+
this.matrixWorld.makeTranslation(this.position);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
dispose() {
|
|
217
|
+
this.geometry.dispose();
|
|
218
|
+
this.material.dispose();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
class PivotMaterial extends ShaderMaterial {
|
|
223
|
+
constructor(size, thickness) {
|
|
224
|
+
const coreD = size + thickness;
|
|
225
|
+
const planeD = coreD + 3 * thickness;
|
|
226
|
+
const normThk = thickness / coreD;
|
|
227
|
+
const ringR = (coreD - 0.4 * thickness - 4.0) / coreD;
|
|
228
|
+
const hw = 0.4 * normThk;
|
|
229
|
+
|
|
230
|
+
super({
|
|
231
|
+
depthWrite: false,
|
|
232
|
+
depthTest: false,
|
|
233
|
+
transparent: true,
|
|
234
|
+
|
|
235
|
+
uniforms: {
|
|
236
|
+
resolution: { value: new Vector2() },
|
|
237
|
+
opacity: { value: 1 },
|
|
238
|
+
planeD: { value: planeD },
|
|
239
|
+
hw: { value: hw },
|
|
240
|
+
ringR: { value: ringR },
|
|
241
|
+
shadowW: { value: hw * 5.0 },
|
|
242
|
+
uvScale: { value: planeD / coreD },
|
|
243
|
+
},
|
|
244
|
+
|
|
245
|
+
vertexShader: `
|
|
246
|
+
uniform float planeD;
|
|
247
|
+
uniform vec2 resolution;
|
|
248
|
+
varying vec2 vUv;
|
|
249
|
+
|
|
250
|
+
void main() {
|
|
251
|
+
vUv = uv;
|
|
252
|
+
float aspect = resolution.x / resolution.y;
|
|
253
|
+
vec2 offset = uv * 2.0 - vec2(1.0);
|
|
254
|
+
offset.y *= aspect;
|
|
255
|
+
vec4 screenPoint = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
256
|
+
screenPoint.xy += offset * planeD * screenPoint.w / resolution.x;
|
|
257
|
+
gl_Position = screenPoint;
|
|
258
|
+
}
|
|
259
|
+
`,
|
|
260
|
+
fragmentShader: `
|
|
261
|
+
uniform float hw;
|
|
262
|
+
uniform float ringR;
|
|
263
|
+
uniform float shadowW;
|
|
264
|
+
uniform float opacity;
|
|
265
|
+
uniform float uvScale;
|
|
266
|
+
varying vec2 vUv;
|
|
267
|
+
|
|
268
|
+
void main() {
|
|
269
|
+
vec2 uv = (vUv * 2.0 - 1.0) * uvScale;
|
|
270
|
+
float len = length(uv);
|
|
271
|
+
float fw = fwidth(len) * 0.5;
|
|
272
|
+
float d = abs(len - ringR);
|
|
273
|
+
|
|
274
|
+
float ring = 1.0 - smoothstep(hw - fw, hw + fw, d);
|
|
275
|
+
|
|
276
|
+
float shadow = (1.0 - smoothstep(hw, shadowW, d)) * (1.0 - smoothstep(ringR - fw, ringR + fw, len)) * 0.5;
|
|
277
|
+
|
|
278
|
+
float white = ring;
|
|
279
|
+
float black = shadow * (1.0 - white);
|
|
280
|
+
float alpha = (white + black) * opacity;
|
|
281
|
+
if (alpha < 0.001) discard;
|
|
282
|
+
gl_FragColor = vec4(vec3(white / max(alpha / opacity, 0.001)), alpha);
|
|
283
|
+
}
|
|
284
|
+
`,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
194
289
|
const _matrix = new Matrix4();
|
|
195
290
|
// custom version of set raycaster from camera that relies on the underlying matrices
|
|
196
291
|
// so the ray origin is position at the camera near clip.
|
|
@@ -240,6 +335,8 @@ const UPDATE_EVENT = { type: 'update' };
|
|
|
240
335
|
const FINISH_EVENT = { type: 'finish' };
|
|
241
336
|
const THRESHOLD = 1e-3;
|
|
242
337
|
const MAX = 1e8;
|
|
338
|
+
const PIVOT_SIZE = 22;
|
|
339
|
+
const PIVOT_THICKNESS = 2.5;
|
|
243
340
|
const VIRTUAL_HIT_DISTANCE = 50;
|
|
244
341
|
const ZOOM_OUT_TRANSITION_COS_THRESHOLD = Math.cos((105 * Math.PI) / 180);
|
|
245
342
|
const ZOOM_OUT_TRANSITION_COS_MAX_THRESHOLD = Math.cos((95 * Math.PI) / 180);
|
|
@@ -281,6 +378,7 @@ class CameraController extends EventDispatcher {
|
|
|
281
378
|
#camera;
|
|
282
379
|
#scene;
|
|
283
380
|
#raycaster;
|
|
381
|
+
#pivotMesh;
|
|
284
382
|
#zoomDelta;
|
|
285
383
|
#zoomInertia;
|
|
286
384
|
#rotateInertia;
|
|
@@ -330,6 +428,8 @@ class CameraController extends EventDispatcher {
|
|
|
330
428
|
this.#pointerTracker = new PointerTracker();
|
|
331
429
|
this.#raycaster = new Raycaster();
|
|
332
430
|
this.#raycaster.params.Points.threshold = 0.1;
|
|
431
|
+
this.#pivotMesh = new PivotPointMesh(PIVOT_SIZE, PIVOT_THICKNESS);
|
|
432
|
+
this.#pivotMesh.visible = false;
|
|
333
433
|
this.#zoomDelta = 0;
|
|
334
434
|
this.#zoomInertia = 0;
|
|
335
435
|
this.#rotateInertia = new Vector2();
|
|
@@ -368,13 +468,22 @@ class CameraController extends EventDispatcher {
|
|
|
368
468
|
get camera() {
|
|
369
469
|
return this.#camera;
|
|
370
470
|
}
|
|
471
|
+
get indicator() {
|
|
472
|
+
return this.#pivotMesh;
|
|
473
|
+
}
|
|
371
474
|
#setState(state = this.state) {
|
|
372
475
|
if (this.state === state) {
|
|
373
476
|
return;
|
|
374
477
|
}
|
|
375
478
|
this.state = state;
|
|
479
|
+
if (state !== NONE) {
|
|
480
|
+
this.#pivotMesh.visible = true;
|
|
481
|
+
}
|
|
376
482
|
}
|
|
377
483
|
#setZooming(zooming, touchZooming = false) {
|
|
484
|
+
if (!this.zooming && this.state === NONE && zooming) {
|
|
485
|
+
this.#pivotMesh.visible = true;
|
|
486
|
+
}
|
|
378
487
|
this.zooming = zooming;
|
|
379
488
|
this.touchZooming = touchZooming;
|
|
380
489
|
}
|
|
@@ -391,6 +500,7 @@ class CameraController extends EventDispatcher {
|
|
|
391
500
|
this.#dragPlaneNormal.set(0, 0, 0);
|
|
392
501
|
this.#zoomInertia = 0;
|
|
393
502
|
this.#hit = null;
|
|
503
|
+
this.#pivotMesh.visible = false;
|
|
394
504
|
}
|
|
395
505
|
setCamera(camera) {
|
|
396
506
|
this.#camera = camera;
|
|
@@ -405,6 +515,8 @@ class CameraController extends EventDispatcher {
|
|
|
405
515
|
}
|
|
406
516
|
init() {
|
|
407
517
|
this.#domElement.style.touchAction = 'none';
|
|
518
|
+
this.#pivotMesh.raycast = () => {};
|
|
519
|
+
this.#scene.add(this.#pivotMesh);
|
|
408
520
|
this.#contextMenuEvent = this.#contextMenu.bind(this);
|
|
409
521
|
this.#pointerDownEvent = this.#pointerDown.bind(this);
|
|
410
522
|
this.#pointerMoveEvent = this.#pointerMove.bind(this);
|
|
@@ -600,6 +712,8 @@ class CameraController extends EventDispatcher {
|
|
|
600
712
|
'pointerenter',
|
|
601
713
|
this.#pointerEnterEvent,
|
|
602
714
|
);
|
|
715
|
+
this.#pivotMesh.removeFromParent();
|
|
716
|
+
this.#pivotMesh.dispose();
|
|
603
717
|
this.#domElement.style.touchAction = '';
|
|
604
718
|
this.#enabled = false;
|
|
605
719
|
this.#ellipsoid = null;
|
|
@@ -615,6 +729,15 @@ class CameraController extends EventDispatcher {
|
|
|
615
729
|
#contextMenu = (e) => {
|
|
616
730
|
e.preventDefault();
|
|
617
731
|
};
|
|
732
|
+
#updateIndicatorFromHit() {
|
|
733
|
+
if (this.#hit && this.#hit.distance > 0) {
|
|
734
|
+
this.#pivotMesh.visible = true;
|
|
735
|
+
this.#pivotMesh.position.copy(this.#hit.point);
|
|
736
|
+
this.#pivotMesh.focus = !this.#hit.onGlobe;
|
|
737
|
+
} else {
|
|
738
|
+
this.#pivotMesh.visible = false;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
618
741
|
#pointerDown = (e) => {
|
|
619
742
|
if (!this.#enabled) {
|
|
620
743
|
return;
|
|
@@ -653,6 +776,7 @@ class CameraController extends EventDispatcher {
|
|
|
653
776
|
mouseToCoords(_pointer1.x, _pointer1.y, this.#domElement, _pointer1);
|
|
654
777
|
setRaycasterFromCamera(this.#raycaster, _pointer1, this.#camera);
|
|
655
778
|
this.#hit = this.#raycast(this.#raycaster);
|
|
779
|
+
this.#updateIndicatorFromHit();
|
|
656
780
|
if (this.state === DRAG && this.#hit.distance > 0) {
|
|
657
781
|
this.#initializeDragAnchor();
|
|
658
782
|
}
|
|
@@ -689,9 +813,9 @@ class CameraController extends EventDispatcher {
|
|
|
689
813
|
if (!this.#enabled) {
|
|
690
814
|
return;
|
|
691
815
|
}
|
|
692
|
-
const tooClose =
|
|
693
|
-
|
|
694
|
-
|
|
816
|
+
const tooClose =
|
|
817
|
+
this.#pivotMesh.position.distanceTo(this.#camera.position) <=
|
|
818
|
+
this.#camera.near;
|
|
695
819
|
if (!this.zooming || tooClose) {
|
|
696
820
|
this.#rotateInertia.set(0, 0);
|
|
697
821
|
this.#dragInertia.set(0, 0, 0);
|
|
@@ -705,6 +829,7 @@ class CameraController extends EventDispatcher {
|
|
|
705
829
|
setRaycasterFromCamera(this.#raycaster, _pointer1, this.#camera);
|
|
706
830
|
if ((!this.zooming && this.state === NONE) || tooClose) {
|
|
707
831
|
this.#hit = this.#raycast(this.#raycaster);
|
|
832
|
+
this.#updateIndicatorFromHit();
|
|
708
833
|
}
|
|
709
834
|
let delta = 0;
|
|
710
835
|
switch (e.deltaMode) {
|
|
@@ -1056,6 +1181,7 @@ class CameraController extends EventDispatcher {
|
|
|
1056
1181
|
.copy(this.#camera.position)
|
|
1057
1182
|
.addScaledVector(_forward, VIRTUAL_HIT_DISTANCE);
|
|
1058
1183
|
hit.distance = VIRTUAL_HIT_DISTANCE;
|
|
1184
|
+
this.#pivotMesh.position.copy(hit.point);
|
|
1059
1185
|
}
|
|
1060
1186
|
let zoomFactor = Math.exp(-zoomAmount * 0.001);
|
|
1061
1187
|
if (this.minDistance > 0 && zoomFactor < 1) {
|
package/src/viewer/session.js
CHANGED
|
@@ -344,9 +344,7 @@ function scaleTilesetGeometricErrors(
|
|
|
344
344
|
tile,
|
|
345
345
|
geometricErrorScale,
|
|
346
346
|
geometricErrorLayerScale,
|
|
347
|
-
|
|
348
|
-
rootDir,
|
|
349
|
-
leafGeometricErrorCache,
|
|
347
|
+
leafGeometricError,
|
|
350
348
|
pathLabel = 'tileset.root',
|
|
351
349
|
) {
|
|
352
350
|
if (!tile || typeof tile !== 'object') {
|
|
@@ -358,13 +356,7 @@ function scaleTilesetGeometricErrors(
|
|
|
358
356
|
'geometricError',
|
|
359
357
|
geometricErrorScale,
|
|
360
358
|
geometricErrorLayerScale,
|
|
361
|
-
|
|
362
|
-
tile,
|
|
363
|
-
baseDir,
|
|
364
|
-
rootDir,
|
|
365
|
-
leafGeometricErrorCache,
|
|
366
|
-
new Set(),
|
|
367
|
-
),
|
|
359
|
+
leafGeometricError,
|
|
368
360
|
`${pathLabel}.geometricError`,
|
|
369
361
|
);
|
|
370
362
|
|
|
@@ -377,9 +369,7 @@ function scaleTilesetGeometricErrors(
|
|
|
377
369
|
child,
|
|
378
370
|
geometricErrorScale,
|
|
379
371
|
geometricErrorLayerScale,
|
|
380
|
-
|
|
381
|
-
rootDir,
|
|
382
|
-
leafGeometricErrorCache,
|
|
372
|
+
leafGeometricError,
|
|
383
373
|
`${pathLabel}.children[${index}]`,
|
|
384
374
|
);
|
|
385
375
|
});
|
|
@@ -428,6 +418,7 @@ function updateTilesetJsonFile(
|
|
|
428
418
|
rootDir,
|
|
429
419
|
rootTransform = null,
|
|
430
420
|
leafGeometricErrorCache = new Map(),
|
|
421
|
+
globalLeafGeometricError = null,
|
|
431
422
|
},
|
|
432
423
|
visited = new Set(),
|
|
433
424
|
) {
|
|
@@ -455,27 +446,30 @@ function updateTilesetJsonFile(
|
|
|
455
446
|
}
|
|
456
447
|
|
|
457
448
|
const tilesetDir = path.dirname(resolvedPath);
|
|
449
|
+
const effectiveLeafGeometricError =
|
|
450
|
+
globalLeafGeometricError == null
|
|
451
|
+
? getTileLeafGeometricError(
|
|
452
|
+
tileset.root,
|
|
453
|
+
tilesetDir,
|
|
454
|
+
rootDir,
|
|
455
|
+
leafGeometricErrorCache,
|
|
456
|
+
new Set(),
|
|
457
|
+
)
|
|
458
|
+
: globalLeafGeometricError;
|
|
459
|
+
|
|
458
460
|
scaleGeometricErrorValue(
|
|
459
461
|
tileset,
|
|
460
462
|
'geometricError',
|
|
461
463
|
geometricErrorScale,
|
|
462
464
|
geometricErrorLayerScale,
|
|
463
|
-
|
|
464
|
-
tileset.root,
|
|
465
|
-
tilesetDir,
|
|
466
|
-
rootDir,
|
|
467
|
-
leafGeometricErrorCache,
|
|
468
|
-
new Set(),
|
|
469
|
-
),
|
|
465
|
+
effectiveLeafGeometricError,
|
|
470
466
|
`${resolvedPath}.geometricError`,
|
|
471
467
|
);
|
|
472
468
|
scaleTilesetGeometricErrors(
|
|
473
469
|
tileset.root,
|
|
474
470
|
geometricErrorScale,
|
|
475
471
|
geometricErrorLayerScale,
|
|
476
|
-
|
|
477
|
-
rootDir,
|
|
478
|
-
leafGeometricErrorCache,
|
|
472
|
+
effectiveLeafGeometricError,
|
|
479
473
|
`${resolvedPath}.root`,
|
|
480
474
|
);
|
|
481
475
|
writeJsonAtomic(resolvedPath, tileset);
|
|
@@ -489,6 +483,7 @@ function updateTilesetJsonFile(
|
|
|
489
483
|
geometricErrorLayerScale,
|
|
490
484
|
geometricErrorScale,
|
|
491
485
|
leafGeometricErrorCache,
|
|
486
|
+
globalLeafGeometricError: effectiveLeafGeometricError,
|
|
492
487
|
rootDir,
|
|
493
488
|
},
|
|
494
489
|
visited,
|
|
@@ -905,6 +900,24 @@ function buildViewerHtml(viewerConfig) {
|
|
|
905
900
|
pointer-events: none;
|
|
906
901
|
}
|
|
907
902
|
|
|
903
|
+
.tile-runtime-stats {
|
|
904
|
+
position: fixed;
|
|
905
|
+
right: 16px;
|
|
906
|
+
bottom: 16px;
|
|
907
|
+
display: flex;
|
|
908
|
+
flex-wrap: nowrap;
|
|
909
|
+
justify-content: flex-end;
|
|
910
|
+
gap: 14px;
|
|
911
|
+
box-sizing: border-box;
|
|
912
|
+
max-width: calc(100vw - 32px);
|
|
913
|
+
padding: 4px 8px;
|
|
914
|
+
border-radius: 6px;
|
|
915
|
+
background: rgba(255, 255, 255, 0.15);
|
|
916
|
+
backdrop-filter: blur(6px);
|
|
917
|
+
z-index: 12;
|
|
918
|
+
pointer-events: none;
|
|
919
|
+
}
|
|
920
|
+
|
|
908
921
|
.runtime-stat {
|
|
909
922
|
display: grid;
|
|
910
923
|
gap: 4px;
|
|
@@ -917,6 +930,19 @@ function buildViewerHtml(viewerConfig) {
|
|
|
917
930
|
backdrop-filter: blur(14px);
|
|
918
931
|
}
|
|
919
932
|
|
|
933
|
+
.tile-runtime-stats .runtime-stat {
|
|
934
|
+
display: inline-flex;
|
|
935
|
+
align-items: center;
|
|
936
|
+
gap: 0;
|
|
937
|
+
min-width: 0;
|
|
938
|
+
padding: 0;
|
|
939
|
+
border: 0;
|
|
940
|
+
border-radius: 0;
|
|
941
|
+
background: transparent;
|
|
942
|
+
box-shadow: none;
|
|
943
|
+
backdrop-filter: none;
|
|
944
|
+
}
|
|
945
|
+
|
|
920
946
|
.runtime-stat-label {
|
|
921
947
|
margin: 0;
|
|
922
948
|
font-size: 10px;
|
|
@@ -926,6 +952,18 @@ function buildViewerHtml(viewerConfig) {
|
|
|
926
952
|
color: #5d738b;
|
|
927
953
|
}
|
|
928
954
|
|
|
955
|
+
.tile-runtime-stats .runtime-stat-label,
|
|
956
|
+
.tile-runtime-stats .runtime-stat-value {
|
|
957
|
+
display: inline-flex;
|
|
958
|
+
align-items: center;
|
|
959
|
+
font-size: 12px;
|
|
960
|
+
font-weight: 400;
|
|
961
|
+
letter-spacing: 0;
|
|
962
|
+
line-height: 14px;
|
|
963
|
+
text-transform: none;
|
|
964
|
+
color: #24292f;
|
|
965
|
+
}
|
|
966
|
+
|
|
929
967
|
.runtime-stat-value {
|
|
930
968
|
margin: 0;
|
|
931
969
|
font-size: 15px;
|
|
@@ -934,6 +972,10 @@ function buildViewerHtml(viewerConfig) {
|
|
|
934
972
|
color: #16324f;
|
|
935
973
|
}
|
|
936
974
|
|
|
975
|
+
.tile-runtime-stats .runtime-stat-value {
|
|
976
|
+
font-variant-numeric: tabular-nums;
|
|
977
|
+
}
|
|
978
|
+
|
|
937
979
|
canvas {
|
|
938
980
|
display: block;
|
|
939
981
|
}
|
|
@@ -1221,6 +1263,16 @@ function buildViewerHtml(viewerConfig) {
|
|
|
1221
1263
|
top: 16px;
|
|
1222
1264
|
right: 16px;
|
|
1223
1265
|
left: 16px;
|
|
1266
|
+
flex-wrap: wrap;
|
|
1267
|
+
justify-content: stretch;
|
|
1268
|
+
max-width: none;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
.tile-runtime-stats {
|
|
1272
|
+
right: 16px;
|
|
1273
|
+
bottom: 16px;
|
|
1274
|
+
left: 16px;
|
|
1275
|
+
flex-wrap: wrap;
|
|
1224
1276
|
justify-content: stretch;
|
|
1225
1277
|
max-width: none;
|
|
1226
1278
|
}
|
|
@@ -1230,6 +1282,11 @@ function buildViewerHtml(viewerConfig) {
|
|
|
1230
1282
|
min-width: 0;
|
|
1231
1283
|
}
|
|
1232
1284
|
|
|
1285
|
+
.tile-runtime-stats .runtime-stat {
|
|
1286
|
+
flex: 0 0 auto;
|
|
1287
|
+
min-width: 0;
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1233
1290
|
.toolbar-dock {
|
|
1234
1291
|
top: auto;
|
|
1235
1292
|
bottom: 16px;
|
|
@@ -1262,6 +1319,24 @@ function buildViewerHtml(viewerConfig) {
|
|
|
1262
1319
|
<p id="splats-count-value" class="runtime-stat-value">0</p>
|
|
1263
1320
|
</div>
|
|
1264
1321
|
</div>
|
|
1322
|
+
<div class="tile-runtime-stats" aria-live="polite">
|
|
1323
|
+
<div class="runtime-stat">
|
|
1324
|
+
<p class="runtime-stat-label">Downloading: </p>
|
|
1325
|
+
<p id="tiles-downloading-value" class="runtime-stat-value">0</p>
|
|
1326
|
+
</div>
|
|
1327
|
+
<div class="runtime-stat">
|
|
1328
|
+
<p class="runtime-stat-label">Parsing: </p>
|
|
1329
|
+
<p id="tiles-parsing-value" class="runtime-stat-value">0</p>
|
|
1330
|
+
</div>
|
|
1331
|
+
<div class="runtime-stat">
|
|
1332
|
+
<p class="runtime-stat-label">Loaded: </p>
|
|
1333
|
+
<p id="tiles-loaded-value" class="runtime-stat-value">0</p>
|
|
1334
|
+
</div>
|
|
1335
|
+
<div class="runtime-stat">
|
|
1336
|
+
<p class="runtime-stat-label">Visible: </p>
|
|
1337
|
+
<p id="tiles-visible-value" class="runtime-stat-value">0</p>
|
|
1338
|
+
</div>
|
|
1339
|
+
</div>
|
|
1265
1340
|
<div class="toolbar-dock expanded">
|
|
1266
1341
|
<button
|
|
1267
1342
|
id="toolbar-toggle"
|
|
@@ -1274,16 +1349,6 @@ function buildViewerHtml(viewerConfig) {
|
|
|
1274
1349
|
Hide Sidebar
|
|
1275
1350
|
</button>
|
|
1276
1351
|
<div id="toolbar" class="toolbar">
|
|
1277
|
-
<div class="toolbar-section">
|
|
1278
|
-
<div class="toolbar-section-header">
|
|
1279
|
-
<p class="toolbar-section-title">Transform</p>
|
|
1280
|
-
</div>
|
|
1281
|
-
<div class="transform-actions">
|
|
1282
|
-
<button id="translate" type="button">Translate</button>
|
|
1283
|
-
<button id="rotate" type="button">Rotate</button>
|
|
1284
|
-
<button id="set-position" class="full-span" type="button">Set Position</button>
|
|
1285
|
-
</div>
|
|
1286
|
-
</div>
|
|
1287
1352
|
<div class="toolbar-section">
|
|
1288
1353
|
<div class="toolbar-section-header">
|
|
1289
1354
|
<p class="toolbar-section-title">Canvas</p>
|
|
@@ -1294,6 +1359,16 @@ function buildViewerHtml(viewerConfig) {
|
|
|
1294
1359
|
<button id="move-to-tiles" type="button">Move To Tiles</button>
|
|
1295
1360
|
</div>
|
|
1296
1361
|
</div>
|
|
1362
|
+
<div class="toolbar-section">
|
|
1363
|
+
<div class="toolbar-section-header">
|
|
1364
|
+
<p class="toolbar-section-title">Transform</p>
|
|
1365
|
+
</div>
|
|
1366
|
+
<div class="transform-actions">
|
|
1367
|
+
<button id="translate" type="button">Translate</button>
|
|
1368
|
+
<button id="rotate" type="button">Rotate</button>
|
|
1369
|
+
<button id="set-position" class="full-span" type="button">Set Position</button>
|
|
1370
|
+
</div>
|
|
1371
|
+
</div>
|
|
1297
1372
|
<div class="toolbar-section">
|
|
1298
1373
|
<div class="toolbar-section-header">
|
|
1299
1374
|
<p class="toolbar-section-title">Coordinate</p>
|
|
@@ -1304,8 +1379,8 @@ function buildViewerHtml(viewerConfig) {
|
|
|
1304
1379
|
<label><span>Height</span><input id="height" type="number" step="any" value="0" /></label>
|
|
1305
1380
|
</div>
|
|
1306
1381
|
<div class="coordinate-actions">
|
|
1307
|
-
<button id="move-tiles-to-coordinate" class="wide" type="button">Move Tiles</button>
|
|
1308
1382
|
<button id="move-camera-to-coordinate" class="wide" type="button">Move Camera</button>
|
|
1383
|
+
<button id="move-tiles-to-coordinate" class="wide" type="button">Move Tiles</button>
|
|
1309
1384
|
</div>
|
|
1310
1385
|
</div>
|
|
1311
1386
|
<div class="toolbar-section">
|