@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/store/use-viewer.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { create } from 'zustand';
|
|
3
3
|
import { persist } from 'zustand/middleware';
|
|
4
|
-
import { SCENE_THEME_IDS } from '../lib/scene-themes';
|
|
4
|
+
import { getSceneTheme, projectSceneTheme, SCENE_THEME_IDS, } from '../lib/scene-themes';
|
|
5
5
|
const CAMERA_MODES = ['perspective', 'orthographic'];
|
|
6
6
|
const RENDER_SHADINGS = ['solid', 'rendered'];
|
|
7
7
|
const COLOR_PRESETS = ['clay', 'white', 'mono', 'blueprint'];
|
|
@@ -89,7 +89,24 @@ function normalizeProjectPreferences(value) {
|
|
|
89
89
|
if (!preferences || typeof preferences !== 'object' || Array.isArray(preferences))
|
|
90
90
|
continue;
|
|
91
91
|
const record = preferences;
|
|
92
|
+
const look = record.look && typeof record.look === 'object'
|
|
93
|
+
? record.look
|
|
94
|
+
: undefined;
|
|
92
95
|
next[projectId] = {
|
|
96
|
+
...(look
|
|
97
|
+
? {
|
|
98
|
+
look: {
|
|
99
|
+
...(typeof look.theme === 'string' && SCENE_THEME_IDS.includes(look.theme)
|
|
100
|
+
? { theme: look.theme }
|
|
101
|
+
: {}),
|
|
102
|
+
...(typeof look.edges === 'string' && EDGE_MODES.includes(look.edges)
|
|
103
|
+
? { edges: look.edges }
|
|
104
|
+
: {}),
|
|
105
|
+
...(typeof look.shadows === 'boolean' ? { shadows: look.shadows } : {}),
|
|
106
|
+
shadingByContext: normalizeShadingByContext(look.shadingByContext),
|
|
107
|
+
},
|
|
108
|
+
}
|
|
109
|
+
: {}),
|
|
93
110
|
...(typeof record.showScans === 'boolean' ? { showScans: record.showScans } : {}),
|
|
94
111
|
...(typeof record.showGuides === 'boolean' ? { showGuides: record.showGuides } : {}),
|
|
95
112
|
...(typeof record.showGrid === 'boolean' ? { showGrid: record.showGrid } : {}),
|
|
@@ -120,6 +137,47 @@ function normalizePersistedViewerState(value) {
|
|
|
120
137
|
projectPreferences: normalizeProjectPreferences(state.projectPreferences),
|
|
121
138
|
};
|
|
122
139
|
}
|
|
140
|
+
/** The owner's pick (16 Oct): the section cut's clean edge over earth walls, and the simple fade. */
|
|
141
|
+
export const DEFAULT_UNDERGROUND_LOOK = {
|
|
142
|
+
edge: 'section-earth',
|
|
143
|
+
transition: 'fade',
|
|
144
|
+
};
|
|
145
|
+
function resolveLook(state, patch) {
|
|
146
|
+
const next = { ...state, ...patch };
|
|
147
|
+
const local = next.projectPreferences[next.projectId ?? '']?.look;
|
|
148
|
+
const savedTheme = local?.theme ?? next.projectPresentation?.theme ?? next.lookDefaults.theme;
|
|
149
|
+
const theme = getSceneTheme(next.previewSceneTheme ?? savedTheme);
|
|
150
|
+
return {
|
|
151
|
+
...patch,
|
|
152
|
+
savedSceneTheme: savedTheme,
|
|
153
|
+
sceneTheme: theme.id,
|
|
154
|
+
sceneAppearance: projectSceneTheme(theme, next.previewSceneTheme === null && !local?.theme ? next.projectPresentation : undefined),
|
|
155
|
+
shading: local?.shadingByContext?.[next.renderContext] ??
|
|
156
|
+
next.projectPresentation?.shading ??
|
|
157
|
+
next.shadingByContext[next.renderContext] ??
|
|
158
|
+
next.contextDefaultShading,
|
|
159
|
+
edges: local?.edges ?? next.projectPresentation?.edges ?? next.lookDefaults.edges,
|
|
160
|
+
shadows: local?.shadows ?? next.projectPresentation?.shadows ?? next.lookDefaults.shadows,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function chooseLook(state, look) {
|
|
164
|
+
const key = state.projectId ?? '';
|
|
165
|
+
const previous = state.projectPreferences[key];
|
|
166
|
+
const { shadingByContext, ...defaults } = look;
|
|
167
|
+
return resolveLook(state, {
|
|
168
|
+
...(!state.projectId
|
|
169
|
+
? {
|
|
170
|
+
lookDefaults: { ...state.lookDefaults, ...defaults },
|
|
171
|
+
shadingByContext: { ...state.shadingByContext, ...shadingByContext },
|
|
172
|
+
}
|
|
173
|
+
: {}),
|
|
174
|
+
projectPreferences: {
|
|
175
|
+
...state.projectPreferences,
|
|
176
|
+
[key]: { ...previous, look: { ...previous?.look, ...look } },
|
|
177
|
+
},
|
|
178
|
+
...('theme' in look ? { previewSceneTheme: null } : {}),
|
|
179
|
+
});
|
|
180
|
+
}
|
|
123
181
|
const useViewer = create()(persist((set) => ({
|
|
124
182
|
selection: { buildingId: null, levelId: null, zoneId: null, selectedIds: [] },
|
|
125
183
|
focusedUnitId: null,
|
|
@@ -134,6 +192,15 @@ const useViewer = create()(persist((set) => ({
|
|
|
134
192
|
}
|
|
135
193
|
return { externalSelectedIds: ids };
|
|
136
194
|
}),
|
|
195
|
+
pointedIds: [],
|
|
196
|
+
setPointedIds: (ids) => set((state) => {
|
|
197
|
+
const next = ids ?? [];
|
|
198
|
+
if (state.pointedIds.length === next.length &&
|
|
199
|
+
state.pointedIds.every((id, index) => id === next[index])) {
|
|
200
|
+
return state;
|
|
201
|
+
}
|
|
202
|
+
return { pointedIds: next };
|
|
203
|
+
}),
|
|
137
204
|
hoverHighlightMode: 'default',
|
|
138
205
|
setHoverHighlightMode: (mode) => set((state) => (state.hoverHighlightMode === mode ? state : { hoverHighlightMode: mode })),
|
|
139
206
|
hoveredId: null,
|
|
@@ -141,9 +208,24 @@ const useViewer = create()(persist((set) => ({
|
|
|
141
208
|
cameraMode: 'perspective',
|
|
142
209
|
setCameraMode: (mode) => set({ cameraMode: mode }),
|
|
143
210
|
sceneTheme: 'studio',
|
|
144
|
-
|
|
211
|
+
sceneAppearance: getSceneTheme('studio'),
|
|
212
|
+
lookDefaults: { theme: 'studio', edges: 'soft', shadows: true },
|
|
213
|
+
projectPresentation: undefined,
|
|
214
|
+
setProjectPresentation: (projectPresentation) => set((state) => resolveLook(state, { projectPresentation })),
|
|
215
|
+
resetProjectLook: () => set((state) => {
|
|
216
|
+
const projectPreferences = { ...state.projectPreferences };
|
|
217
|
+
const key = state.projectId ?? '';
|
|
218
|
+
const { look: _look, ...rest } = projectPreferences[key] ?? {};
|
|
219
|
+
projectPreferences[key] = rest;
|
|
220
|
+
return resolveLook(state, { projectPreferences, previewSceneTheme: null });
|
|
221
|
+
}),
|
|
222
|
+
previewSceneTheme: null,
|
|
223
|
+
contextDefaultShading: 'rendered',
|
|
224
|
+
savedSceneTheme: 'studio',
|
|
225
|
+
setSceneTheme: (id) => set((state) => chooseLook(state, { theme: id })),
|
|
226
|
+
showSceneTheme: (id) => set((state) => resolveLook(state, { previewSceneTheme: id })),
|
|
145
227
|
renderContext: 'editor',
|
|
146
|
-
setRenderContext: (context) => set({ renderContext: context }),
|
|
228
|
+
setRenderContext: (context, defaultShading = 'rendered') => set((state) => resolveLook(state, { renderContext: context, contextDefaultShading: defaultShading })),
|
|
147
229
|
isExporting: false,
|
|
148
230
|
setExporting: (value) => set({ isExporting: value }),
|
|
149
231
|
itemLoadFailures: {},
|
|
@@ -161,18 +243,20 @@ const useViewer = create()(persist((set) => ({
|
|
|
161
243
|
setRenderPaused: (value) => set({ renderPaused: value }),
|
|
162
244
|
shading: 'rendered',
|
|
163
245
|
shadingByContext: {},
|
|
164
|
-
setShading: (shading) => set((state) => ({
|
|
165
|
-
|
|
166
|
-
|
|
246
|
+
setShading: (shading) => set((state) => chooseLook(state, {
|
|
247
|
+
shadingByContext: {
|
|
248
|
+
...state.projectPreferences[state.projectId ?? '']?.look?.shadingByContext,
|
|
249
|
+
[state.renderContext]: shading,
|
|
250
|
+
},
|
|
167
251
|
})),
|
|
168
252
|
textures: true,
|
|
169
253
|
setTextures: (textures) => set({ textures }),
|
|
170
254
|
colorPreset: 'clay',
|
|
171
255
|
setColorPreset: (preset) => set({ colorPreset: preset }),
|
|
172
256
|
edges: 'soft',
|
|
173
|
-
setEdges: (edges) => set({ edges }),
|
|
257
|
+
setEdges: (edges) => set((state) => chooseLook(state, { edges })),
|
|
174
258
|
shadows: true,
|
|
175
|
-
setShadows: (shadows) => set({ shadows }),
|
|
259
|
+
setShadows: (shadows) => set((state) => chooseLook(state, { shadows })),
|
|
176
260
|
unit: detectDefaultUnit(),
|
|
177
261
|
metricNotation: 'meters',
|
|
178
262
|
unitExplicit: false,
|
|
@@ -180,6 +264,22 @@ const useViewer = create()(persist((set) => ({
|
|
|
180
264
|
setMetricNotation: (metricNotation) => set({ unit: 'metric', metricNotation, unitExplicit: true }),
|
|
181
265
|
levelMode: 'stacked',
|
|
182
266
|
setLevelMode: (mode) => set({ levelMode: mode }),
|
|
267
|
+
hideLevelsAboveSelection: false,
|
|
268
|
+
levelsRevealing: [],
|
|
269
|
+
nodesRevealing: [],
|
|
270
|
+
belowGrade: false,
|
|
271
|
+
gradeStanding: 'at-grade',
|
|
272
|
+
groundWindowBox: null,
|
|
273
|
+
groundWindowVeiled: false,
|
|
274
|
+
groundMode: 'auto',
|
|
275
|
+
setGroundMode: (mode) => set({ groundMode: mode }),
|
|
276
|
+
hasLevelBelowGrade: false,
|
|
277
|
+
diveAvailable: false,
|
|
278
|
+
eyeUnderGround: false,
|
|
279
|
+
pluginFoundationLevelIds: [],
|
|
280
|
+
ambianceActive: false,
|
|
281
|
+
undergroundLook: DEFAULT_UNDERGROUND_LOOK,
|
|
282
|
+
setUndergroundLook: (look) => set((state) => ({ undergroundLook: { ...state.undergroundLook, ...look } })),
|
|
183
283
|
wallMode: 'up',
|
|
184
284
|
setWallMode: (mode) => set({ wallMode: mode }),
|
|
185
285
|
showScans: true,
|
|
@@ -234,16 +334,20 @@ const useViewer = create()(persist((set) => ({
|
|
|
234
334
|
setInkOpacity: (opacity) => set({ inkOpacity: opacity }),
|
|
235
335
|
projectId: null,
|
|
236
336
|
setProjectId: (id) => set((state) => {
|
|
237
|
-
if (
|
|
238
|
-
return
|
|
239
|
-
const prefs = state.projectPreferences?.[id] || {};
|
|
240
|
-
|
|
337
|
+
if (id === state.projectId)
|
|
338
|
+
return state;
|
|
339
|
+
const prefs = (id && state.projectPreferences?.[id]) || {};
|
|
340
|
+
const { '': _unscoped, ...projectPreferences } = state.projectPreferences;
|
|
341
|
+
return resolveLook(state, {
|
|
241
342
|
projectId: id,
|
|
343
|
+
projectPreferences,
|
|
344
|
+
projectPresentation: undefined,
|
|
345
|
+
previewSceneTheme: null,
|
|
242
346
|
showScans: prefs.showScans ?? true,
|
|
243
347
|
showGuides: prefs.showGuides ?? true,
|
|
244
348
|
showGrid: prefs.showGrid ?? true,
|
|
245
349
|
showMeasurements: prefs.showMeasurements ?? true,
|
|
246
|
-
};
|
|
350
|
+
});
|
|
247
351
|
}),
|
|
248
352
|
projectPreferences: {},
|
|
249
353
|
setSelection: (updates) => set((state) => {
|
|
@@ -295,25 +399,64 @@ const useViewer = create()(persist((set) => ({
|
|
|
295
399
|
setInputDragging: (dragging) => set({ inputDragging: dragging }),
|
|
296
400
|
}), {
|
|
297
401
|
name: 'viewer-preferences',
|
|
298
|
-
merge: (persistedState, currentState) =>
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
402
|
+
merge: (persistedState, currentState) => {
|
|
403
|
+
const persisted = normalizePersistedViewerState(persistedState);
|
|
404
|
+
return {
|
|
405
|
+
...currentState,
|
|
406
|
+
...persisted,
|
|
407
|
+
...resolveLook(currentState, {
|
|
408
|
+
...persisted,
|
|
409
|
+
lookDefaults: {
|
|
410
|
+
theme: persisted.sceneTheme ?? currentState.lookDefaults.theme,
|
|
411
|
+
edges: persisted.edges ?? currentState.lookDefaults.edges,
|
|
412
|
+
shadows: persisted.shadows ?? currentState.lookDefaults.shadows,
|
|
413
|
+
},
|
|
414
|
+
}),
|
|
415
|
+
};
|
|
416
|
+
},
|
|
302
417
|
partialize: (state) => ({
|
|
303
418
|
cameraMode: state.cameraMode,
|
|
304
|
-
sceneTheme: state.
|
|
419
|
+
sceneTheme: state.lookDefaults.theme,
|
|
305
420
|
shadingByContext: state.shadingByContext,
|
|
306
421
|
textures: state.textures,
|
|
307
422
|
colorPreset: state.colorPreset,
|
|
308
|
-
edges: state.edges,
|
|
309
|
-
shadows: state.shadows,
|
|
423
|
+
edges: state.lookDefaults.edges,
|
|
424
|
+
shadows: state.lookDefaults.shadows,
|
|
310
425
|
...(state.unitExplicit ? { unit: state.unit } : {}),
|
|
311
426
|
metricNotation: state.metricNotation,
|
|
312
427
|
levelMode: state.levelMode,
|
|
313
428
|
wallMode: state.wallMode,
|
|
314
|
-
projectPreferences: state.projectPreferences,
|
|
429
|
+
projectPreferences: Object.fromEntries(Object.entries(state.projectPreferences).filter(([key]) => key !== '')),
|
|
315
430
|
}),
|
|
316
431
|
}));
|
|
432
|
+
/**
|
|
433
|
+
* Whether the site's ground is drawn as a veil wholesale: a level below grade is shown, the
|
|
434
|
+
* ground on auto. A level only partly below grade is the exception — there the ground window
|
|
435
|
+
* veils just the ground in the way, and the rest stays solid. The walkthrough walks on its
|
|
436
|
+
* floors: its ground is always solid, whatever level the overview had shown.
|
|
437
|
+
*/
|
|
438
|
+
export function groundVeiled(state) {
|
|
439
|
+
return (state.belowGrade &&
|
|
440
|
+
state.groundMode === 'auto' &&
|
|
441
|
+
state.gradeStanding !== 'partly-below-grade' &&
|
|
442
|
+
state.walkthroughMode !== true);
|
|
443
|
+
}
|
|
444
|
+
/** Whether any ground is veiled now, wholesale or through the eye's window. */
|
|
445
|
+
export function anyGroundVeiled(state) {
|
|
446
|
+
return (groundVeiled(state) ||
|
|
447
|
+
(state.groundMode === 'auto' && state.groundWindowVeiled && state.walkthroughMode !== true));
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Whether the site draws its own ground. `showGround: false` says other geometry supplies the
|
|
451
|
+
* ground (an authored landscape), which stands on a level above a basement; while a level below
|
|
452
|
+
* grade is shown, the level display hides the storeys above it (`belowGrade`) and the landscape
|
|
453
|
+
* with them. The site's ground stands in then, with its veil, hole and pit, so the basement is
|
|
454
|
+
* seen through an opening and not through nothing. With a level at or above grade shown, the
|
|
455
|
+
* landscape is shown and `showGround` is the author's.
|
|
456
|
+
*/
|
|
457
|
+
export function siteGroundShown(site, state) {
|
|
458
|
+
return site.showGround !== false || state.belowGrade;
|
|
459
|
+
}
|
|
317
460
|
/** Apply an authoritative country code (e.g. IP-derived by the host app) as
|
|
318
461
|
* the unit default. Stronger signal than the timezone heuristic used at store
|
|
319
462
|
* creation, but still a default: it never overrides an explicit user choice
|
|
@@ -1,6 +1,19 @@
|
|
|
1
|
-
import { type CeilingNode } from '@pascal-app/core';
|
|
1
|
+
import { type CeilingNode, type CeilingSurfaceCell, useScene } from '@pascal-app/core';
|
|
2
2
|
import * as THREE from 'three';
|
|
3
|
+
/** Name of the meshes a ceiling draws its paint regions with, one per region. */
|
|
4
|
+
export declare const CEILING_REGION_MESH = "ceiling-region";
|
|
5
|
+
/**
|
|
6
|
+
* The material a ceiling draws a region finish with. The ceiling's renderer
|
|
7
|
+
* owns materials (theme, textures, scene materials) and leaves this on the
|
|
8
|
+
* registered mesh; the system builds geometry and asks it.
|
|
9
|
+
*/
|
|
10
|
+
export type CeilingRegionMaterial = (finish: CeilingSurfaceCell['finish']) => THREE.Material;
|
|
11
|
+
type SceneNodes = ReturnType<typeof useScene.getState>['nodes'];
|
|
3
12
|
export declare const CeilingSystem: () => null;
|
|
13
|
+
/**
|
|
14
|
+
* Updates the geometry for a single ceiling
|
|
15
|
+
*/
|
|
16
|
+
export declare function updateCeilingGeometry(node: CeilingNode, mesh: THREE.Mesh, extraHoles?: Array<Array<[number, number]>>, nodes?: SceneNodes): void;
|
|
4
17
|
/**
|
|
5
18
|
* Generates flat ceiling geometry from polygon (no extrusion).
|
|
6
19
|
*
|
|
@@ -9,4 +22,5 @@ export declare const CeilingSystem: () => null;
|
|
|
9
22
|
* stored on the node — they are recomputed on every rebuild.
|
|
10
23
|
*/
|
|
11
24
|
export declare function generateCeilingGeometry(ceilingNode: CeilingNode, extraHoles?: Array<Array<[number, number]>>): THREE.BufferGeometry;
|
|
25
|
+
export {};
|
|
12
26
|
//# sourceMappingURL=ceiling-system.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ceiling-system.d.ts","sourceRoot":"","sources":["../../../src/systems/ceiling/ceiling-system.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"ceiling-system.d.ts","sourceRoot":"","sources":["../../../src/systems/ceiling/ceiling-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,WAAW,EAChB,KAAK,kBAAkB,EAYvB,QAAQ,EACT,MAAM,kBAAkB,CAAA;AAEzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,iFAAiF;AACjF,eAAO,MAAM,mBAAmB,mBAAmB,CAAA;AAEnD;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,QAAQ,CAAC,KAAK,KAAK,CAAC,QAAQ,CAAA;AAE5F,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAA;AAa/D,eAAO,MAAM,aAAa,YA4BzB,CAAA;AAsCD;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,KAAK,CAAC,IAAI,EAChB,UAAU,GAAE,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAM,EAC/C,KAAK,GAAE,UAAsC,QA8C9C;AAuDD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,WAAW,EACxB,UAAU,GAAE,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAM,GAC9C,KAAK,CAAC,cAAc,CA6DtB"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { getEffectiveNode, nodeRegistry, resolveCeilingHeight, sceneRegistry, useLiveTransforms, useScene, } from '@pascal-app/core';
|
|
1
|
+
import { CEILING_DRAW_OFFSET, CEILING_SURFACE_ROLE, ceilingPaintRegions, computeCeilingSurfaceCells, getEffectiveCutterNode, getEffectiveNode, hostedCutterHoles, isScriptedNode, nodeRegistry, resolveCeilingHeight, sceneRegistry, useLiveTransforms, useScene, } from '@pascal-app/core';
|
|
2
2
|
import { useFrame } from '@react-three/fiber';
|
|
3
3
|
import * as THREE from 'three';
|
|
4
|
+
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
|
|
4
5
|
import { mergeSurfaceHolePolygons } from '../surface-hole-geometry';
|
|
6
|
+
/** Name of the meshes a ceiling draws its paint regions with, one per region. */
|
|
7
|
+
export const CEILING_REGION_MESH = 'ceiling-region';
|
|
5
8
|
function ensureUv2Attribute(geometry) {
|
|
6
9
|
const uv = geometry.getAttribute('uv');
|
|
7
10
|
if (!uv)
|
|
@@ -49,29 +52,53 @@ export const CeilingSystem = () => {
|
|
|
49
52
|
* participate just by declaring `capabilities.ceilingCut` on its definition.
|
|
50
53
|
*/
|
|
51
54
|
function collectCeilingHoles(ceiling, nodes) {
|
|
52
|
-
|
|
55
|
+
// Only the ceiling's own children can bind to it.
|
|
56
|
+
const hosted = { [ceiling.id]: ceiling };
|
|
57
|
+
for (const childId of ceiling.children ?? []) {
|
|
58
|
+
const child = nodes[childId];
|
|
59
|
+
if (child)
|
|
60
|
+
hosted[child.id] = getEffectiveCutterNode(child);
|
|
61
|
+
}
|
|
62
|
+
const holes = hostedCutterHoles(ceiling, hosted);
|
|
53
63
|
for (const childId of ceiling.children ?? []) {
|
|
54
64
|
const child = nodes[childId];
|
|
55
65
|
if (!child)
|
|
56
66
|
continue;
|
|
67
|
+
if (isScriptedNode(child) && child.source.manifest.cutters?.length)
|
|
68
|
+
continue;
|
|
57
69
|
const def = nodeRegistry.get(child.type);
|
|
58
70
|
const hole = def?.capabilities?.ceilingCut?.buildCeilingHole(child);
|
|
59
71
|
if (hole)
|
|
60
72
|
holes.push(hole);
|
|
61
73
|
}
|
|
74
|
+
for (const [, def] of nodeRegistry.entries())
|
|
75
|
+
holes.push(...(def.capabilities?.ceilingCut?.holesFor?.(ceiling) ?? []));
|
|
62
76
|
return holes;
|
|
63
77
|
}
|
|
64
78
|
/**
|
|
65
79
|
* Updates the geometry for a single ceiling
|
|
66
80
|
*/
|
|
67
|
-
function updateCeilingGeometry(node, mesh, extraHoles = [], nodes = useScene.getState().nodes) {
|
|
81
|
+
export function updateCeilingGeometry(node, mesh, extraHoles = [], nodes = useScene.getState().nodes) {
|
|
68
82
|
const newGeo = generateCeilingGeometry(node, extraHoles);
|
|
83
|
+
// Painted regions partition the underside: the ceiling's own mesh keeps what
|
|
84
|
+
// no region covers, and each region draws as its own single-material mesh,
|
|
85
|
+
// so batching buckets them by material like any other surface.
|
|
86
|
+
const regions = node.polygon.length >= 3 ? ceilingPaintRegions(node, nodes) : [];
|
|
87
|
+
const cells = regions.length
|
|
88
|
+
? computeCeilingSurfaceCells(node.polygon, [...mergeSurfaceHolePolygons(node.holes || []), ...extraHoles], regions)
|
|
89
|
+
: null;
|
|
90
|
+
const surface = cells?.find((cell) => cell.role === CEILING_SURFACE_ROLE);
|
|
69
91
|
mesh.geometry.dispose();
|
|
70
|
-
mesh.geometry = newGeo;
|
|
92
|
+
mesh.geometry = cells ? flatCeilingGeometry(surface?.polygons ?? []) : newGeo;
|
|
93
|
+
mesh.userData.paintRole = CEILING_SURFACE_ROLE;
|
|
94
|
+
syncCeilingRegionMeshes(mesh, (cells ?? []).filter((cell) => cell.role !== CEILING_SURFACE_ROLE));
|
|
71
95
|
const gridMesh = mesh.getObjectByName('ceiling-grid');
|
|
72
96
|
if (gridMesh) {
|
|
73
97
|
gridMesh.geometry.dispose();
|
|
74
|
-
gridMesh.geometry = newGeo.clone();
|
|
98
|
+
gridMesh.geometry = cells ? newGeo : newGeo.clone();
|
|
99
|
+
}
|
|
100
|
+
else if (cells) {
|
|
101
|
+
newGeo.dispose();
|
|
75
102
|
}
|
|
76
103
|
// Position at the ceiling height and reset X/Z so live-drag mesh
|
|
77
104
|
// offsets (set by move tools during the drag) don't leak into the
|
|
@@ -85,7 +112,62 @@ function updateCeilingGeometry(node, mesh, extraHoles = [], nodes = useScene.get
|
|
|
85
112
|
// follows-mode ceiling re-parks under the current plane on every rebuild
|
|
86
113
|
// (level-height edits / covering-slab changes dirty-mark ceilings).
|
|
87
114
|
// Slight offset to avoid z-fighting with upper-level slabs.
|
|
88
|
-
mesh.position.y =
|
|
115
|
+
mesh.position.y =
|
|
116
|
+
resolveCeilingHeight(node, nodes) - CEILING_DRAW_OFFSET + (liveTransform?.position[1] ?? 0);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* One child mesh per region cell, reused by position so a repaint keeps its
|
|
120
|
+
* mesh; surplus meshes go. The material comes from the renderer's resolver.
|
|
121
|
+
*/
|
|
122
|
+
function syncCeilingRegionMeshes(mesh, cells) {
|
|
123
|
+
const existing = mesh.children.filter((child) => child.isMesh && child.name === CEILING_REGION_MESH);
|
|
124
|
+
const resolve = mesh.userData.ceilingRegionMaterial;
|
|
125
|
+
cells.forEach((cell, index) => {
|
|
126
|
+
let region = existing[index];
|
|
127
|
+
if (!region) {
|
|
128
|
+
region = new THREE.Mesh();
|
|
129
|
+
region.name = CEILING_REGION_MESH;
|
|
130
|
+
region.castShadow = true;
|
|
131
|
+
region.receiveShadow = true;
|
|
132
|
+
mesh.add(region);
|
|
133
|
+
}
|
|
134
|
+
else
|
|
135
|
+
region.geometry.dispose();
|
|
136
|
+
region.geometry = flatCeilingGeometry(cell.polygons);
|
|
137
|
+
region.userData.paintRole = cell.role;
|
|
138
|
+
region.userData.finish = cell.finish;
|
|
139
|
+
region.userData.__fromGeometry = true;
|
|
140
|
+
region.material = resolve?.(cell.finish) ?? mesh.material;
|
|
141
|
+
});
|
|
142
|
+
for (const surplus of existing.slice(cells.length)) {
|
|
143
|
+
surplus.geometry.dispose();
|
|
144
|
+
surplus.removeFromParent();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/** Flat geometry for level-XZ pieces, laid out the way `generateCeilingGeometry` lays a ceiling. */
|
|
148
|
+
function flatCeilingGeometry(pieces) {
|
|
149
|
+
const parts = pieces
|
|
150
|
+
.filter((piece) => piece.outer.length >= 3)
|
|
151
|
+
.map((piece) => {
|
|
152
|
+
const shape = new THREE.Shape(piece.outer.map(([x, z]) => new THREE.Vector2(x, -z)));
|
|
153
|
+
for (const hole of piece.holes)
|
|
154
|
+
if (hole.length >= 3)
|
|
155
|
+
shape.holes.push(new THREE.Path(hole.map(([x, z]) => new THREE.Vector2(x, -z))));
|
|
156
|
+
const geometry = new THREE.ShapeGeometry(shape);
|
|
157
|
+
geometry.rotateX(-Math.PI / 2);
|
|
158
|
+
geometry.computeVertexNormals();
|
|
159
|
+
ensureUv2Attribute(geometry);
|
|
160
|
+
return geometry;
|
|
161
|
+
});
|
|
162
|
+
if (parts.length === 0)
|
|
163
|
+
return generateCeilingGeometry({ polygon: [] });
|
|
164
|
+
if (parts.length === 1)
|
|
165
|
+
return parts[0];
|
|
166
|
+
const merged = mergeGeometries(parts) ?? parts[0];
|
|
167
|
+
for (const part of parts)
|
|
168
|
+
if (part !== merged)
|
|
169
|
+
part.dispose();
|
|
170
|
+
return merged;
|
|
89
171
|
}
|
|
90
172
|
/**
|
|
91
173
|
* Generates flat ceiling geometry from polygon (no extrusion).
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type DustPool } from '../../lib/construction-dust';
|
|
2
|
+
/**
|
|
3
|
+
* Draws a reveal's dust: one instanced mesh of camera-facing soft discs, as
|
|
4
|
+
* many as the pool holds, on the grid layer: inside the scene pass, so walls
|
|
5
|
+
* in front hide it (on the overlay layer it showed through them, Victor run
|
|
6
|
+
* 12), with no depth written, so the ink never outlines it; thumbnails leave
|
|
7
|
+
* it out. Renders nothing itself; it owns the mesh, adds it to the scene and
|
|
8
|
+
* disposes it on unmount.
|
|
9
|
+
*/
|
|
10
|
+
export declare function ConstructionDust({ pool, clock }: {
|
|
11
|
+
pool: DustPool;
|
|
12
|
+
clock: {
|
|
13
|
+
current: number;
|
|
14
|
+
};
|
|
15
|
+
}): null;
|
|
16
|
+
//# sourceMappingURL=construction-dust.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"construction-dust.d.ts","sourceRoot":"","sources":["../../../src/systems/construction-reveal/construction-dust.tsx"],"names":[],"mappings":"AAcA,OAAO,EAAE,KAAK,QAAQ,EAAkB,MAAM,6BAA6B,CAAA;AAI3E;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,QA8E/F"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { emitter } from '@pascal-app/core';
|
|
3
|
+
import { useFrame, useThree } from '@react-three/fiber';
|
|
4
|
+
import { useEffect, useMemo } from 'react';
|
|
5
|
+
import { DynamicDrawUsage, InstancedBufferAttribute, InstancedMesh, Matrix4, PlaneGeometry, Quaternion, Vector3, } from 'three';
|
|
6
|
+
import { dustSpriteSize } from '../../lib/construction-dust';
|
|
7
|
+
import { GRID_LAYER } from '../../lib/layers';
|
|
8
|
+
import { createDustMaterial } from './dust-material';
|
|
9
|
+
/**
|
|
10
|
+
* Draws a reveal's dust: one instanced mesh of camera-facing soft discs, as
|
|
11
|
+
* many as the pool holds, on the grid layer: inside the scene pass, so walls
|
|
12
|
+
* in front hide it (on the overlay layer it showed through them, Victor run
|
|
13
|
+
* 12), with no depth written, so the ink never outlines it; thumbnails leave
|
|
14
|
+
* it out. Renders nothing itself; it owns the mesh, adds it to the scene and
|
|
15
|
+
* disposes it on unmount.
|
|
16
|
+
*/
|
|
17
|
+
export function ConstructionDust({ pool, clock }) {
|
|
18
|
+
const scene = useThree((state) => state.scene);
|
|
19
|
+
const camera = useThree((state) => state.camera);
|
|
20
|
+
const height = useThree((state) => state.size.height);
|
|
21
|
+
const dust = useMemo(() => {
|
|
22
|
+
const geometry = new PlaneGeometry(1, 1);
|
|
23
|
+
const alpha = new InstancedBufferAttribute(new Float32Array(pool.capacity), 1);
|
|
24
|
+
alpha.setUsage(DynamicDrawUsage);
|
|
25
|
+
const material = createDustMaterial(alpha);
|
|
26
|
+
const mesh = new InstancedMesh(geometry, material, pool.capacity);
|
|
27
|
+
mesh.name = 'construction-dust';
|
|
28
|
+
mesh.instanceMatrix.setUsage(DynamicDrawUsage);
|
|
29
|
+
mesh.layers.set(GRID_LAYER);
|
|
30
|
+
mesh.frustumCulled = false;
|
|
31
|
+
mesh.raycast = () => { };
|
|
32
|
+
mesh.count = 0;
|
|
33
|
+
mesh.visible = false;
|
|
34
|
+
return { mesh, alpha, geometry, material };
|
|
35
|
+
}, [pool]);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
scene.add(dust.mesh);
|
|
38
|
+
// A capture clones the scene in the same tick: no dust in it, whatever the camera layers.
|
|
39
|
+
const hide = () => {
|
|
40
|
+
dust.mesh.visible = false;
|
|
41
|
+
dust.mesh.count = 0;
|
|
42
|
+
};
|
|
43
|
+
emitter.on('thumbnail:before-capture', hide);
|
|
44
|
+
return () => {
|
|
45
|
+
emitter.off('thumbnail:before-capture', hide);
|
|
46
|
+
scene.remove(dust.mesh);
|
|
47
|
+
dust.geometry.dispose();
|
|
48
|
+
dust.material.dispose();
|
|
49
|
+
dust.mesh.dispose();
|
|
50
|
+
};
|
|
51
|
+
}, [dust, scene]);
|
|
52
|
+
const write = useMemo(() => {
|
|
53
|
+
const matrix = new Matrix4();
|
|
54
|
+
const position = new Vector3();
|
|
55
|
+
const size = new Vector3();
|
|
56
|
+
const facing = new Quaternion();
|
|
57
|
+
return {
|
|
58
|
+
facing,
|
|
59
|
+
sprite: (slot, x, y, z, scale, alpha) => {
|
|
60
|
+
position.set(x, y, z);
|
|
61
|
+
const drawn = dustSpriteSize(scale, camera, height, position);
|
|
62
|
+
size.set(drawn, drawn, drawn);
|
|
63
|
+
dust.mesh.setMatrixAt(slot, matrix.compose(position, facing, size));
|
|
64
|
+
dust.alpha.setX(slot, alpha);
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}, [dust, camera, height]);
|
|
68
|
+
useFrame(() => {
|
|
69
|
+
const { mesh, alpha } = dust;
|
|
70
|
+
if (pool.takeWarm()) {
|
|
71
|
+
// One sprite with no size and no alpha: drawn, so its pipeline is built, and nothing to see.
|
|
72
|
+
write.facing.copy(camera.quaternion);
|
|
73
|
+
write.sprite(0, 0, 0, 0, 0, 0);
|
|
74
|
+
mesh.count = 1;
|
|
75
|
+
mesh.visible = true;
|
|
76
|
+
mesh.instanceMatrix.needsUpdate = true;
|
|
77
|
+
alpha.needsUpdate = true;
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (!mesh.visible && pool.quietAt(clock.current))
|
|
81
|
+
return;
|
|
82
|
+
write.facing.copy(camera.quaternion);
|
|
83
|
+
const count = pool.step(clock.current, write.sprite);
|
|
84
|
+
mesh.count = count;
|
|
85
|
+
mesh.visible = count > 0;
|
|
86
|
+
if (count === 0)
|
|
87
|
+
return;
|
|
88
|
+
mesh.instanceMatrix.needsUpdate = true;
|
|
89
|
+
alpha.needsUpdate = true;
|
|
90
|
+
});
|
|
91
|
+
return null;
|
|
92
|
+
}
|