@pascal-app/viewer 1.0.3 → 1.0.5
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/capture/capture-runtime.js +1 -1
- package/dist/components/error-boundary.d.ts.map +1 -1
- package/dist/components/renderers/node-renderer.d.ts +1 -1
- package/dist/components/renderers/node-renderer.d.ts.map +1 -1
- package/dist/components/renderers/node-renderer.js +12 -4
- package/dist/components/renderers/parametric-node-renderer.d.ts.map +1 -1
- package/dist/components/viewer/bvh-ecctrl.d.ts.map +1 -1
- package/dist/components/viewer/frame-limiter.d.ts +1 -0
- package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
- package/dist/components/viewer/frame-limiter.js +22 -3
- package/dist/components/viewer/glb-interactive.d.ts +25 -1
- package/dist/components/viewer/glb-interactive.d.ts.map +1 -1
- package/dist/components/viewer/glb-interactive.js +253 -86
- package/dist/components/viewer/glb-mechanisms.d.ts +10 -0
- package/dist/components/viewer/glb-mechanisms.d.ts.map +1 -0
- package/dist/components/viewer/glb-mechanisms.js +19 -0
- package/dist/components/viewer/glb-reference-nodes.js +1 -1
- package/dist/components/viewer/glb-replace-instances.js +1 -1
- package/dist/components/viewer/glb-scene.d.ts +45 -27
- package/dist/components/viewer/glb-scene.d.ts.map +1 -1
- package/dist/components/viewer/glb-scene.js +421 -508
- package/dist/components/viewer/glb-walkthrough-controller.d.ts +1 -1
- package/dist/components/viewer/glb-walkthrough-controller.d.ts.map +1 -1
- package/dist/components/viewer/glb-walkthrough-controller.js +13 -2
- package/dist/components/viewer/index.d.ts +24 -1
- package/dist/components/viewer/index.d.ts.map +1 -1
- package/dist/components/viewer/index.js +111 -37
- package/dist/components/viewer/lights.d.ts.map +1 -1
- package/dist/components/viewer/lights.js +113 -22
- package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
- package/dist/components/viewer/perf-monitor.js +6 -0
- package/dist/components/viewer/perf-panel.d.ts.map +1 -1
- package/dist/components/viewer/perf-panel.js +1 -1
- package/dist/components/viewer/post-processing.d.ts.map +1 -1
- package/dist/components/viewer/post-processing.js +104 -36
- package/dist/components/viewer/registered-systems.d.ts.map +1 -1
- package/dist/components/viewer/registered-systems.js +17 -3
- package/dist/components/viewer/render-error.d.ts +18 -0
- package/dist/components/viewer/render-error.d.ts.map +1 -0
- package/dist/components/viewer/render-error.js +29 -0
- package/dist/components/viewer/scene-bvh.d.ts.map +1 -1
- package/dist/components/viewer/scene-bvh.js +1 -1
- package/dist/components/viewer/scene-environment.d.ts.map +1 -1
- package/dist/components/viewer/scene-environment.js +31 -11
- package/dist/components/viewer/scene-ground-replacement.d.ts +6 -0
- package/dist/components/viewer/scene-ground-replacement.d.ts.map +1 -1
- package/dist/components/viewer/scene-ground-replacement.js +15 -0
- package/dist/components/viewer/selection-manager.d.ts.map +1 -1
- package/dist/components/viewer/selection-manager.js +25 -7
- package/dist/components/viewer/underground-ambiance.d.ts +9 -0
- package/dist/components/viewer/underground-ambiance.d.ts.map +1 -0
- package/dist/components/viewer/underground-ambiance.js +105 -0
- package/dist/components/viewer/viewer-camera.d.ts +4 -0
- package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
- package/dist/components/viewer/viewer-camera.js +12 -2
- package/dist/components/viewer/viewer-presentations.d.ts.map +1 -1
- package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
- package/dist/hooks/use-gltf-ktx2.js +2 -0
- package/dist/hooks/use-node-events.d.ts.map +1 -1
- package/dist/index.d.ts +36 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +34 -19
- package/dist/lib/asset-url.d.ts.map +1 -1
- package/dist/lib/asset-url.js +7 -1
- package/dist/lib/backdrop.d.ts +17 -0
- package/dist/lib/backdrop.d.ts.map +1 -1
- package/dist/lib/backdrop.js +18 -1
- package/dist/lib/construction-dust.d.ts +57 -0
- package/dist/lib/construction-dust.d.ts.map +1 -0
- package/dist/lib/construction-dust.js +187 -0
- package/dist/lib/csg-utils.js +1 -1
- package/dist/lib/display-state.d.ts +25 -0
- package/dist/lib/display-state.d.ts.map +1 -0
- package/dist/lib/display-state.js +1 -0
- package/dist/lib/foundation-materials.d.ts +26 -0
- package/dist/lib/foundation-materials.d.ts.map +1 -0
- package/dist/lib/foundation-materials.js +65 -0
- package/dist/lib/gltf-unlit.d.ts +10 -0
- package/dist/lib/gltf-unlit.d.ts.map +1 -0
- package/dist/lib/gltf-unlit.js +23 -0
- package/dist/lib/ground-window.d.ts +33 -0
- package/dist/lib/ground-window.d.ts.map +1 -0
- package/dist/lib/ground-window.js +114 -0
- package/dist/lib/hero-pose.d.ts +13 -3
- package/dist/lib/hero-pose.d.ts.map +1 -1
- package/dist/lib/hero-pose.js +72 -5
- package/dist/lib/ink-edges.d.ts +6 -3
- package/dist/lib/ink-edges.d.ts.map +1 -1
- package/dist/lib/ink-edges.js +17 -7
- package/dist/lib/key-light-override.d.ts +11 -0
- package/dist/lib/key-light-override.d.ts.map +1 -0
- package/dist/lib/key-light-override.js +14 -0
- package/dist/lib/ktx2-loader.d.ts +7 -0
- package/dist/lib/ktx2-loader.d.ts.map +1 -1
- package/dist/lib/ktx2-loader.js +35 -0
- package/dist/lib/layer-pass.d.ts.map +1 -1
- package/dist/lib/layers.d.ts +11 -7
- package/dist/lib/layers.d.ts.map +1 -1
- package/dist/lib/layers.js +16 -6
- package/dist/lib/live-frame-hold.d.ts +14 -0
- package/dist/lib/live-frame-hold.d.ts.map +1 -0
- package/dist/lib/live-frame-hold.js +28 -0
- package/dist/lib/materials.d.ts +19 -7
- package/dist/lib/materials.d.ts.map +1 -1
- package/dist/lib/materials.js +65 -9
- package/dist/lib/merged-outline-node.d.ts +5 -0
- package/dist/lib/merged-outline-node.d.ts.map +1 -1
- package/dist/lib/merged-outline-node.js +80 -2
- package/dist/lib/node-top-surface-height.d.ts +6 -0
- package/dist/lib/node-top-surface-height.d.ts.map +1 -0
- package/dist/lib/node-top-surface-height.js +74 -0
- package/dist/lib/perf-probe-state.d.ts +39 -0
- package/dist/lib/perf-probe-state.d.ts.map +1 -0
- package/dist/lib/perf-probe-state.js +17 -0
- package/dist/lib/pointer-events.d.ts +26 -2
- package/dist/lib/pointer-events.d.ts.map +1 -1
- package/dist/lib/pointer-events.js +99 -12
- package/dist/lib/post-processing-resources.d.ts.map +1 -1
- package/dist/lib/procedural-emission.d.ts +7 -0
- package/dist/lib/procedural-emission.d.ts.map +1 -0
- package/dist/lib/procedural-emission.js +54 -0
- package/dist/lib/reveal-candidates.d.ts +80 -0
- package/dist/lib/reveal-candidates.d.ts.map +1 -0
- package/dist/lib/reveal-candidates.js +524 -0
- package/dist/lib/reveal-ghost.d.ts +9 -0
- package/dist/lib/reveal-ghost.d.ts.map +1 -0
- package/dist/lib/reveal-ghost.js +62 -0
- package/dist/lib/reveal-pending.d.ts +12 -0
- package/dist/lib/reveal-pending.d.ts.map +1 -0
- package/dist/lib/reveal-pending.js +12 -0
- package/dist/lib/reveal-pose.d.ts +70 -0
- package/dist/lib/reveal-pose.d.ts.map +1 -0
- package/dist/lib/reveal-pose.js +248 -0
- package/dist/lib/reveal-schedule.d.ts +208 -0
- package/dist/lib/reveal-schedule.d.ts.map +1 -0
- package/dist/lib/reveal-schedule.js +410 -0
- package/dist/lib/reveal-texture-scale.d.ts +5 -0
- package/dist/lib/reveal-texture-scale.d.ts.map +1 -0
- package/dist/lib/reveal-texture-scale.js +113 -0
- package/dist/lib/reveal-windows.d.ts +23 -0
- package/dist/lib/reveal-windows.d.ts.map +1 -0
- package/dist/lib/reveal-windows.js +31 -0
- package/dist/lib/reveal-wipe.d.ts +71 -0
- package/dist/lib/reveal-wipe.d.ts.map +1 -0
- package/dist/lib/reveal-wipe.js +105 -0
- package/dist/lib/scene-pass-mrt.d.ts +12 -0
- package/dist/lib/scene-pass-mrt.d.ts.map +1 -0
- package/dist/lib/scene-pass-mrt.js +23 -0
- package/dist/lib/scene-support-height.d.ts +4 -0
- package/dist/lib/scene-support-height.d.ts.map +1 -0
- package/dist/lib/scene-support-height.js +34 -0
- package/dist/lib/scene-themes.d.ts +30 -8
- package/dist/lib/scene-themes.d.ts.map +1 -1
- package/dist/lib/scene-themes.js +36 -0
- package/dist/lib/scene-visibility.d.ts +17 -1
- package/dist/lib/scene-visibility.d.ts.map +1 -1
- package/dist/lib/scene-visibility.js +53 -3
- package/dist/lib/scripted-opening.d.ts +11 -0
- package/dist/lib/scripted-opening.d.ts.map +1 -0
- package/dist/lib/scripted-opening.js +20 -0
- package/dist/lib/site-surfaces.d.ts +32 -0
- package/dist/lib/site-surfaces.d.ts.map +1 -0
- package/dist/lib/site-surfaces.js +70 -0
- package/dist/lib/snapshot-pipeline.d.ts +34 -3
- package/dist/lib/snapshot-pipeline.d.ts.map +1 -1
- package/dist/lib/snapshot-pipeline.js +247 -117
- package/dist/lib/terrain-perimeter-fill.d.ts.map +1 -1
- package/dist/lib/terrain-perimeter-fill.js +22 -14
- package/dist/lib/tsl-compat.d.ts +2 -2
- package/dist/lib/tsl-compat.d.ts.map +1 -1
- package/dist/lib/underground-ambiance.d.ts +168 -0
- package/dist/lib/underground-ambiance.d.ts.map +1 -0
- package/dist/lib/underground-ambiance.js +334 -0
- package/dist/lib/zone-geometry.d.ts +11 -0
- package/dist/lib/zone-geometry.d.ts.map +1 -0
- package/dist/lib/zone-geometry.js +46 -0
- package/dist/store/use-item-light-pool.d.ts +14 -10
- package/dist/store/use-item-light-pool.d.ts.map +1 -1
- package/dist/store/use-item-light-pool.js +52 -26
- package/dist/store/use-viewer.d.ts +194 -26
- package/dist/store/use-viewer.d.ts.map +1 -1
- package/dist/store/use-viewer.js +164 -21
- package/dist/systems/ceiling/ceiling-system.d.ts +15 -1
- package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
- package/dist/systems/ceiling/ceiling-system.js +88 -6
- package/dist/systems/construction-reveal/construction-dust.d.ts +16 -0
- package/dist/systems/construction-reveal/construction-dust.d.ts.map +1 -0
- package/dist/systems/construction-reveal/construction-dust.js +92 -0
- package/dist/systems/construction-reveal/construction-reveal.d.ts +276 -0
- package/dist/systems/construction-reveal/construction-reveal.d.ts.map +1 -0
- package/dist/systems/construction-reveal/construction-reveal.js +2383 -0
- package/dist/systems/construction-reveal/dust-material.d.ts +10 -0
- package/dist/systems/construction-reveal/dust-material.d.ts.map +1 -0
- package/dist/systems/construction-reveal/dust-material.js +17 -0
- package/dist/systems/construction-reveal/ghost-dress.d.ts +10 -0
- package/dist/systems/construction-reveal/ghost-dress.d.ts.map +1 -0
- package/dist/systems/construction-reveal/ghost-dress.js +24 -0
- package/dist/systems/door/door-system.d.ts.map +1 -1
- package/dist/systems/door/door-system.js +55 -17
- package/dist/systems/elevator/elevator-interaction-system.d.ts +7 -0
- package/dist/systems/elevator/elevator-interaction-system.d.ts.map +1 -1
- package/dist/systems/elevator/elevator-interaction-system.js +18 -1
- package/dist/systems/floor-elevation/floor-elevation-system.d.ts +3 -1
- package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -1
- package/dist/systems/floor-elevation/floor-elevation-system.js +33 -5
- package/dist/systems/geometry/geometry-system.d.ts.map +1 -1
- package/dist/systems/geometry/geometry-system.js +71 -11
- package/dist/systems/interactive/control-widget.d.ts.map +1 -1
- package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
- package/dist/systems/interactive/interactive-system.js +52 -17
- package/dist/systems/interactive/procedural-controls.d.ts +13 -0
- package/dist/systems/interactive/procedural-controls.d.ts.map +1 -0
- package/dist/systems/interactive/procedural-controls.js +17 -0
- package/dist/systems/interactive/scripted-clips.d.ts +19 -0
- package/dist/systems/interactive/scripted-clips.d.ts.map +1 -0
- package/dist/systems/interactive/scripted-clips.js +134 -0
- package/dist/systems/item/item-system.d.ts.map +1 -1
- package/dist/systems/item/item-system.js +9 -3
- package/dist/systems/item/object-cut-invalidation.d.ts +3 -0
- package/dist/systems/item/object-cut-invalidation.d.ts.map +1 -0
- package/dist/systems/item/object-cut-invalidation.js +68 -0
- 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 +48 -53
- package/dist/systems/level/level-hold.d.ts +4 -0
- package/dist/systems/level/level-hold.d.ts.map +1 -0
- package/dist/systems/level/level-hold.js +14 -0
- package/dist/systems/level/level-system.d.ts.map +1 -1
- package/dist/systems/level/level-system.js +19 -6
- package/dist/systems/level/level-utils.d.ts +19 -6
- package/dist/systems/level/level-utils.d.ts.map +1 -1
- package/dist/systems/level/level-utils.js +68 -9
- package/dist/systems/level/shadow-only-levels.d.ts +9 -0
- package/dist/systems/level/shadow-only-levels.d.ts.map +1 -0
- package/dist/systems/level/shadow-only-levels.js +7 -0
- package/dist/systems/roof/__fixtures__/roof-plane-cases.d.ts +103 -0
- package/dist/systems/roof/__fixtures__/roof-plane-cases.d.ts.map +1 -0
- package/dist/systems/roof/__fixtures__/roof-plane-cases.js +317 -0
- package/dist/systems/roof/roof-materials.d.ts +16 -2
- package/dist/systems/roof/roof-materials.d.ts.map +1 -1
- package/dist/systems/roof/roof-materials.js +64 -31
- package/dist/systems/roof/roof-system.d.ts +21 -4
- package/dist/systems/roof/roof-system.d.ts.map +1 -1
- package/dist/systems/roof/roof-system.js +374 -49
- package/dist/systems/slab/slab-system.d.ts +1 -1
- package/dist/systems/slab/slab-system.d.ts.map +1 -1
- package/dist/systems/slab/slab-system.js +49 -19
- package/dist/systems/stair/stair-system.d.ts.map +1 -1
- package/dist/systems/stair/stair-system.js +133 -354
- package/dist/systems/wall/wall-cutout-cache.d.ts +39 -3
- package/dist/systems/wall/wall-cutout-cache.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout-cache.js +141 -19
- package/dist/systems/wall/wall-cutout.d.ts +6 -0
- package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout.js +42 -28
- package/dist/systems/wall/wall-finish-data.d.ts +22 -0
- package/dist/systems/wall/wall-finish-data.d.ts.map +1 -0
- package/dist/systems/wall/wall-finish-data.js +17 -0
- package/dist/systems/wall/wall-material-variant.d.ts.map +1 -1
- package/dist/systems/wall/wall-materials.d.ts +9 -4
- package/dist/systems/wall/wall-materials.d.ts.map +1 -1
- package/dist/systems/wall/wall-materials.js +135 -153
- package/dist/systems/wall/wall-placeholder-sweep.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.d.ts +33 -2
- package/dist/systems/wall/wall-system.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.js +621 -159
- package/dist/systems/window/corner-frame.d.ts +10 -0
- package/dist/systems/window/corner-frame.d.ts.map +1 -0
- package/dist/systems/window/corner-frame.js +17 -0
- package/dist/systems/window/window-system.d.ts +1 -1
- package/dist/systems/window/window-system.d.ts.map +1 -1
- package/dist/systems/window/window-system.js +71 -27
- package/package.json +10 -10
- package/dist/systems/fence/fence-system.d.ts +0 -22
- package/dist/systems/fence/fence-system.d.ts.map +0 -1
- package/dist/systems/fence/fence-system.js +0 -357
package/dist/lib/materials.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getMaterialPresetByRef, parseMaterialRef, resolveMaterial, } from '@pascal-app/core';
|
|
1
|
+
import { getMaterialPresetByRef, materialColorPaint, parseMaterialColor, parseMaterialRef, resolveMaterial, } from '@pascal-app/core';
|
|
2
2
|
import { addAfterEffect, invalidate } from '@react-three/fiber';
|
|
3
3
|
import * as THREE from 'three';
|
|
4
4
|
import { float, mix, positionViewDirection, transformedNormalView } from 'three/tsl';
|
|
@@ -82,12 +82,35 @@ const defaultMaterialCache = new Map();
|
|
|
82
82
|
const surfaceRoleMaterialCache = new Map();
|
|
83
83
|
const textureCache = new Map();
|
|
84
84
|
const textureLoadPromises = new Map();
|
|
85
|
+
const pendingTextures = new Set();
|
|
86
|
+
const failedTextures = new Set();
|
|
85
87
|
const textureLoader = new THREE.TextureLoader();
|
|
86
88
|
let materialTextureVersion = 0;
|
|
87
89
|
// Highlight clones can observe late assignments without polling every material.
|
|
88
90
|
export function getMaterialTextureVersion() {
|
|
89
91
|
return materialTextureVersion;
|
|
90
92
|
}
|
|
93
|
+
/** Conservative capture readiness across the current material cache. */
|
|
94
|
+
export function materialTextureLoadState() {
|
|
95
|
+
return { pending: pendingTextures.size + textureLoadPromises.size, failed: failedTextures.size };
|
|
96
|
+
}
|
|
97
|
+
function loadTexture(url, key) {
|
|
98
|
+
pendingTextures.add(key);
|
|
99
|
+
try {
|
|
100
|
+
return pickTextureLoader(url).load(url, () => {
|
|
101
|
+
pendingTextures.delete(key);
|
|
102
|
+
failedTextures.delete(key);
|
|
103
|
+
}, undefined, () => {
|
|
104
|
+
pendingTextures.delete(key);
|
|
105
|
+
failedTextures.add(key);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
pendingTextures.delete(key);
|
|
110
|
+
failedTextures.add(key);
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
91
114
|
// `.ktx2` finish maps transcode through the shared KTX2 loader (support is
|
|
92
115
|
// detected once at viewer init); everything else loads as a normal image.
|
|
93
116
|
function pickTextureLoader(url) {
|
|
@@ -157,7 +180,7 @@ function getTexture(material) {
|
|
|
157
180
|
const resolvedUrl = /^(?:asset|blob|data):/.test(textureConfig.url)
|
|
158
181
|
? textureConfig.url
|
|
159
182
|
: (resolveCdnUrl(textureConfig.url) ?? textureConfig.url);
|
|
160
|
-
const texture =
|
|
183
|
+
const texture = loadTexture(resolvedUrl, cacheKey);
|
|
161
184
|
texture.wrapS = THREE.RepeatWrapping;
|
|
162
185
|
texture.wrapT = THREE.RepeatWrapping;
|
|
163
186
|
const [repeatX, repeatY] = resolveTextureRepeat(textureConfig.repeat, textureConfig.scale);
|
|
@@ -207,7 +230,7 @@ function getPresetTexture(path, props, slot) {
|
|
|
207
230
|
const cached = textureCache.get(cacheKey);
|
|
208
231
|
if (cached)
|
|
209
232
|
return cached;
|
|
210
|
-
const texture =
|
|
233
|
+
const texture = loadTexture(resolvedPath, cacheKey);
|
|
211
234
|
applyTextureProperties(texture, props, slot);
|
|
212
235
|
stampPascalTextureRef(texture, {
|
|
213
236
|
kind: 'material',
|
|
@@ -263,11 +286,13 @@ async function loadPresetTexture(path, props, slot) {
|
|
|
263
286
|
setTextureCacheKey(texture, cacheKey);
|
|
264
287
|
textureCache.set(cacheKey, texture);
|
|
265
288
|
textureLoadPromises.delete(cacheKey);
|
|
289
|
+
failedTextures.delete(cacheKey);
|
|
266
290
|
return texture;
|
|
267
291
|
})
|
|
268
292
|
.catch((error) => {
|
|
269
293
|
console.warn('[viewer] Failed to load material texture', resolvedPath, error);
|
|
270
294
|
textureLoadPromises.delete(cacheKey);
|
|
295
|
+
failedTextures.add(cacheKey);
|
|
271
296
|
return null;
|
|
272
297
|
});
|
|
273
298
|
textureLoadPromises.set(cacheKey, promise);
|
|
@@ -346,6 +371,12 @@ function applyMaterialMapProperties(material, mapProperties) {
|
|
|
346
371
|
// Glass-like transparency threshold: any standard material authored as
|
|
347
372
|
// `transparent` with opacity below this gets the fresnel treatment.
|
|
348
373
|
const GLASS_OPACITY_THRESHOLD = 0.6;
|
|
374
|
+
/** Glass lets light through; shadow maps would otherwise treat it as a solid panel. */
|
|
375
|
+
export function materialCastsShadow(material) {
|
|
376
|
+
const materials = Array.isArray(material) ? material : [material];
|
|
377
|
+
return !materials.some((entry) => entry.transmission > 0 ||
|
|
378
|
+
(entry.transparent && entry.opacity < GLASS_OPACITY_THRESHOLD));
|
|
379
|
+
}
|
|
349
380
|
/**
|
|
350
381
|
* Fresnel-driven opacity for glass: nearly the authored opacity head-on,
|
|
351
382
|
* increasingly opaque (showing the environment reflection) at grazing angles.
|
|
@@ -413,7 +444,8 @@ export function createMaterialFromPresetRef(materialPreset, shading = 'rendered'
|
|
|
413
444
|
}
|
|
414
445
|
export function createMaterial(material, shading = 'rendered') {
|
|
415
446
|
const props = resolveMaterial(material);
|
|
416
|
-
const
|
|
447
|
+
const glassPreset = material?.preset === 'glass';
|
|
448
|
+
const cacheKey = `${getCacheKey(props, shading)}-${getTextureKey(material)}-${glassPreset}`;
|
|
417
449
|
if (materialCache.has(cacheKey)) {
|
|
418
450
|
return materialCache.get(cacheKey);
|
|
419
451
|
}
|
|
@@ -433,7 +465,9 @@ export function createMaterial(material, shading = 'rendered') {
|
|
|
433
465
|
roughness: props.roughness,
|
|
434
466
|
metalness: props.metalness,
|
|
435
467
|
});
|
|
436
|
-
|
|
468
|
+
// Explicit source transparency keeps its opacity; the glass preset owns this styling.
|
|
469
|
+
if (glassPreset)
|
|
470
|
+
maybeApplyGlassFresnel(threeMaterial);
|
|
437
471
|
threeMaterial.userData.__pascalCachedMaterial = true;
|
|
438
472
|
materialCache.set(cacheKey, threeMaterial);
|
|
439
473
|
return threeMaterial;
|
|
@@ -446,14 +480,19 @@ export function createMaterial(material, shading = 'rendered') {
|
|
|
446
480
|
* the dangling-ref fallback for the whole session.
|
|
447
481
|
*/
|
|
448
482
|
export function materialPresetRefSignature(ref) {
|
|
449
|
-
return getMaterialPresetByRef(ref) ? ref : `${ref}#unresolved`;
|
|
483
|
+
return getMaterialPresetByRef(ref) || parseMaterialColor(ref) ? ref : `${ref}#unresolved`;
|
|
450
484
|
}
|
|
451
485
|
/**
|
|
452
|
-
* Resolve a MaterialRef ('library:<id>' | 'scene:<id>')
|
|
453
|
-
*
|
|
454
|
-
*
|
|
486
|
+
* Resolve a MaterialRef ('library:<id>' | 'scene:<id>') or a plain '#rrggbb'
|
|
487
|
+
* colour to a three.js material; the colour paints like
|
|
488
|
+
* `material: { properties: { color } }`. Returns null for an unknown / dangling
|
|
489
|
+
* ref so callers fall back to the slot's default (authored material, then
|
|
490
|
+
* themed default). Never throws.
|
|
455
491
|
*/
|
|
456
492
|
export function resolveMaterialRef(ref, sceneMaterials, shading = 'rendered') {
|
|
493
|
+
const color = parseMaterialColor(ref);
|
|
494
|
+
if (color)
|
|
495
|
+
return createMaterial(materialColorPaint(color), shading);
|
|
457
496
|
const parsed = parseMaterialRef(ref);
|
|
458
497
|
if (!parsed)
|
|
459
498
|
return null;
|
|
@@ -470,7 +509,22 @@ export function resolveMaterialRef(ref, sceneMaterials, shading = 'rendered') {
|
|
|
470
509
|
* procedural kinds whose colored-mode unpainted appearance comes from a
|
|
471
510
|
* declarative default (slab, wall).
|
|
472
511
|
*/
|
|
512
|
+
let slotDefaultOverrides = null;
|
|
513
|
+
/**
|
|
514
|
+
* Swaps declared slot DEFAULTS (never painted refs) for another finish, e.g.
|
|
515
|
+
* the worker thumbnail renders unpainted walls as plaster instead of the
|
|
516
|
+
* prepared-drywall default. Must be set before the scene builds its
|
|
517
|
+
* materials; `null` restores the declared defaults.
|
|
518
|
+
*/
|
|
519
|
+
export function setSlotDefaultOverrides(overrides) {
|
|
520
|
+
slotDefaultOverrides = overrides;
|
|
521
|
+
}
|
|
522
|
+
/** A declared slot default after `setSlotDefaultOverrides`. */
|
|
523
|
+
export function resolveSlotDefaultRef(slotDefault) {
|
|
524
|
+
return slotDefaultOverrides?.[slotDefault] ?? slotDefault;
|
|
525
|
+
}
|
|
473
526
|
export function resolveSlotDefaultMaterial(slotDefault, shading = 'rendered', roughness = 0.9) {
|
|
527
|
+
slotDefault = resolveSlotDefaultRef(slotDefault);
|
|
474
528
|
if (parseMaterialRef(slotDefault)?.kind === 'library') {
|
|
475
529
|
return (createMaterialFromPresetRef(slotDefault, shading) ??
|
|
476
530
|
cachedDefaultMaterial(`slot-#ffffff-${roughness}`, '#ffffff', roughness, shading));
|
|
@@ -604,6 +658,8 @@ export function clearMaterialCache() {
|
|
|
604
658
|
surfaceRoleMaterialCache.clear();
|
|
605
659
|
textureCache.clear();
|
|
606
660
|
textureLoadPromises.clear();
|
|
661
|
+
pendingTextures.clear();
|
|
662
|
+
failedTextures.clear();
|
|
607
663
|
const disposals = [];
|
|
608
664
|
for (const cleanup of materialCacheCleanups) {
|
|
609
665
|
const dispose = cleanup();
|
|
@@ -63,6 +63,10 @@ export declare class MergedOutlineNode extends TempNode {
|
|
|
63
63
|
private readonly _proxiesA;
|
|
64
64
|
private readonly _proxiesB;
|
|
65
65
|
private readonly _proxyMaskMaterials;
|
|
66
|
+
private readonly _cutoutMasksA;
|
|
67
|
+
private readonly _cutoutMasksB;
|
|
68
|
+
private readonly _usedCutoutMasksA;
|
|
69
|
+
private readonly _usedCutoutMasksB;
|
|
66
70
|
private readonly _maskSceneA;
|
|
67
71
|
private readonly _maskSceneB;
|
|
68
72
|
private _wroteGroupALastFrame;
|
|
@@ -90,6 +94,7 @@ export declare class MergedOutlineNode extends TempNode {
|
|
|
90
94
|
private _syncProxies;
|
|
91
95
|
private _supportsProxies;
|
|
92
96
|
private _disposeProxyMaterial;
|
|
97
|
+
private _pruneCutoutMasks;
|
|
93
98
|
private _runEdgePipeline;
|
|
94
99
|
setup(builder: any): any;
|
|
95
100
|
dispose(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merged-outline-node.d.ts","sourceRoot":"","sources":["../../src/lib/merged-outline-node.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"merged-outline-node.d.ts","sourceRoot":"","sources":["../../src/lib/merged-outline-node.ts"],"names":[],"mappings":"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"}
|
|
@@ -124,6 +124,10 @@ export class MergedOutlineNode extends TempNode {
|
|
|
124
124
|
_proxiesA = new Map();
|
|
125
125
|
_proxiesB = new Map();
|
|
126
126
|
_proxyMaskMaterials = new WeakMap();
|
|
127
|
+
_cutoutMasksA = new Map();
|
|
128
|
+
_cutoutMasksB = new Map();
|
|
129
|
+
_usedCutoutMasksA = new Set();
|
|
130
|
+
_usedCutoutMasksB = new Set();
|
|
127
131
|
_maskSceneA = new Scene();
|
|
128
132
|
_maskSceneB = new Scene();
|
|
129
133
|
// Tracks whether either group rendered last frame. We use this to decide
|
|
@@ -267,7 +271,23 @@ export class MergedOutlineNode extends TempNode {
|
|
|
267
271
|
this._buildCache(this.secondaryObjects, this._cacheB);
|
|
268
272
|
const useProxiesA = this._syncProxies(this._cacheA, this._proxiesA, this._maskSceneA);
|
|
269
273
|
const useProxiesB = this._syncProxies(this._cacheB, this._proxiesB, this._maskSceneB);
|
|
274
|
+
this._usedCutoutMasksA.clear();
|
|
275
|
+
this._usedCutoutMasksB.clear();
|
|
276
|
+
for (const [cache, used] of [
|
|
277
|
+
[this._cacheA, this._usedCutoutMasksA],
|
|
278
|
+
[this._cacheB, this._usedCutoutMasksB],
|
|
279
|
+
]) {
|
|
280
|
+
for (const object of cache) {
|
|
281
|
+
if (!(object instanceof Mesh))
|
|
282
|
+
continue;
|
|
283
|
+
const materials = Array.isArray(object.material) ? object.material : [object.material];
|
|
284
|
+
for (const source of materials)
|
|
285
|
+
if (source?.alphaTest > 0)
|
|
286
|
+
used.add(source);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
270
289
|
if (!hasAny) {
|
|
290
|
+
this._pruneCutoutMasks();
|
|
271
291
|
RendererUtils.restoreRendererAndSceneState(renderer, scene, _rendererState);
|
|
272
292
|
return;
|
|
273
293
|
}
|
|
@@ -300,6 +320,7 @@ export class MergedOutlineNode extends TempNode {
|
|
|
300
320
|
this._runEdgePipeline(renderer, 'A');
|
|
301
321
|
if (hasSecondary)
|
|
302
322
|
this._runEdgePipeline(renderer, 'B');
|
|
323
|
+
this._pruneCutoutMasks();
|
|
303
324
|
RendererUtils.restoreRendererAndSceneState(renderer, scene, _rendererState);
|
|
304
325
|
}
|
|
305
326
|
_renderMask(renderer, group, useProxies) {
|
|
@@ -309,6 +330,8 @@ export class MergedOutlineNode extends TempNode {
|
|
|
309
330
|
const maskScene = isA ? this._maskSceneA : this._maskSceneB;
|
|
310
331
|
const material = isA ? this._prepareMaskMatA : this._prepareMaskMatB;
|
|
311
332
|
const spriteMaterial = isA ? this._prepareMaskSpriteMatA : this._prepareMaskSpriteMatB;
|
|
333
|
+
const cutoutMasks = isA ? this._cutoutMasksA : this._cutoutMasksB;
|
|
334
|
+
const usedCutoutMasks = isA ? this._usedCutoutMasksA : this._usedCutoutMasksB;
|
|
312
335
|
if (useProxies) {
|
|
313
336
|
for (const [source, proxy] of proxies)
|
|
314
337
|
proxy.matrixWorld.copy(source.matrixWorld);
|
|
@@ -316,11 +339,39 @@ export class MergedOutlineNode extends TempNode {
|
|
|
316
339
|
renderer.setRenderTarget((isA ? this._groupA : this._groupB).maskBuffer);
|
|
317
340
|
// Keep source materials on proxies for material visibility and geometry
|
|
318
341
|
// groups; substitute only at submission, just like the full-scene path.
|
|
319
|
-
renderer.setRenderObjectFunction((obj, sc, cam, geo,
|
|
342
|
+
renderer.setRenderObjectFunction((obj, sc, cam, geo, sourceMaterial, grp, lights, clip) => {
|
|
320
343
|
if (!hasDrawableGeometry(geo))
|
|
321
344
|
return;
|
|
322
345
|
if (useProxies || cache.has(obj)) {
|
|
323
346
|
let maskMaterial = obj.isSprite ? spriteMaterial : material;
|
|
347
|
+
if (!obj.isSprite && sourceMaterial?.alphaTest > 0) {
|
|
348
|
+
let cutoutMask = cutoutMasks.get(sourceMaterial);
|
|
349
|
+
const opacityNode = sourceMaterial.opacityNode ??
|
|
350
|
+
(sourceMaterial.map
|
|
351
|
+
? cutoutMask?.userData.sourceMap === sourceMaterial.map
|
|
352
|
+
? cutoutMask.opacityNode
|
|
353
|
+
: texture(sourceMaterial.map).a
|
|
354
|
+
: null);
|
|
355
|
+
if (opacityNode) {
|
|
356
|
+
if (!cutoutMask) {
|
|
357
|
+
cutoutMask = material.clone();
|
|
358
|
+
cutoutMasks.set(sourceMaterial, cutoutMask);
|
|
359
|
+
}
|
|
360
|
+
const positionNode = sourceMaterial.positionNode ?? null;
|
|
361
|
+
const changed = cutoutMask.opacityNode !== opacityNode ||
|
|
362
|
+
cutoutMask.positionNode !== positionNode ||
|
|
363
|
+
cutoutMask.side !== sourceMaterial.side;
|
|
364
|
+
cutoutMask.opacityNode = opacityNode;
|
|
365
|
+
cutoutMask.positionNode = positionNode;
|
|
366
|
+
cutoutMask.alphaTest = sourceMaterial.alphaTest;
|
|
367
|
+
cutoutMask.side = sourceMaterial.side;
|
|
368
|
+
cutoutMask.userData.sourceMap = sourceMaterial.map;
|
|
369
|
+
if (changed)
|
|
370
|
+
cutoutMask.needsUpdate = true;
|
|
371
|
+
maskMaterial = cutoutMask;
|
|
372
|
+
usedCutoutMasks.add(sourceMaterial);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
324
375
|
if (useProxies) {
|
|
325
376
|
let ownedMaterial = this._proxyMaskMaterials.get(obj);
|
|
326
377
|
if (!ownedMaterial) {
|
|
@@ -329,8 +380,16 @@ export class MergedOutlineNode extends TempNode {
|
|
|
329
380
|
ownedMaterial = maskMaterial.clone();
|
|
330
381
|
this._proxyMaskMaterials.set(obj, ownedMaterial);
|
|
331
382
|
}
|
|
332
|
-
if (ownedMaterial.colorNode !== maskMaterial.colorNode
|
|
383
|
+
if (ownedMaterial.colorNode !== maskMaterial.colorNode ||
|
|
384
|
+
ownedMaterial.opacityNode !== maskMaterial.opacityNode ||
|
|
385
|
+
ownedMaterial.positionNode !== maskMaterial.positionNode ||
|
|
386
|
+
ownedMaterial.alphaTest !== maskMaterial.alphaTest ||
|
|
387
|
+
ownedMaterial.side !== maskMaterial.side) {
|
|
333
388
|
ownedMaterial.colorNode = maskMaterial.colorNode;
|
|
389
|
+
ownedMaterial.opacityNode = maskMaterial.opacityNode;
|
|
390
|
+
ownedMaterial.positionNode = maskMaterial.positionNode;
|
|
391
|
+
ownedMaterial.alphaTest = maskMaterial.alphaTest;
|
|
392
|
+
ownedMaterial.side = maskMaterial.side;
|
|
334
393
|
ownedMaterial.needsUpdate = true;
|
|
335
394
|
}
|
|
336
395
|
maskMaterial = ownedMaterial;
|
|
@@ -413,6 +472,19 @@ export class MergedOutlineNode extends TempNode {
|
|
|
413
472
|
this._proxyMaskMaterials.get(proxy)?.dispose();
|
|
414
473
|
this._proxyMaskMaterials.delete(proxy);
|
|
415
474
|
}
|
|
475
|
+
_pruneCutoutMasks() {
|
|
476
|
+
for (const [materials, used] of [
|
|
477
|
+
[this._cutoutMasksA, this._usedCutoutMasksA],
|
|
478
|
+
[this._cutoutMasksB, this._usedCutoutMasksB],
|
|
479
|
+
]) {
|
|
480
|
+
for (const [source, mask] of materials) {
|
|
481
|
+
if (used.has(source))
|
|
482
|
+
continue;
|
|
483
|
+
mask.dispose();
|
|
484
|
+
materials.delete(source);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
416
488
|
_runEdgePipeline(renderer, group) {
|
|
417
489
|
const isA = group === 'A';
|
|
418
490
|
const g = isA ? this._groupA : this._groupB;
|
|
@@ -586,6 +658,12 @@ export class MergedOutlineNode extends TempNode {
|
|
|
586
658
|
this._proxiesB.clear();
|
|
587
659
|
this._cacheA.clear();
|
|
588
660
|
this._cacheB.clear();
|
|
661
|
+
for (const mask of this._cutoutMasksA.values())
|
|
662
|
+
mask.dispose();
|
|
663
|
+
for (const mask of this._cutoutMasksB.values())
|
|
664
|
+
mask.dispose();
|
|
665
|
+
this._cutoutMasksA.clear();
|
|
666
|
+
this._cutoutMasksB.clear();
|
|
589
667
|
this._depthRT.dispose();
|
|
590
668
|
this._groupA.dispose();
|
|
591
669
|
this._groupB.dispose();
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type AnyNodeId } from '@pascal-app/core';
|
|
2
|
+
type HeightSampler = (x: number, z: number) => number | null;
|
|
3
|
+
/** Samples one locked node's upward-facing mesh in level-local coordinates. */
|
|
4
|
+
export declare function createNodeTopSurfaceHeightSampler(hostId: AnyNodeId, levelId: AnyNodeId): HeightSampler | null;
|
|
5
|
+
export {};
|
|
6
|
+
//# sourceMappingURL=node-top-surface-height.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-top-surface-height.d.ts","sourceRoot":"","sources":["../../src/lib/node-top-surface-height.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAiB,MAAM,kBAAkB,CAAA;AAKhE,KAAK,aAAa,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAA;AAS5D,+EAA+E;AAC/E,wBAAgB,iCAAiC,CAC/C,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,SAAS,GACjB,aAAa,GAAG,IAAI,CAqEtB"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { sceneRegistry } from '@pascal-app/core';
|
|
2
|
+
import { Box3, Matrix3, Raycaster, Vector3 } from 'three';
|
|
3
|
+
import useViewer from '../store/use-viewer';
|
|
4
|
+
import { setSurfaceRaycastLayers } from './layers';
|
|
5
|
+
const samplerCache = new WeakMap();
|
|
6
|
+
/** Samples one locked node's upward-facing mesh in level-local coordinates. */
|
|
7
|
+
export function createNodeTopSurfaceHeightSampler(hostId, levelId) {
|
|
8
|
+
const host = sceneRegistry.nodes.get(hostId);
|
|
9
|
+
const level = sceneRegistry.nodes.get(levelId);
|
|
10
|
+
if (!(host && level))
|
|
11
|
+
return null;
|
|
12
|
+
host.updateWorldMatrix(true, false);
|
|
13
|
+
level.updateWorldMatrix(true, false);
|
|
14
|
+
const revision = useViewer.getState().geometryRevision;
|
|
15
|
+
const cached = samplerCache.get(host)?.get(level);
|
|
16
|
+
if (cached?.revision === revision &&
|
|
17
|
+
cached.hostMatrix.every((value, index) => value === host.matrixWorld.elements[index]) &&
|
|
18
|
+
cached.levelMatrix.every((value, index) => value === level.matrixWorld.elements[index])) {
|
|
19
|
+
return cached.sample;
|
|
20
|
+
}
|
|
21
|
+
host.updateWorldMatrix(true, true);
|
|
22
|
+
const bounds = new Box3().setFromObject(host);
|
|
23
|
+
if (bounds.isEmpty())
|
|
24
|
+
return null;
|
|
25
|
+
const raycaster = new Raycaster();
|
|
26
|
+
setSurfaceRaycastLayers(raycaster.layers);
|
|
27
|
+
raycaster.far = Math.max(4, bounds.max.y - bounds.min.y + 4);
|
|
28
|
+
const origin = new Vector3();
|
|
29
|
+
const direction = new Vector3(0, -1, 0);
|
|
30
|
+
const normal = new Vector3();
|
|
31
|
+
const normalMatrix = new Matrix3();
|
|
32
|
+
const registeredRoots = new Set(sceneRegistry.nodes.values());
|
|
33
|
+
const sample = (x, z) => {
|
|
34
|
+
origin.set(x, 0, z);
|
|
35
|
+
level.localToWorld(origin);
|
|
36
|
+
origin.y = bounds.max.y + 2;
|
|
37
|
+
raycaster.set(origin, direction);
|
|
38
|
+
for (const hit of raycaster.intersectObject(host, true)) {
|
|
39
|
+
if (!hit.face)
|
|
40
|
+
continue;
|
|
41
|
+
let owner = hit.object.parent;
|
|
42
|
+
let nestedNode = false;
|
|
43
|
+
while (owner && owner !== host) {
|
|
44
|
+
if (registeredRoots.has(owner)) {
|
|
45
|
+
nestedNode = true;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
owner = owner.parent;
|
|
49
|
+
}
|
|
50
|
+
if (nestedNode)
|
|
51
|
+
continue;
|
|
52
|
+
normalMatrix.getNormalMatrix(hit.object.matrixWorld);
|
|
53
|
+
normal.copy(hit.face.normal).applyMatrix3(normalMatrix).normalize();
|
|
54
|
+
if (normal.y < 0.1)
|
|
55
|
+
continue;
|
|
56
|
+
const point = hit.point.clone();
|
|
57
|
+
level.worldToLocal(point);
|
|
58
|
+
return point.y;
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
};
|
|
62
|
+
let byLevel = samplerCache.get(host);
|
|
63
|
+
if (!byLevel) {
|
|
64
|
+
byLevel = new WeakMap();
|
|
65
|
+
samplerCache.set(host, byLevel);
|
|
66
|
+
}
|
|
67
|
+
byLevel.set(level, {
|
|
68
|
+
revision,
|
|
69
|
+
hostMatrix: [...host.matrixWorld.elements],
|
|
70
|
+
levelMatrix: [...level.matrixWorld.elements],
|
|
71
|
+
sample,
|
|
72
|
+
});
|
|
73
|
+
return sample;
|
|
74
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
type TemporalLike = {
|
|
2
|
+
temporal: {
|
|
3
|
+
getState(): {
|
|
4
|
+
pastStates: readonly unknown[];
|
|
5
|
+
futureStates: readonly unknown[];
|
|
6
|
+
isTracking: boolean;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
type SelectionLike = {
|
|
11
|
+
getState(): {
|
|
12
|
+
selection: {
|
|
13
|
+
buildingId: string | null;
|
|
14
|
+
levelId: string | null;
|
|
15
|
+
zoneId: string | null;
|
|
16
|
+
selectedIds: readonly string[];
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
export type PerfHistoryState = {
|
|
21
|
+
/** Undo entries. */
|
|
22
|
+
past: number;
|
|
23
|
+
/** Redo entries. */
|
|
24
|
+
future: number;
|
|
25
|
+
/** False while any owner has history paused. */
|
|
26
|
+
tracking: boolean;
|
|
27
|
+
/** Refcounted pause owners (`pauseSceneHistory` + leases); direct `temporal.pause()` is not counted. */
|
|
28
|
+
pauseDepth: number;
|
|
29
|
+
};
|
|
30
|
+
export type PerfSelectionState = {
|
|
31
|
+
buildingId: string | null;
|
|
32
|
+
levelId: string | null;
|
|
33
|
+
zoneId: string | null;
|
|
34
|
+
selectedIds: string[];
|
|
35
|
+
};
|
|
36
|
+
export declare function readPerfHistory(store: TemporalLike): PerfHistoryState;
|
|
37
|
+
export declare function readPerfSelection(store: SelectionLike): PerfSelectionState;
|
|
38
|
+
export {};
|
|
39
|
+
//# sourceMappingURL=perf-probe-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"perf-probe-state.d.ts","sourceRoot":"","sources":["../../src/lib/perf-probe-state.ts"],"names":[],"mappings":"AAMA,KAAK,YAAY,GAAG;IAClB,QAAQ,EAAE;QACR,QAAQ,IAAI;YACV,UAAU,EAAE,SAAS,OAAO,EAAE,CAAA;YAC9B,YAAY,EAAE,SAAS,OAAO,EAAE,CAAA;YAChC,UAAU,EAAE,OAAO,CAAA;SACpB,CAAA;KACF,CAAA;CACF,CAAA;AAED,KAAK,aAAa,GAAG;IACnB,QAAQ,IAAI;QACV,SAAS,EAAE;YACT,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;YACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;YACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;YACrB,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;SAC/B,CAAA;KACF,CAAA;CACF,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAA;IACZ,oBAAoB;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,gDAAgD;IAChD,QAAQ,EAAE,OAAO,CAAA;IACjB,wGAAwG;IACxG,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,WAAW,EAAE,MAAM,EAAE,CAAA;CACtB,CAAA;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,YAAY,GAAG,gBAAgB,CAQrE;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,kBAAkB,CAG1E"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Read-only state for scripted `?perf` receipts (bench/next-house, scripts/perf): the scene
|
|
2
|
+
// history and the viewer selection, so production builds can report undo entries and
|
|
3
|
+
// history pauses without dev-only store handles.
|
|
4
|
+
import { getSceneHistoryPauseDepth } from '@pascal-app/core';
|
|
5
|
+
export function readPerfHistory(store) {
|
|
6
|
+
const temporal = store.temporal.getState();
|
|
7
|
+
return {
|
|
8
|
+
past: temporal.pastStates.length,
|
|
9
|
+
future: temporal.futureStates.length,
|
|
10
|
+
tracking: temporal.isTracking,
|
|
11
|
+
pauseDepth: getSceneHistoryPauseDepth(),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function readPerfSelection(store) {
|
|
15
|
+
const { buildingId, levelId, zoneId, selectedIds } = store.getState().selection;
|
|
16
|
+
return { buildingId, levelId, zoneId, selectedIds: [...selectedIds] };
|
|
17
|
+
}
|
|
@@ -1,7 +1,31 @@
|
|
|
1
1
|
import { events as createWebEvents, type EventManager, type RootStore } from '@react-three/fiber';
|
|
2
2
|
import * as THREE from 'three';
|
|
3
|
+
/** A diagnostic the viewer hands its host, shaped for an error reporter's breadcrumb trail. */
|
|
4
|
+
export type ViewerBreadcrumb = {
|
|
5
|
+
category: string;
|
|
6
|
+
message: string;
|
|
7
|
+
level: 'info' | 'warning';
|
|
8
|
+
data?: Record<string, unknown>;
|
|
9
|
+
};
|
|
10
|
+
export type ViewerBreadcrumbHandler = (breadcrumb: ViewerBreadcrumb) => void;
|
|
3
11
|
/** Opt in only when the raycast appends hits without reading prior hits or mutating scene/query state. */
|
|
4
12
|
export declare function markPureRaycast<T extends THREE.Object3D['raycast']>(raycast: T): T;
|
|
5
|
-
export declare function choosePointerEvents(search?: string): typeof createWebEvents;
|
|
6
|
-
|
|
13
|
+
export declare function choosePointerEvents(search?: string, onBreadcrumb?: ViewerBreadcrumbHandler): typeof createWebEvents;
|
|
14
|
+
/**
|
|
15
|
+
* Whether a mesh has a material for everything a raycast reads: three indexes
|
|
16
|
+
* a material array by each geometry group, and one with no entry throws on
|
|
17
|
+
* `material.side`. A mesh caught mid-swap (a preview restored an older array
|
|
18
|
+
* over a rebuilt geometry) is skipped for this move instead of taking the whole
|
|
19
|
+
* pointer event down; its children are still tested. `material` asks the same of
|
|
20
|
+
* an array the mesh would wear.
|
|
21
|
+
*/
|
|
22
|
+
export declare function hasMaterialsForGroups(object: THREE.Object3D, material?: THREE.Material | THREE.Material[] | undefined): boolean;
|
|
23
|
+
type SkipInvalidMaterial = (object: THREE.Object3D) => void;
|
|
24
|
+
/**
|
|
25
|
+
* Three's recursive `intersectObject`, skipping meshes `hasMaterialsForGroups`
|
|
26
|
+
* rejects. A raycaster with its own `intersectObject` is asked directly.
|
|
27
|
+
*/
|
|
28
|
+
export declare function intersectDrawable(raycaster: THREE.Raycaster, object: THREE.Object3D, skip: SkipInvalidMaterial): THREE.Intersection[];
|
|
29
|
+
export declare function createPascalPointerEvents(store: RootStore, onBreadcrumb?: ViewerBreadcrumbHandler): EventManager<HTMLElement>;
|
|
30
|
+
export {};
|
|
7
31
|
//# sourceMappingURL=pointer-events.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pointer-events.d.ts","sourceRoot":"","sources":["../../src/lib/pointer-events.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pointer-events.d.ts","sourceRoot":"","sources":["../../src/lib/pointer-events.ts"],"names":[],"mappings":"AA8BA,OAAO,EACL,MAAM,IAAI,eAAe,EAGzB,KAAK,YAAY,EAMjB,KAAK,SAAS,EAEf,MAAM,oBAAoB,CAAA;AAC3B,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,+FAA+F;AAC/F,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC/B,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG,CAAC,UAAU,EAAE,gBAAgB,KAAK,IAAI,CAAA;AAmC5E,0GAA0G;AAC1G,wBAAgB,eAAe,CAAC,CAAC,SAAS,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,CAGlF;AAYD,wBAAgB,mBAAmB,CACjC,MAAM,SAA8D,EACpE,YAAY,CAAC,EAAE,uBAAuB,GACrC,OAAO,eAAe,CAIxB;AAiED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,KAAK,CAAC,QAAQ,EACtB,QAAQ,GAAE,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,EAAE,GAAG,SAA2C,GACxF,OAAO,CAST;AAMD,KAAK,mBAAmB,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,KAAK,IAAI,CAAA;AAmC3D;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,KAAK,CAAC,SAAS,EAC1B,MAAM,EAAE,KAAK,CAAC,QAAQ,EACtB,IAAI,EAAE,mBAAmB,GACxB,KAAK,CAAC,YAAY,EAAE,CAiBtB;AA+GD,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,SAAS,EAChB,YAAY,CAAC,EAAE,uBAAuB,GACrC,YAAY,CAAC,WAAW,CAAC,CAmB3B"}
|