@pascal-app/viewer 0.8.0 → 0.9.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.
- package/dist/components/error-boundary.d.ts +6 -0
- package/dist/components/error-boundary.d.ts.map +1 -1
- package/dist/components/error-boundary.js +6 -0
- package/dist/components/renderers/node-renderer.d.ts +6 -2
- package/dist/components/renderers/node-renderer.d.ts.map +1 -1
- package/dist/components/renderers/node-renderer.js +41 -22
- package/dist/components/renderers/parametric-node-renderer.d.ts +5 -0
- package/dist/components/renderers/parametric-node-renderer.d.ts.map +1 -0
- package/dist/components/renderers/parametric-node-renderer.js +37 -0
- package/dist/components/renderers/scene-renderer.d.ts +1 -1
- package/dist/components/renderers/scene-renderer.d.ts.map +1 -1
- package/dist/components/viewer/bvh-ecctrl.d.ts +77 -0
- package/dist/components/viewer/bvh-ecctrl.d.ts.map +1 -0
- package/dist/components/viewer/bvh-ecctrl.js +552 -0
- package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
- package/dist/components/viewer/frame-limiter.js +39 -4
- package/dist/components/viewer/glb-interactive.d.ts +41 -0
- package/dist/components/viewer/glb-interactive.d.ts.map +1 -0
- package/dist/components/viewer/glb-interactive.js +394 -0
- package/dist/components/viewer/glb-reference-nodes.d.ts +29 -0
- package/dist/components/viewer/glb-reference-nodes.d.ts.map +1 -0
- package/dist/components/viewer/glb-reference-nodes.js +80 -0
- package/dist/components/viewer/glb-replace-instances.d.ts +18 -0
- package/dist/components/viewer/glb-replace-instances.d.ts.map +1 -0
- package/dist/components/viewer/glb-replace-instances.js +61 -0
- package/dist/components/viewer/glb-scene.d.ts +54 -0
- package/dist/components/viewer/glb-scene.d.ts.map +1 -0
- package/dist/components/viewer/glb-scene.js +920 -0
- package/dist/components/viewer/glb-walkthrough-controller.d.ts +12 -0
- package/dist/components/viewer/glb-walkthrough-controller.d.ts.map +1 -0
- package/dist/components/viewer/glb-walkthrough-controller.js +357 -0
- package/dist/components/viewer/index.d.ts +56 -3
- package/dist/components/viewer/index.d.ts.map +1 -1
- package/dist/components/viewer/index.js +280 -65
- package/dist/components/viewer/lights.d.ts +1 -1
- package/dist/components/viewer/lights.d.ts.map +1 -1
- package/dist/components/viewer/lights.js +201 -43
- package/dist/components/viewer/perf-monitor.d.ts +1 -1
- package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
- package/dist/components/viewer/perf-monitor.js +115 -12
- package/dist/components/viewer/post-processing.d.ts +7 -1
- package/dist/components/viewer/post-processing.d.ts.map +1 -1
- package/dist/components/viewer/post-processing.js +395 -63
- package/dist/components/viewer/registered-systems.d.ts +12 -0
- package/dist/components/viewer/registered-systems.d.ts.map +1 -0
- package/dist/components/viewer/registered-systems.js +49 -0
- package/dist/components/viewer/scene-bvh.d.ts +18 -0
- package/dist/components/viewer/scene-bvh.d.ts.map +1 -0
- package/dist/components/viewer/scene-bvh.js +97 -0
- package/dist/components/viewer/scene-bvh.test.d.ts +2 -0
- package/dist/components/viewer/scene-bvh.test.d.ts.map +1 -0
- package/dist/components/viewer/scene-bvh.test.js +13 -0
- package/dist/components/viewer/scene-environment.d.ts +2 -0
- package/dist/components/viewer/scene-environment.d.ts.map +1 -0
- package/dist/components/viewer/scene-environment.js +79 -0
- package/dist/components/viewer/selection-manager.d.ts +1 -1
- package/dist/components/viewer/selection-manager.d.ts.map +1 -1
- package/dist/components/viewer/selection-manager.js +37 -14
- package/dist/components/viewer/viewer-camera.d.ts +1 -1
- package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
- package/dist/components/viewer/walkthrough-controls.d.ts +2 -1
- package/dist/components/viewer/walkthrough-controls.d.ts.map +1 -1
- package/dist/components/viewer/walkthrough-controls.js +20 -0
- package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
- package/dist/hooks/use-gltf-ktx2.js +3 -22
- package/dist/hooks/use-node-events.d.ts +6 -74
- package/dist/hooks/use-node-events.d.ts.map +1 -1
- package/dist/hooks/use-node-events.js +28 -8
- package/dist/index.d.ts +48 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +82 -4
- package/dist/lib/backdrop.d.ts +33 -0
- package/dist/lib/backdrop.d.ts.map +1 -0
- package/dist/lib/backdrop.js +92 -0
- package/dist/lib/box-uv.d.ts +13 -0
- package/dist/lib/box-uv.d.ts.map +1 -0
- package/dist/lib/box-uv.js +32 -0
- package/dist/lib/csg-utils.d.ts +10 -0
- package/dist/lib/csg-utils.d.ts.map +1 -0
- package/dist/lib/csg-utils.js +93 -0
- package/dist/lib/csg-utils.test.d.ts +2 -0
- package/dist/lib/csg-utils.test.d.ts.map +1 -0
- package/dist/lib/csg-utils.test.js +45 -0
- package/dist/lib/drawable-geometry.d.ts +21 -0
- package/dist/lib/drawable-geometry.d.ts.map +1 -0
- package/dist/lib/drawable-geometry.js +22 -0
- package/dist/lib/edge-style.d.ts +4 -0
- package/dist/lib/edge-style.d.ts.map +1 -0
- package/dist/lib/edge-style.js +31 -0
- package/dist/lib/gpu-perf.d.ts +4 -0
- package/dist/lib/gpu-perf.d.ts.map +1 -0
- package/dist/lib/gpu-perf.js +23 -0
- package/dist/lib/ink-edges.d.ts +9 -0
- package/dist/lib/ink-edges.d.ts.map +1 -0
- package/dist/lib/ink-edges.js +49 -0
- package/dist/lib/isolation.d.ts +39 -0
- package/dist/lib/isolation.d.ts.map +1 -0
- package/dist/lib/isolation.js +103 -0
- package/dist/lib/ktx2-loader.d.ts +43 -0
- package/dist/lib/ktx2-loader.d.ts.map +1 -0
- package/dist/lib/ktx2-loader.js +136 -0
- package/dist/lib/layers.d.ts +28 -0
- package/dist/lib/layers.d.ts.map +1 -1
- package/dist/lib/layers.js +28 -0
- package/dist/lib/materials.d.ts +38 -15
- package/dist/lib/materials.d.ts.map +1 -1
- package/dist/lib/materials.js +364 -72
- package/dist/lib/merged-outline-node.d.ts.map +1 -1
- package/dist/lib/merged-outline-node.js +7 -0
- package/dist/lib/polygon-union.d.ts +4 -0
- package/dist/lib/polygon-union.d.ts.map +1 -0
- package/dist/lib/polygon-union.js +289 -0
- package/dist/lib/polygon-union.test.d.ts +2 -0
- package/dist/lib/polygon-union.test.d.ts.map +1 -0
- package/dist/lib/polygon-union.test.js +105 -0
- package/dist/lib/scene-themes.d.ts +30 -0
- package/dist/lib/scene-themes.d.ts.map +1 -0
- package/dist/lib/scene-themes.js +202 -0
- package/dist/lib/shadow-only.d.ts +4 -0
- package/dist/lib/shadow-only.d.ts.map +1 -0
- package/dist/lib/shadow-only.js +36 -0
- package/dist/lib/tsl-compat.d.ts +12 -0
- package/dist/lib/tsl-compat.d.ts.map +1 -0
- package/dist/lib/tsl-compat.js +13 -0
- package/dist/store/use-viewer.d.ts +92 -12
- package/dist/store/use-viewer.d.ts.map +1 -1
- package/dist/store/use-viewer.js +201 -6
- package/dist/store/use-viewer.test.d.ts +2 -0
- package/dist/store/use-viewer.test.d.ts.map +1 -0
- package/dist/store/use-viewer.test.js +42 -0
- package/dist/systems/ceiling/ceiling-system.d.ts +6 -2
- package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
- package/dist/systems/ceiling/ceiling-system.js +66 -13
- package/dist/systems/door/door-animation.test.d.ts +2 -0
- package/dist/systems/door/door-animation.test.d.ts.map +1 -0
- package/dist/systems/door/door-animation.test.js +51 -0
- package/dist/systems/door/door-floor-alignment.test.d.ts +2 -0
- package/dist/systems/door/door-floor-alignment.test.d.ts.map +1 -0
- package/dist/systems/door/door-floor-alignment.test.js +50 -0
- package/dist/systems/door/door-system.d.ts +24 -0
- package/dist/systems/door/door-system.d.ts.map +1 -1
- package/dist/systems/door/door-system.js +864 -180
- package/dist/systems/elevator/elevator-interaction-system.d.ts +2 -0
- package/dist/systems/elevator/elevator-interaction-system.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-interaction-system.js +62 -0
- package/dist/systems/fence/fence-system.d.ts +20 -0
- package/dist/systems/fence/fence-system.d.ts.map +1 -1
- package/dist/systems/fence/fence-system.js +229 -57
- package/dist/systems/floor-elevation/floor-elevation-system.d.ts +20 -0
- package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -0
- package/dist/systems/floor-elevation/floor-elevation-system.js +78 -0
- package/dist/systems/geometry/geometry-system.d.ts +41 -0
- package/dist/systems/geometry/geometry-system.d.ts.map +1 -0
- package/dist/systems/geometry/geometry-system.js +348 -0
- package/dist/systems/geometry/geometry-system.test.d.ts +2 -0
- package/dist/systems/geometry/geometry-system.test.d.ts.map +1 -0
- package/dist/systems/geometry/geometry-system.test.js +30 -0
- package/dist/systems/interactive/control-widget.d.ts +10 -0
- package/dist/systems/interactive/control-widget.d.ts.map +1 -0
- package/dist/systems/interactive/control-widget.js +35 -0
- package/dist/systems/interactive/interactive-system.d.ts +1 -1
- package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
- package/dist/systems/interactive/interactive-system.js +55 -56
- package/dist/systems/item/item-system.d.ts +7 -0
- package/dist/systems/item/item-system.d.ts.map +1 -1
- package/dist/systems/item/item-system.js +28 -17
- package/dist/systems/item-light/item-light-system.d.ts +1 -1
- package/dist/systems/item-light/item-light-system.d.ts.map +1 -1
- package/dist/systems/item-light/item-light-system.js +18 -5
- package/dist/systems/level/level-system.d.ts.map +1 -1
- package/dist/systems/level/level-system.js +28 -4
- package/dist/systems/level/level-utils.d.ts +0 -3
- package/dist/systems/level/level-utils.d.ts.map +1 -1
- package/dist/systems/level/level-utils.js +2 -41
- package/dist/systems/roof/roof-materials.d.ts +3 -2
- package/dist/systems/roof/roof-materials.d.ts.map +1 -1
- package/dist/systems/roof/roof-materials.js +59 -14
- package/dist/systems/roof/roof-system.d.ts +46 -6
- package/dist/systems/roof/roof-system.d.ts.map +1 -1
- package/dist/systems/roof/roof-system.js +1365 -170
- package/dist/systems/slab/slab-system.d.ts +5 -3
- package/dist/systems/slab/slab-system.d.ts.map +1 -1
- package/dist/systems/slab/slab-system.js +204 -68
- package/dist/systems/slab/slab-system.test.d.ts +2 -0
- package/dist/systems/slab/slab-system.test.d.ts.map +1 -0
- package/dist/systems/slab/slab-system.test.js +63 -0
- package/dist/systems/stair/stair-materials.d.ts +4 -3
- package/dist/systems/stair/stair-materials.d.ts.map +1 -1
- package/dist/systems/stair/stair-materials.js +30 -15
- package/dist/systems/stair/stair-system.d.ts.map +1 -1
- package/dist/systems/stair/stair-system.js +55 -63
- package/dist/systems/surface-hole-geometry.d.ts +3 -0
- package/dist/systems/surface-hole-geometry.d.ts.map +1 -0
- package/dist/systems/surface-hole-geometry.js +4 -0
- package/dist/systems/wall/opening-cutout-geometry.d.ts +31 -0
- package/dist/systems/wall/opening-cutout-geometry.d.ts.map +1 -0
- package/dist/systems/wall/opening-cutout-geometry.js +167 -0
- package/dist/systems/wall/opening-cutout-geometry.test.d.ts +2 -0
- package/dist/systems/wall/opening-cutout-geometry.test.d.ts.map +1 -0
- package/dist/systems/wall/opening-cutout-geometry.test.js +172 -0
- package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout.js +62 -17
- package/dist/systems/wall/wall-materials.d.ts +12 -7
- package/dist/systems/wall/wall-materials.d.ts.map +1 -1
- package/dist/systems/wall/wall-materials.js +279 -46
- package/dist/systems/wall/wall-opening-cutout.test.d.ts +2 -0
- package/dist/systems/wall/wall-opening-cutout.test.d.ts.map +1 -0
- package/dist/systems/wall/wall-opening-cutout.test.js +59 -0
- package/dist/systems/wall/wall-support-extension.test.d.ts +2 -0
- package/dist/systems/wall/wall-support-extension.test.d.ts.map +1 -0
- package/dist/systems/wall/wall-support-extension.test.js +48 -0
- package/dist/systems/wall/wall-system.d.ts +2 -2
- package/dist/systems/wall/wall-system.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.js +493 -193
- package/dist/systems/window/window-animation-system.d.ts +12 -0
- package/dist/systems/window/window-animation-system.d.ts.map +1 -1
- package/dist/systems/window/window-animation-system.js +16 -5
- package/dist/systems/window/window-system.d.ts +7 -0
- package/dist/systems/window/window-system.d.ts.map +1 -1
- package/dist/systems/window/window-system.js +308 -18
- package/package.json +5 -6
- package/dist/components/renderers/building/building-renderer.d.ts +0 -5
- package/dist/components/renderers/building/building-renderer.d.ts.map +0 -1
- package/dist/components/renderers/building/building-renderer.js +0 -11
- package/dist/components/renderers/ceiling/ceiling-renderer.d.ts +0 -5
- package/dist/components/renderers/ceiling/ceiling-renderer.d.ts.map +0 -1
- package/dist/components/renderers/ceiling/ceiling-renderer.js +0 -58
- package/dist/components/renderers/column/column-renderer.d.ts +0 -5
- package/dist/components/renderers/column/column-renderer.d.ts.map +0 -1
- package/dist/components/renderers/column/column-renderer.js +0 -597
- package/dist/components/renderers/door/door-renderer.d.ts +0 -5
- package/dist/components/renderers/door/door-renderer.d.ts.map +0 -1
- package/dist/components/renderers/door/door-renderer.js +0 -16
- package/dist/components/renderers/fence/fence-renderer.d.ts +0 -5
- package/dist/components/renderers/fence/fence-renderer.d.ts.map +0 -1
- package/dist/components/renderers/fence/fence-renderer.js +0 -15
- package/dist/components/renderers/guide/guide-renderer.d.ts +0 -5
- package/dist/components/renderers/guide/guide-renderer.d.ts.map +0 -1
- package/dist/components/renderers/guide/guide-renderer.js +0 -38
- package/dist/components/renderers/item/item-renderer.d.ts +0 -5
- package/dist/components/renderers/item/item-renderer.d.ts.map +0 -1
- package/dist/components/renderers/item/item-renderer.js +0 -172
- package/dist/components/renderers/level/level-renderer.d.ts +0 -5
- package/dist/components/renderers/level/level-renderer.d.ts.map +0 -1
- package/dist/components/renderers/level/level-renderer.js +0 -11
- package/dist/components/renderers/roof/roof-materials.d.ts +0 -4
- package/dist/components/renderers/roof/roof-materials.d.ts.map +0 -1
- package/dist/components/renderers/roof/roof-materials.js +0 -16
- package/dist/components/renderers/roof/roof-renderer.d.ts +0 -5
- package/dist/components/renderers/roof/roof-renderer.d.ts.map +0 -1
- package/dist/components/renderers/roof/roof-renderer.js +0 -32
- package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts +0 -5
- package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts.map +0 -1
- package/dist/components/renderers/roof-segment/roof-segment-renderer.js +0 -40
- package/dist/components/renderers/scan/scan-renderer.d.ts +0 -5
- package/dist/components/renderers/scan/scan-renderer.d.ts.map +0 -1
- package/dist/components/renderers/scan/scan-renderer.js +0 -54
- package/dist/components/renderers/site/site-renderer.d.ts +0 -5
- package/dist/components/renderers/site/site-renderer.d.ts.map +0 -1
- package/dist/components/renderers/site/site-renderer.js +0 -106
- package/dist/components/renderers/slab/slab-renderer.d.ts +0 -5
- package/dist/components/renderers/slab/slab-renderer.d.ts.map +0 -1
- package/dist/components/renderers/slab/slab-renderer.js +0 -58
- package/dist/components/renderers/spawn/spawn-renderer.d.ts +0 -5
- package/dist/components/renderers/spawn/spawn-renderer.d.ts.map +0 -1
- package/dist/components/renderers/spawn/spawn-renderer.js +0 -32
- package/dist/components/renderers/stair/stair-renderer.d.ts +0 -5
- package/dist/components/renderers/stair/stair-renderer.d.ts.map +0 -1
- package/dist/components/renderers/stair/stair-renderer.js +0 -603
- package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts +0 -5
- package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts.map +0 -1
- package/dist/components/renderers/stair-segment/stair-segment-renderer.js +0 -51
- package/dist/components/renderers/wall/wall-renderer.d.ts +0 -5
- package/dist/components/renderers/wall/wall-renderer.d.ts.map +0 -1
- package/dist/components/renderers/wall/wall-renderer.js +0 -16
- package/dist/components/renderers/window/window-renderer.d.ts +0 -5
- package/dist/components/renderers/window/window-renderer.d.ts.map +0 -1
- package/dist/components/renderers/window/window-renderer.js +0 -21
- package/dist/components/renderers/zone/zone-renderer.d.ts +0 -5
- package/dist/components/renderers/zone/zone-renderer.d.ts.map +0 -1
- package/dist/components/renderers/zone/zone-renderer.js +0 -181
- package/dist/components/viewer/ground-occluder.d.ts +0 -2
- package/dist/components/viewer/ground-occluder.d.ts.map +0 -1
- package/dist/components/viewer/ground-occluder.js +0 -79
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* True when `geometry` has a bound, non-empty `position` attribute — i.e. it is
|
|
3
|
+
* safe to submit to the WebGPU renderer.
|
|
4
|
+
*
|
|
5
|
+
* A geometry whose `position` attribute has `count === 0` (or no `position` at
|
|
6
|
+
* all) leaves WebGPU **vertex buffer slot 0 unbound**. The validator rejects the
|
|
7
|
+
* draw with "Vertex buffer slot 0 … was not set", and — critically — that single
|
|
8
|
+
* rejected draw **poisons the entire command encoder**: every other draw in the
|
|
9
|
+
* frame (the whole scene + every editor overlay) is discarded on the next queue
|
|
10
|
+
* submit ("Invalid CommandBuffer"). The visible result is the whole canvas
|
|
11
|
+
* flickering/garbling, not just the offending mesh.
|
|
12
|
+
*
|
|
13
|
+
* Individual call-sites guard against *creating* empty geometry (see
|
|
14
|
+
* `createPlaceholderGeometry`, the ceiling/door degenerate fallbacks, etc.), but
|
|
15
|
+
* transient/derived geometries can still slip through. This predicate is the
|
|
16
|
+
* renderer-level safety net: skipping a count-0 draw is a no-op visually (it
|
|
17
|
+
* would draw nothing anyway) while keeping the command encoder healthy.
|
|
18
|
+
*/
|
|
19
|
+
export function hasDrawableGeometry(geometry) {
|
|
20
|
+
const position = geometry?.attributes?.position;
|
|
21
|
+
return Boolean(position && position.count > 0);
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edge-style.d.ts","sourceRoot":"","sources":["../../src/lib/edge-style.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAA;AAgBhD,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAWvD;AAID,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE9D"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
function lumaOf(background) {
|
|
2
|
+
const hex = background.replace('#', '');
|
|
3
|
+
const r = Number.parseInt(hex.slice(0, 2), 16) / 255;
|
|
4
|
+
const g = Number.parseInt(hex.slice(2, 4), 16) / 255;
|
|
5
|
+
const b = Number.parseInt(hex.slice(4, 6), 16) / 255;
|
|
6
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
7
|
+
}
|
|
8
|
+
// Ink line colour follows background luminance — light backgrounds get
|
|
9
|
+
// near-black lines (the rule Mapbox uses for label outlines). Dark backgrounds
|
|
10
|
+
// get a tint *derived from the background* rather than a near-white constant:
|
|
11
|
+
// full-white lines glow harshly against night/twilight scenes, while lifting
|
|
12
|
+
// the background's own hue toward white stays legible but reads native to the
|
|
13
|
+
// theme.
|
|
14
|
+
export function edgeColorFor(background) {
|
|
15
|
+
if (lumaOf(background) > 0.5)
|
|
16
|
+
return '#1a1d24';
|
|
17
|
+
const hex = background.replace('#', '');
|
|
18
|
+
let out = '#';
|
|
19
|
+
for (let i = 0; i < 3; i++) {
|
|
20
|
+
const c = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16) / 255;
|
|
21
|
+
out += Math.round((c + (1 - c) * 0.42) * 255)
|
|
22
|
+
.toString(16)
|
|
23
|
+
.padStart(2, '0');
|
|
24
|
+
}
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
// Dark scenes additionally run the ink slightly transparent — even a muted
|
|
28
|
+
// tint at full alpha reads as glowing wireframe against a low-luma backdrop.
|
|
29
|
+
export function edgeOpacityScaleFor(background) {
|
|
30
|
+
return lumaOf(background) > 0.5 ? 1 : 0.7;
|
|
31
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gpu-perf.d.ts","sourceRoot":"","sources":["../../src/lib/gpu-perf.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,oBAAoB,SACyD,CAAA;AAK1F,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAG9C;AAED,wBAAgB,eAAe,IAAI,MAAM,EAAE,CAK1C"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// GPU work-time measurement, gated by `?perf` in the URL.
|
|
2
|
+
//
|
|
3
|
+
// We can't use WebGPU timestamp queries here because the editor renders via
|
|
4
|
+
// a custom `RenderPipeline.render()` path that bypasses three.js's built-in
|
|
5
|
+
// timestamp infrastructure. Instead we use `device.queue.onSubmittedWorkDone()`,
|
|
6
|
+
// which resolves when the GPU finishes all submitted work — measuring the
|
|
7
|
+
// CPU→GPU-done delta gives a clean approximation of per-frame GPU duration
|
|
8
|
+
// regardless of which render path produced it.
|
|
9
|
+
export const PERF_OVERLAY_ENABLED = typeof window !== 'undefined' && new URLSearchParams(window.location.search).has('perf');
|
|
10
|
+
const MAX_SAMPLES = 256;
|
|
11
|
+
const samples = [];
|
|
12
|
+
export function pushGpuSample(ms) {
|
|
13
|
+
samples.push(ms);
|
|
14
|
+
if (samples.length > MAX_SAMPLES)
|
|
15
|
+
samples.shift();
|
|
16
|
+
}
|
|
17
|
+
export function drainGpuSamples() {
|
|
18
|
+
if (samples.length === 0)
|
|
19
|
+
return [];
|
|
20
|
+
const out = samples.slice();
|
|
21
|
+
samples.length = 0;
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ink-edges.d.ts","sourceRoot":"","sources":["../../src/lib/ink-edges.ts"],"names":[],"mappings":"AAoBA,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,MAAM,EACN,OAAO,EACP,QAAQ,GACT,EAAE;IACD,QAAQ,EAAE,GAAG,CAAA;IACb,SAAS,EAAE,GAAG,CAAA;IACd,QAAQ,EAAE,GAAG,CAAA;IAKb,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,GAAG,CAAA;IACZ,QAAQ,EAAE,GAAG,CAAA;CACd,OAsCA"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { abs, float, max, min, mix, screenSize, screenUV, smoothstep, vec2 } from 'three/tsl';
|
|
2
|
+
import { unpackRGBToNormal } from './tsl-compat';
|
|
3
|
+
// Screen-space ink outline (SketchUp / Moebius look). Reads the scene-pass
|
|
4
|
+
// depth + normal MRT and inks two signals:
|
|
5
|
+
//
|
|
6
|
+
// 1. Crease — center normal vs each neighbour (`1 - dot`): 0 on a flat
|
|
7
|
+
// surface, ~1 at a 90° corner. Catches wall↔roof, wall↔wall and window-
|
|
8
|
+
// reveal creases. Normals are re-normalized because the 8-bit normal MRT
|
|
9
|
+
// decodes to non-unit vectors, which otherwise crushes the signal.
|
|
10
|
+
// 2. Depth — raw-depth Laplacian (screen-linear across planes → ~0 on flat
|
|
11
|
+
// ground, no banding) normalized by (1 - depth)² so it becomes ≈
|
|
12
|
+
// worldStep / near, i.e. DISTANCE-INDEPENDENT (a window reveal reads the
|
|
13
|
+
// same zoomed in or out). A raw-Laplacian gate rejects flat-plane
|
|
14
|
+
// quantization noise so the far ground/roof never inks.
|
|
15
|
+
//
|
|
16
|
+
// Topology-agnostic: works on CSG triangle soup, organic GLBs, anything — it
|
|
17
|
+
// only sees the rendered buffers. `intensity` scales the final mask; `inkColor`
|
|
18
|
+
// should track the background luminance (dark lines on light scenes).
|
|
19
|
+
export function inkedEdges({ depthTex, normalTex, inkColor, radius, opacity, sceneRgb, }) {
|
|
20
|
+
const px = vec2(1, 1).div(screenSize).mul(radius);
|
|
21
|
+
const uvN = screenUV;
|
|
22
|
+
const dC = depthTex.sample(uvN).r;
|
|
23
|
+
const dR = depthTex.sample(uvN.add(vec2(px.x, 0))).r;
|
|
24
|
+
const dL = depthTex.sample(uvN.sub(vec2(px.x, 0))).r;
|
|
25
|
+
const dU = depthTex.sample(uvN.add(vec2(0, px.y))).r;
|
|
26
|
+
const dD = depthTex.sample(uvN.sub(vec2(0, px.y))).r;
|
|
27
|
+
const depthLap = abs(dR.add(dL).add(dU).add(dD).sub(dC.mul(4)));
|
|
28
|
+
const invDepth = float(1).sub(dC);
|
|
29
|
+
const depthMetric = depthLap.div(invDepth.mul(invDepth).add(float(0.00002)));
|
|
30
|
+
const noiseGate = smoothstep(float(0.00002), float(0.00006), depthLap);
|
|
31
|
+
// ≈ metres of step / near (near≈0.1): ~5cm starts a line, ~25cm solid.
|
|
32
|
+
const depthEdge = smoothstep(float(0.5), float(2.5), depthMetric).mul(noiseGate);
|
|
33
|
+
const nC = unpackRGBToNormal(normalTex.sample(uvN)).normalize();
|
|
34
|
+
const nR = unpackRGBToNormal(normalTex.sample(uvN.add(vec2(px.x, 0)))).normalize();
|
|
35
|
+
const nL = unpackRGBToNormal(normalTex.sample(uvN.sub(vec2(px.x, 0)))).normalize();
|
|
36
|
+
const nU = unpackRGBToNormal(normalTex.sample(uvN.add(vec2(0, px.y)))).normalize();
|
|
37
|
+
const nD = unpackRGBToNormal(normalTex.sample(uvN.sub(vec2(0, px.y)))).normalize();
|
|
38
|
+
// Ink is a near/mid-field affordance: fade it out with raw depth so the
|
|
39
|
+
// horizon (the infinite ground disc vanishing against the backdrop) and
|
|
40
|
+
// other far-field depth cliffs never draw a line across the sky junction.
|
|
41
|
+
// ~full ink below ≈150 m, none beyond ≈350 m (perspective near 0.1/far 1000).
|
|
42
|
+
const distanceFade = float(1).sub(smoothstep(float(0.9994), float(0.9998), dC));
|
|
43
|
+
const nDiff = max(max(float(1).sub(nC.dot(nR)), float(1).sub(nC.dot(nL))), max(float(1).sub(nC.dot(nU)), float(1).sub(nC.dot(nD))));
|
|
44
|
+
const normalEdge = smoothstep(float(0.01), float(0.05), nDiff);
|
|
45
|
+
// TSL's typed overloads are finicky across versions; the runtime is proven in
|
|
46
|
+
// the aesthetic sandbox, so cast at the mask/mix boundary.
|
|
47
|
+
const edgeMask = min(max(depthEdge, normalEdge).mul(opacity).mul(distanceFade), float(1));
|
|
48
|
+
return mix(sceneRgb, inkColor, edgeMask);
|
|
49
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { AnyNodeId } from '@pascal-app/core';
|
|
2
|
+
import type { Object3D } from 'three';
|
|
3
|
+
/** True while an isolation filter is applied (see {@link applyIsolation}). */
|
|
4
|
+
export declare function isIsolationActive(): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Compute the union of every isolated subtree's `Object3D` descendants.
|
|
7
|
+
*
|
|
8
|
+
* Pure traversal — exported so future "focus mode" / debug tooling can
|
|
9
|
+
* reuse the same definition of "what's in the isolated set". Each root
|
|
10
|
+
* is walked via `Object3D.traverse` (the live Three.js graph, not the
|
|
11
|
+
* data-model `children` array — those can disagree when systems mount
|
|
12
|
+
* synthesized sub-meshes that the data model doesn't track).
|
|
13
|
+
*/
|
|
14
|
+
export declare function collectIsolationSubtree(ids: ReadonlyArray<string>): Set<Object3D>;
|
|
15
|
+
/**
|
|
16
|
+
* Imperative visibility filter on the live `sceneRegistry`. Hides every
|
|
17
|
+
* registered group (and its synthesized child meshes) outside the
|
|
18
|
+
* isolated subtree by disabling the {@link SCENE_LAYER} bit on the
|
|
19
|
+
* relevant `Object3D.layers` masks.
|
|
20
|
+
*
|
|
21
|
+
* Why layers instead of `obj.visible = false`? Three.js's visibility
|
|
22
|
+
* flag *cascades* — hiding a parent hides every descendant — so we
|
|
23
|
+
* can't hide a host wall while keeping a door rendered inside it.
|
|
24
|
+
* Layer masks are per-object and don't cascade: `WebGLRenderer
|
|
25
|
+
* .projectObject` skips objects whose layer mask doesn't intersect the
|
|
26
|
+
* camera's, but always recurses into their children. So we can disable
|
|
27
|
+
* `SCENE_LAYER` on the wall and the door (hosted under it in the
|
|
28
|
+
* scene graph) still renders, with its local position relative to the
|
|
29
|
+
* wall preserved automatically by the matrix walk.
|
|
30
|
+
*
|
|
31
|
+
* The original `layers.mask` is stashed under a private Symbol so
|
|
32
|
+
* {@link clearIsolation} can restore the exact prior state.
|
|
33
|
+
*
|
|
34
|
+
* Pass `null` to clear isolation (equivalent to calling
|
|
35
|
+
* {@link clearIsolation}).
|
|
36
|
+
*/
|
|
37
|
+
export declare function applyIsolation(ids: ReadonlyArray<AnyNodeId> | null): void;
|
|
38
|
+
export declare function clearIsolation(): void;
|
|
39
|
+
//# sourceMappingURL=isolation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isolation.d.ts","sourceRoot":"","sources":["../../src/lib/isolation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAEjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAgBrC,8EAA8E;AAC9E,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAC,CAUjF;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,CAAC,SAAS,CAAC,GAAG,IAAI,GAAG,IAAI,CAiBzE;AAcD,wBAAgB,cAAc,IAAI,IAAI,CAcrC"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { sceneRegistry } from '@pascal-app/core';
|
|
3
|
+
import { SCENE_LAYER } from './layers';
|
|
4
|
+
// Marker on each Object3D we modify during isolation so we can restore
|
|
5
|
+
// the original `layers.mask` bitfield. Stored under a `Symbol` so it
|
|
6
|
+
// can't collide with any kind's own userData fields.
|
|
7
|
+
const ORIGINAL_LAYERS = Symbol('isolation:original-layers');
|
|
8
|
+
// Whether a subtree is currently isolated (some objects have SCENE_LAYER
|
|
9
|
+
// disabled). Read by consumers that must not act on the partial view — e.g.
|
|
10
|
+
// the project-thumbnail autosave skips capturing while isolated so it never
|
|
11
|
+
// snapshots a single focused item as the whole project's thumbnail.
|
|
12
|
+
let isolationActive = false;
|
|
13
|
+
/** True while an isolation filter is applied (see {@link applyIsolation}). */
|
|
14
|
+
export function isIsolationActive() {
|
|
15
|
+
return isolationActive;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Compute the union of every isolated subtree's `Object3D` descendants.
|
|
19
|
+
*
|
|
20
|
+
* Pure traversal — exported so future "focus mode" / debug tooling can
|
|
21
|
+
* reuse the same definition of "what's in the isolated set". Each root
|
|
22
|
+
* is walked via `Object3D.traverse` (the live Three.js graph, not the
|
|
23
|
+
* data-model `children` array — those can disagree when systems mount
|
|
24
|
+
* synthesized sub-meshes that the data model doesn't track).
|
|
25
|
+
*/
|
|
26
|
+
export function collectIsolationSubtree(ids) {
|
|
27
|
+
const keep = new Set();
|
|
28
|
+
for (const id of ids) {
|
|
29
|
+
const root = sceneRegistry.nodes.get(id);
|
|
30
|
+
if (!root)
|
|
31
|
+
continue;
|
|
32
|
+
root.traverse((child) => {
|
|
33
|
+
keep.add(child);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return keep;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Imperative visibility filter on the live `sceneRegistry`. Hides every
|
|
40
|
+
* registered group (and its synthesized child meshes) outside the
|
|
41
|
+
* isolated subtree by disabling the {@link SCENE_LAYER} bit on the
|
|
42
|
+
* relevant `Object3D.layers` masks.
|
|
43
|
+
*
|
|
44
|
+
* Why layers instead of `obj.visible = false`? Three.js's visibility
|
|
45
|
+
* flag *cascades* — hiding a parent hides every descendant — so we
|
|
46
|
+
* can't hide a host wall while keeping a door rendered inside it.
|
|
47
|
+
* Layer masks are per-object and don't cascade: `WebGLRenderer
|
|
48
|
+
* .projectObject` skips objects whose layer mask doesn't intersect the
|
|
49
|
+
* camera's, but always recurses into their children. So we can disable
|
|
50
|
+
* `SCENE_LAYER` on the wall and the door (hosted under it in the
|
|
51
|
+
* scene graph) still renders, with its local position relative to the
|
|
52
|
+
* wall preserved automatically by the matrix walk.
|
|
53
|
+
*
|
|
54
|
+
* The original `layers.mask` is stashed under a private Symbol so
|
|
55
|
+
* {@link clearIsolation} can restore the exact prior state.
|
|
56
|
+
*
|
|
57
|
+
* Pass `null` to clear isolation (equivalent to calling
|
|
58
|
+
* {@link clearIsolation}).
|
|
59
|
+
*/
|
|
60
|
+
export function applyIsolation(ids) {
|
|
61
|
+
if (ids == null || ids.length === 0) {
|
|
62
|
+
clearIsolation();
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const keep = collectIsolationSubtree(ids);
|
|
66
|
+
// Iterate registered roots. For each one outside the keep set,
|
|
67
|
+
// disable `SCENE_LAYER` on it and on every descendant — *except*
|
|
68
|
+
// descendants that are themselves in `keep` (a kept node nested under
|
|
69
|
+
// a non-kept host: the isolated door under the hidden wall).
|
|
70
|
+
for (const [, obj] of sceneRegistry.nodes) {
|
|
71
|
+
if (keep.has(obj))
|
|
72
|
+
continue;
|
|
73
|
+
hideRecursive(obj, keep);
|
|
74
|
+
}
|
|
75
|
+
isolationActive = true;
|
|
76
|
+
}
|
|
77
|
+
function hideRecursive(obj, keep) {
|
|
78
|
+
if (keep.has(obj))
|
|
79
|
+
return;
|
|
80
|
+
const carrier = obj;
|
|
81
|
+
if (carrier[ORIGINAL_LAYERS] === undefined) {
|
|
82
|
+
carrier[ORIGINAL_LAYERS] = obj.layers.mask;
|
|
83
|
+
}
|
|
84
|
+
obj.layers.disable(SCENE_LAYER);
|
|
85
|
+
for (const child of obj.children) {
|
|
86
|
+
hideRecursive(child, keep);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export function clearIsolation() {
|
|
90
|
+
// We don't know which objects were touched without re-walking, so
|
|
91
|
+
// walk every registered root + its descendants and restore any
|
|
92
|
+
// stashed original-mask. `traverse` is cheap and idempotent here.
|
|
93
|
+
for (const [, obj] of sceneRegistry.nodes) {
|
|
94
|
+
obj.traverse((child) => {
|
|
95
|
+
const carrier = child;
|
|
96
|
+
if (carrier[ORIGINAL_LAYERS] !== undefined) {
|
|
97
|
+
child.layers.mask = carrier[ORIGINAL_LAYERS];
|
|
98
|
+
delete carrier[ORIGINAL_LAYERS];
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
isolationActive = false;
|
|
103
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader.js';
|
|
2
|
+
/**
|
|
3
|
+
* KTX2Loader that survives block-misaligned textures. WebGPU rejects
|
|
4
|
+
* block-compressed textures whose base dimensions aren't multiples of 4;
|
|
5
|
+
* three's KTX2Loader only warns and transcodes anyway, and the resulting
|
|
6
|
+
* invalid texture poisons every render pass that binds it (endless
|
|
7
|
+
* "Invalid BindGroup … Invalid CommandBuffer" spam — baked GLBs carrying an
|
|
8
|
+
* odd-sized user-item texture triggered exactly this). Such payloads are
|
|
9
|
+
* routed to a fallback loader whose support flags report no compressed
|
|
10
|
+
* formats, which makes the transcoder emit plain RGBA32 — uncompressed
|
|
11
|
+
* textures have no alignment requirement.
|
|
12
|
+
*/
|
|
13
|
+
declare class AlignmentSafeKTX2Loader extends KTX2Loader {
|
|
14
|
+
private rgbaFallback;
|
|
15
|
+
private fallbackLoader;
|
|
16
|
+
/** Both `load()` and GLTFLoader's KHR_texture_basisu path funnel through
|
|
17
|
+
* this internal — overriding it covers every entry point. */
|
|
18
|
+
_createTexture(buffer: ArrayBuffer, config?: Record<string, unknown>): Promise<unknown>;
|
|
19
|
+
dispose(): this;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Single shared KTX2 loader for the whole viewer — used both by the GLB loader
|
|
23
|
+
* (`use-gltf-ktx2`) and by catalog finish textures (`materials.ts`). KTX2 must
|
|
24
|
+
* be transcoded at load via the Basis WASM, and `detectSupport(renderer)` has to
|
|
25
|
+
* run once before any `.ktx2` is loaded so the loader picks a GPU format the
|
|
26
|
+
* device supports. `ensureKtx2Support` is idempotent per renderer and is called
|
|
27
|
+
* from the viewer root the moment the renderer is ready (even when no GLB is in
|
|
28
|
+
* the scene, so catalog `.ktx2` finishes still load).
|
|
29
|
+
*/
|
|
30
|
+
export declare const ktx2Loader: AlignmentSafeKTX2Loader;
|
|
31
|
+
/**
|
|
32
|
+
* Resolves once `detectSupport` has succeeded for any renderer. `.ktx2` loads
|
|
33
|
+
* issued before that point would throw inside KTX2Loader ("Missing
|
|
34
|
+
* initialization with `.detectSupport( renderer )`"), so texture loaders await
|
|
35
|
+
* this instead of failing — covers materials created while the renderer is
|
|
36
|
+
* still initializing (e.g. a standalone capture canvas).
|
|
37
|
+
*/
|
|
38
|
+
export declare function whenKtx2Ready(): Promise<void>;
|
|
39
|
+
/** Returns true once support has been detected for this renderer (KTX2 safe to load). */
|
|
40
|
+
export declare function ensureKtx2Support(renderer: unknown): boolean;
|
|
41
|
+
export declare function isKtx2Url(url: string): boolean;
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=ktx2-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ktx2-loader.d.ts","sourceRoot":"","sources":["../../src/lib/ktx2-loader.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAA;AAwBrE;;;;;;;;;;GAUG;AACH,cAAM,uBAAwB,SAAQ,UAAU;IAC9C,OAAO,CAAC,YAAY,CAA0B;IAE9C,OAAO,CAAC,cAAc;IAkBtB;kEAC8D;IACxD,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAmC7F,OAAO,IAAI,IAAI;CAMhB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,yBAAgC,CAAA;AAWvD;;;;;;GAMG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAE7C;AAED,yFAAyF;AACzF,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAkB5D;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE9C"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { DataTexture, LinearFilter, LinearMipmapLinearFilter, RGBAFormat, UnsignedByteType, } from 'three';
|
|
2
|
+
// Deep path, NOT the `Addons.js` aggregate: the aggregate re-exports
|
|
3
|
+
// LottieLoader/TTFLoader, whose CDN URL imports (`lottie-web`, `opentype.js`)
|
|
4
|
+
// crash bun's test runtime for every consumer of the viewer barrel.
|
|
5
|
+
import { KTX2Loader } from 'three/examples/jsm/loaders/KTX2Loader.js';
|
|
6
|
+
/** Base width/height of a Basis-supercompressed KTX2 payload needing transcode
|
|
7
|
+
* (vkFormat 0 = ETC1S/UASTC) whose dimensions aren't block-aligned, or null
|
|
8
|
+
* for anything safe. Little-endian u32s: vkFormat at byte 12, pixelWidth at
|
|
9
|
+
* 20, pixelHeight at 24. */
|
|
10
|
+
function readMisalignedBasisSize(buffer) {
|
|
11
|
+
if (buffer.byteLength < 28)
|
|
12
|
+
return null;
|
|
13
|
+
const view = new DataView(buffer);
|
|
14
|
+
const vkFormat = view.getUint32(12, true);
|
|
15
|
+
if (vkFormat !== 0)
|
|
16
|
+
return null;
|
|
17
|
+
const width = view.getUint32(20, true);
|
|
18
|
+
const height = view.getUint32(24, true);
|
|
19
|
+
return width % 4 !== 0 || height % 4 !== 0 ? { width, height } : null;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* KTX2Loader that survives block-misaligned textures. WebGPU rejects
|
|
23
|
+
* block-compressed textures whose base dimensions aren't multiples of 4;
|
|
24
|
+
* three's KTX2Loader only warns and transcodes anyway, and the resulting
|
|
25
|
+
* invalid texture poisons every render pass that binds it (endless
|
|
26
|
+
* "Invalid BindGroup … Invalid CommandBuffer" spam — baked GLBs carrying an
|
|
27
|
+
* odd-sized user-item texture triggered exactly this). Such payloads are
|
|
28
|
+
* routed to a fallback loader whose support flags report no compressed
|
|
29
|
+
* formats, which makes the transcoder emit plain RGBA32 — uncompressed
|
|
30
|
+
* textures have no alignment requirement.
|
|
31
|
+
*/
|
|
32
|
+
class AlignmentSafeKTX2Loader extends KTX2Loader {
|
|
33
|
+
rgbaFallback = null;
|
|
34
|
+
fallbackLoader() {
|
|
35
|
+
if (!this.rgbaFallback) {
|
|
36
|
+
this.rgbaFallback = new KTX2Loader(this.manager);
|
|
37
|
+
this.rgbaFallback.setTranscoderPath(this.transcoderPath);
|
|
38
|
+
this.rgbaFallback.workerConfig = {
|
|
39
|
+
astcSupported: false,
|
|
40
|
+
astcHDRSupported: false,
|
|
41
|
+
etc1Supported: false,
|
|
42
|
+
etc2Supported: false,
|
|
43
|
+
dxtSupported: false,
|
|
44
|
+
bptcSupported: false,
|
|
45
|
+
pvrtcSupported: false,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return this.rgbaFallback;
|
|
49
|
+
}
|
|
50
|
+
/** Both `load()` and GLTFLoader's KHR_texture_basisu path funnel through
|
|
51
|
+
* this internal — overriding it covers every entry point. */
|
|
52
|
+
async _createTexture(buffer, config) {
|
|
53
|
+
const misaligned = readMisalignedBasisSize(buffer);
|
|
54
|
+
if (misaligned) {
|
|
55
|
+
console.warn(`[viewer] KTX2 texture is ${misaligned.width}x${misaligned.height} (not multiple-of-4); ` +
|
|
56
|
+
'transcoding uncompressed so WebGPU can create it.');
|
|
57
|
+
const transcoded = (await this.fallbackLoader()._createTexture(buffer, config));
|
|
58
|
+
// The RGBA32 result still arrives as a CompressedTexture; three's WebGPU
|
|
59
|
+
// texture upload treats every CompressedTexture as block-compressed and
|
|
60
|
+
// crashes looking up a block descriptor for rgba8. Repackage the decoded
|
|
61
|
+
// pixels as a plain DataTexture instead.
|
|
62
|
+
const mip = transcoded.mipmaps?.[0];
|
|
63
|
+
if (!mip)
|
|
64
|
+
return transcoded;
|
|
65
|
+
const texture = new DataTexture(mip.data, mip.width, mip.height, RGBAFormat, UnsignedByteType);
|
|
66
|
+
texture.colorSpace = transcoded.colorSpace;
|
|
67
|
+
texture.generateMipmaps = true;
|
|
68
|
+
texture.minFilter = LinearMipmapLinearFilter;
|
|
69
|
+
texture.magFilter = LinearFilter;
|
|
70
|
+
texture.needsUpdate = true;
|
|
71
|
+
transcoded.dispose();
|
|
72
|
+
return texture;
|
|
73
|
+
}
|
|
74
|
+
return KTX2Loader.prototype._createTexture.call(this, buffer, config);
|
|
75
|
+
}
|
|
76
|
+
dispose() {
|
|
77
|
+
this.rgbaFallback?.dispose();
|
|
78
|
+
this.rgbaFallback = null;
|
|
79
|
+
super.dispose();
|
|
80
|
+
return this;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Single shared KTX2 loader for the whole viewer — used both by the GLB loader
|
|
85
|
+
* (`use-gltf-ktx2`) and by catalog finish textures (`materials.ts`). KTX2 must
|
|
86
|
+
* be transcoded at load via the Basis WASM, and `detectSupport(renderer)` has to
|
|
87
|
+
* run once before any `.ktx2` is loaded so the loader picks a GPU format the
|
|
88
|
+
* device supports. `ensureKtx2Support` is idempotent per renderer and is called
|
|
89
|
+
* from the viewer root the moment the renderer is ready (even when no GLB is in
|
|
90
|
+
* the scene, so catalog `.ktx2` finishes still load).
|
|
91
|
+
*/
|
|
92
|
+
export const ktx2Loader = new AlignmentSafeKTX2Loader();
|
|
93
|
+
ktx2Loader.setTranscoderPath('https://cdn.jsdelivr.net/gh/pmndrs/drei-assets@master/basis/');
|
|
94
|
+
const configuredRenderers = new WeakSet();
|
|
95
|
+
const warnedRenderers = new WeakSet();
|
|
96
|
+
let resolveKtx2Ready;
|
|
97
|
+
const ktx2ReadyPromise = new Promise((resolve) => {
|
|
98
|
+
resolveKtx2Ready = resolve;
|
|
99
|
+
});
|
|
100
|
+
/**
|
|
101
|
+
* Resolves once `detectSupport` has succeeded for any renderer. `.ktx2` loads
|
|
102
|
+
* issued before that point would throw inside KTX2Loader ("Missing
|
|
103
|
+
* initialization with `.detectSupport( renderer )`"), so texture loaders await
|
|
104
|
+
* this instead of failing — covers materials created while the renderer is
|
|
105
|
+
* still initializing (e.g. a standalone capture canvas).
|
|
106
|
+
*/
|
|
107
|
+
export function whenKtx2Ready() {
|
|
108
|
+
return ktx2ReadyPromise;
|
|
109
|
+
}
|
|
110
|
+
/** Returns true once support has been detected for this renderer (KTX2 safe to load). */
|
|
111
|
+
export function ensureKtx2Support(renderer) {
|
|
112
|
+
const key = renderer;
|
|
113
|
+
if (!key)
|
|
114
|
+
return false;
|
|
115
|
+
if (configuredRenderers.has(key))
|
|
116
|
+
return true;
|
|
117
|
+
try {
|
|
118
|
+
;
|
|
119
|
+
ktx2Loader.detectSupport(renderer);
|
|
120
|
+
configuredRenderers.add(key);
|
|
121
|
+
resolveKtx2Ready();
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
// Some WebGPU flows can transiently call this before backend init; don't
|
|
126
|
+
// crash the scene — a later call (or the next render) retries.
|
|
127
|
+
if (!warnedRenderers.has(key)) {
|
|
128
|
+
console.warn('[viewer] Skipping KTX2 support detection for now.', error);
|
|
129
|
+
warnedRenderers.add(key);
|
|
130
|
+
}
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
export function isKtx2Url(url) {
|
|
135
|
+
return url.toLowerCase().endsWith('.ktx2');
|
|
136
|
+
}
|
package/dist/lib/layers.d.ts
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
/** Default Three.js layer for main scene geometry. */
|
|
2
2
|
export declare const SCENE_LAYER = 0;
|
|
3
|
+
/**
|
|
4
|
+
* Layer for editor-only overlays (gizmos, move handles, tool previews, grid).
|
|
5
|
+
* The post-processing pipeline excludes this layer from the depth/normal MRT
|
|
6
|
+
* scene pass so the screen-space ink and SSGI never treat overlays as geometry,
|
|
7
|
+
* then composites it back on top via a dedicated overlay pass.
|
|
8
|
+
*
|
|
9
|
+
* Editor's `EDITOR_LAYER` (packages/editor) re-exports this — they MUST match.
|
|
10
|
+
*/
|
|
11
|
+
export declare const OVERLAY_LAYER = 1;
|
|
3
12
|
/** Layer used for zone rendering (floor fills and wall borders). */
|
|
4
13
|
export declare const ZONE_LAYER = 2;
|
|
14
|
+
/**
|
|
15
|
+
* Layer for the editor ground grid. Rendered *inside* the scene pass (so scene
|
|
16
|
+
* geometry depth-occludes it instead of it bleeding through walls/objects) — it
|
|
17
|
+
* is a flat, depth-non-writing plane, so the screen-space ink never picks it up.
|
|
18
|
+
* Kept off OVERLAY_LAYER because overlays composite on top with no scene-depth
|
|
19
|
+
* test, which is exactly what we don't want for a full-floor plane. Excluded
|
|
20
|
+
* from thumbnails like the other editor-only layers.
|
|
21
|
+
*/
|
|
22
|
+
export declare const GRID_LAYER = 3;
|
|
23
|
+
/**
|
|
24
|
+
* Layer for geometry hidden from the color passes but still rendered into the
|
|
25
|
+
* shadow map ("shadow-caster-only"). Used when cutaway/solo views hide roofs
|
|
26
|
+
* or non-selected levels: the sun keeps casting their shadows, so interiors
|
|
27
|
+
* get window-shaped light patches instead of flooding with uniform sun.
|
|
28
|
+
* No camera or pass enables this layer — only each shadow-casting light's
|
|
29
|
+
* shadow camera does (see `lights.tsx`). Applied per-object (layers don't
|
|
30
|
+
* cascade) via `applyShadowOnly` / `clearShadowOnly` in `lib/shadow-only.ts`.
|
|
31
|
+
*/
|
|
32
|
+
export declare const SHADOW_ONLY_LAYER = 4;
|
|
5
33
|
//# sourceMappingURL=layers.d.ts.map
|
package/dist/lib/layers.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layers.d.ts","sourceRoot":"","sources":["../../src/lib/layers.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,eAAO,MAAM,WAAW,IAAI,CAAA;AAE5B,oEAAoE;AACpE,eAAO,MAAM,UAAU,IAAI,CAAA"}
|
|
1
|
+
{"version":3,"file":"layers.d.ts","sourceRoot":"","sources":["../../src/lib/layers.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,eAAO,MAAM,WAAW,IAAI,CAAA;AAE5B;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,IAAI,CAAA;AAE9B,oEAAoE;AACpE,eAAO,MAAM,UAAU,IAAI,CAAA;AAE3B;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,IAAI,CAAA;AAE3B;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,IAAI,CAAA"}
|
package/dist/lib/layers.js
CHANGED
|
@@ -1,4 +1,32 @@
|
|
|
1
1
|
/** Default Three.js layer for main scene geometry. */
|
|
2
2
|
export const SCENE_LAYER = 0;
|
|
3
|
+
/**
|
|
4
|
+
* Layer for editor-only overlays (gizmos, move handles, tool previews, grid).
|
|
5
|
+
* The post-processing pipeline excludes this layer from the depth/normal MRT
|
|
6
|
+
* scene pass so the screen-space ink and SSGI never treat overlays as geometry,
|
|
7
|
+
* then composites it back on top via a dedicated overlay pass.
|
|
8
|
+
*
|
|
9
|
+
* Editor's `EDITOR_LAYER` (packages/editor) re-exports this — they MUST match.
|
|
10
|
+
*/
|
|
11
|
+
export const OVERLAY_LAYER = 1;
|
|
3
12
|
/** Layer used for zone rendering (floor fills and wall borders). */
|
|
4
13
|
export const ZONE_LAYER = 2;
|
|
14
|
+
/**
|
|
15
|
+
* Layer for the editor ground grid. Rendered *inside* the scene pass (so scene
|
|
16
|
+
* geometry depth-occludes it instead of it bleeding through walls/objects) — it
|
|
17
|
+
* is a flat, depth-non-writing plane, so the screen-space ink never picks it up.
|
|
18
|
+
* Kept off OVERLAY_LAYER because overlays composite on top with no scene-depth
|
|
19
|
+
* test, which is exactly what we don't want for a full-floor plane. Excluded
|
|
20
|
+
* from thumbnails like the other editor-only layers.
|
|
21
|
+
*/
|
|
22
|
+
export const GRID_LAYER = 3;
|
|
23
|
+
/**
|
|
24
|
+
* Layer for geometry hidden from the color passes but still rendered into the
|
|
25
|
+
* shadow map ("shadow-caster-only"). Used when cutaway/solo views hide roofs
|
|
26
|
+
* or non-selected levels: the sun keeps casting their shadows, so interiors
|
|
27
|
+
* get window-shaped light patches instead of flooding with uniform sun.
|
|
28
|
+
* No camera or pass enables this layer — only each shadow-casting light's
|
|
29
|
+
* shadow camera does (see `lights.tsx`). Applied per-object (layers don't
|
|
30
|
+
* cascade) via `applyShadowOnly` / `clearShadowOnly` in `lib/shadow-only.ts`.
|
|
31
|
+
*/
|
|
32
|
+
export const SHADOW_ONLY_LAYER = 4;
|
package/dist/lib/materials.d.ts
CHANGED
|
@@ -1,20 +1,43 @@
|
|
|
1
|
-
import { type MaterialPresetPayload, type MaterialSchema } from '@pascal-app/core';
|
|
1
|
+
import { type MaterialPresetPayload, type MaterialSchema, type SceneMaterial, type SceneMaterialId, type SurfaceRole } from '@pascal-app/core';
|
|
2
2
|
import * as THREE from 'three';
|
|
3
|
-
import {
|
|
4
|
-
export
|
|
5
|
-
export
|
|
3
|
+
import { MeshLambertNodeMaterial } from 'three/webgpu';
|
|
4
|
+
export type RenderShading = 'solid' | 'rendered';
|
|
5
|
+
export type ColorPreset = 'clay' | 'white' | 'mono' | 'blueprint';
|
|
6
|
+
export declare const CLAY_PALETTE: Record<SurfaceRole, string>;
|
|
7
|
+
export declare const WHITE_PALETTE: Record<SurfaceRole, string>;
|
|
8
|
+
export declare const MONO_PALETTE: Record<SurfaceRole, string>;
|
|
9
|
+
export declare const BLUEPRINT_PALETTE: Record<SurfaceRole, string>;
|
|
10
|
+
export declare const PRESET_PALETTES: Record<ColorPreset, Record<SurfaceRole, string>>;
|
|
11
|
+
export declare function resolveSurfaceColor(role: SurfaceRole, preset: ColorPreset, sceneThemeId?: string): string;
|
|
12
|
+
export declare const glassMaterial: MeshLambertNodeMaterial;
|
|
6
13
|
export declare function applyMaterialPresetToMaterials(materialInput: THREE.Material | THREE.Material[], preset: MaterialPresetPayload | null | undefined): void;
|
|
7
|
-
export declare function createMaterialFromPreset(preset: MaterialPresetPayload): THREE.
|
|
8
|
-
export declare function createMaterialFromPresetRef(materialPreset?: string): THREE.
|
|
9
|
-
export declare function createMaterial(material?: MaterialSchema): THREE.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
export declare
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
export declare function createMaterialFromPreset(preset: MaterialPresetPayload, shading?: RenderShading): THREE.Material;
|
|
15
|
+
export declare function createMaterialFromPresetRef(materialPreset?: string, shading?: RenderShading): THREE.Material | null;
|
|
16
|
+
export declare function createMaterial(material?: MaterialSchema, shading?: RenderShading): THREE.Material;
|
|
17
|
+
/**
|
|
18
|
+
* Resolve a MaterialRef ('library:<id>' | 'scene:<id>') to a three.js material.
|
|
19
|
+
* Returns null for an unknown / dangling ref so callers fall back to the
|
|
20
|
+
* slot's default (authored material, then themed default). Never throws.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveMaterialRef(ref: string | undefined, sceneMaterials: Record<SceneMaterialId, SceneMaterial> | undefined, shading?: RenderShading): THREE.Material | null;
|
|
23
|
+
/**
|
|
24
|
+
* Resolve a node kind's declared slot default — either a catalog `library:<id>`
|
|
25
|
+
* finish or a flat `#rrggbb` colour — to a renderable material. Shared by the
|
|
26
|
+
* procedural kinds whose colored-mode unpainted appearance comes from a
|
|
27
|
+
* declarative default (slab, wall).
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveSlotDefaultMaterial(slotDefault: string, shading?: RenderShading, roughness?: number): THREE.Material;
|
|
30
|
+
export declare function createDefaultMaterial(color?: string, roughness?: number, shading?: RenderShading, side?: THREE.Side): THREE.Material;
|
|
31
|
+
export declare function createSurfaceRoleMaterial(role: SurfaceRole, preset: ColorPreset, side?: THREE.Side, sceneThemeId?: string): THREE.Material;
|
|
32
|
+
export declare function baseMaterial(shading?: RenderShading): THREE.Material;
|
|
33
|
+
export declare function DEFAULT_WALL_MATERIAL(shading?: RenderShading): THREE.Material;
|
|
34
|
+
export declare function DEFAULT_SLAB_MATERIAL(shading?: RenderShading): THREE.Material;
|
|
35
|
+
export declare function DEFAULT_DOOR_MATERIAL(shading?: RenderShading): THREE.Material;
|
|
36
|
+
export declare function DEFAULT_WINDOW_MATERIAL(shading?: RenderShading): THREE.Material;
|
|
37
|
+
export declare function DEFAULT_CEILING_MATERIAL(shading?: RenderShading): THREE.Material;
|
|
38
|
+
export declare function DEFAULT_ROOF_MATERIAL(shading?: RenderShading): THREE.Material;
|
|
39
|
+
export declare function DEFAULT_SHELF_MATERIAL(shading?: RenderShading): THREE.Material;
|
|
40
|
+
export declare function DEFAULT_STAIR_MATERIAL(shading?: RenderShading): THREE.Material;
|
|
18
41
|
export declare function disposeMaterial(material: THREE.Material): void;
|
|
19
42
|
export declare function clearMaterialCache(): void;
|
|
20
43
|
//# sourceMappingURL=materials.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"materials.d.ts","sourceRoot":"","sources":["../../src/lib/materials.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,qBAAqB,EAE1B,KAAK,cAAc,
|
|
1
|
+
{"version":3,"file":"materials.d.ts","sourceRoot":"","sources":["../../src/lib/materials.ts"],"names":[],"mappings":"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"}
|