@pascal-app/viewer 1.0.0 → 1.0.1
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/README.md +12 -0
- package/dist/capture/capture-runtime.js +2 -32
- package/dist/capture/layers/device-motion-layer.d.ts.map +1 -1
- package/dist/capture/layers/device-motion-layer.js +5 -7
- package/dist/capture/stream-rendering.d.ts.map +1 -1
- package/dist/capture/stream-rendering.js +2 -1
- package/dist/capture/use-json-artifact.d.ts +2 -0
- package/dist/capture/use-json-artifact.d.ts.map +1 -0
- package/dist/capture/use-json-artifact.js +42 -0
- package/dist/components/viewer/glb-scene.d.ts.map +1 -1
- package/dist/components/viewer/glb-scene.js +3 -1
- package/dist/components/viewer/post-processing.d.ts.map +1 -1
- package/dist/components/viewer/post-processing.js +6 -0
- package/dist/components/viewer/selection-manager.d.ts.map +1 -1
- package/dist/components/viewer/selection-manager.js +8 -16
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/isolation.d.ts +14 -30
- package/dist/lib/isolation.d.ts.map +1 -1
- package/dist/lib/isolation.js +62 -66
- package/dist/lib/materials.d.ts.map +1 -1
- package/dist/lib/materials.js +2 -1
- package/dist/lib/scene-visibility.js +3 -1
- package/dist/lib/snapshot-pipeline.d.ts +3 -1
- package/dist/lib/snapshot-pipeline.d.ts.map +1 -1
- package/dist/lib/snapshot-pipeline.js +32 -16
- package/dist/store/use-viewer.d.ts +3 -1
- package/dist/store/use-viewer.d.ts.map +1 -1
- package/dist/store/use-viewer.js +2 -0
- package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -1
- package/dist/systems/floor-elevation/floor-elevation-system.js +7 -3
- package/dist/systems/geometry/geometry-system.d.ts.map +1 -1
- package/dist/systems/geometry/geometry-system.js +4 -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/zone/zone-system.d.ts.map +1 -1
- package/dist/systems/zone/zone-system.js +28 -2
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -175,6 +175,18 @@ colors; dollhouse enables front-face rendering for surface previews and room mod
|
|
|
175
175
|
inward-facing room surfaces from outside. It changes per-instance materials, not geometry or
|
|
176
176
|
loader-cached materials. Omitting these options preserves the existing presentation.
|
|
177
177
|
|
|
178
|
+
## Scene Snapshots
|
|
179
|
+
|
|
180
|
+
`createSnapshotPipeline` renders offscreen snapshots with the scene theme's backdrop by default.
|
|
181
|
+
Inside a `Viewer`, pass the source returned by `useSceneAtmosphere()` as `atmosphere` to capture
|
|
182
|
+
the active presentation's sky instead. Recreate the pipeline when that source changes; updates
|
|
183
|
+
to its existing uniforms do not require rebuilding it.
|
|
184
|
+
|
|
185
|
+
Use `applyEnvironment({ ..., transparent: true })` to retain scene alpha without compositing a
|
|
186
|
+
backdrop. The snapshot scene pass owns its transparent clear so intermediate post-processing
|
|
187
|
+
passes cannot turn empty sky into opaque pixels. Dispose the pipeline when it is replaced or
|
|
188
|
+
no longer needed.
|
|
189
|
+
|
|
178
190
|
## Viewer State
|
|
179
191
|
|
|
180
192
|
```typescript
|
|
@@ -7,7 +7,6 @@ import { Suspense, useCallback, useEffect, useMemo, useRef, useState, } from 're
|
|
|
7
7
|
import { ErrorBoundary } from '../components/error-boundary';
|
|
8
8
|
import { useNodeEvents } from '../hooks/use-node-events';
|
|
9
9
|
import useViewer from '../store/use-viewer';
|
|
10
|
-
import { rewriteLoopbackAssetUrl } from './asset-url';
|
|
11
10
|
import { resolveCaptureFrameMatrix } from './frame';
|
|
12
11
|
import { isCaptureSessionVisible, isCaptureStreamVisible } from './layer-visibility';
|
|
13
12
|
import { CaptureDeviceMotionLayer } from './layers/device-motion-layer';
|
|
@@ -17,6 +16,7 @@ import { CaptureSurfaceMeshLayer } from './layers/surface-mesh-layer';
|
|
|
17
16
|
import { useCaptureSource } from './source-state';
|
|
18
17
|
import { captureModelFormat, isCaptureModelArtifact, isCapturePointCloudArtifact, isCaptureStreamRenderable, streamHydratesJsonPayload, } from './stream-rendering';
|
|
19
18
|
import { parseDeviceTrajectoryPackets, parseDeviceTrajectoryPayload } from './trajectory';
|
|
19
|
+
import { useJsonArtifactPayload } from './use-json-artifact';
|
|
20
20
|
const EMPTY_RENDERERS = {};
|
|
21
21
|
const EMPTY_LAYER_VISIBILITY = {};
|
|
22
22
|
export function CaptureRuntime({ defaultLayerVisibility = EMPTY_LAYER_VISIBILITY, maxPacketsPerStream = 32, onError, renderers = EMPTY_RENDERERS, resolveSource, retryKey = 0, }) {
|
|
@@ -135,7 +135,7 @@ export function CaptureStreamLayer({ descriptor, meshPresentation, packets, rend
|
|
|
135
135
|
if (Renderer) {
|
|
136
136
|
content = (_jsx(Renderer, { artifactUrl: artifactUrl, descriptor: descriptor, meshPresentation: meshPresentation, packets: packets, scan: scan, source: source, stream: stream, streamEpoch: streamEpoch }));
|
|
137
137
|
}
|
|
138
|
-
else if (layerKey === 'model' &&
|
|
138
|
+
else if ((layerKey === 'model' || layerKey === 'spaceformScene') &&
|
|
139
139
|
isCaptureModelArtifact(stream.artifact) &&
|
|
140
140
|
stream.artifact &&
|
|
141
141
|
artifactUrl) {
|
|
@@ -154,36 +154,6 @@ export function CaptureStreamLayer({ descriptor, meshPresentation, packets, rend
|
|
|
154
154
|
return content;
|
|
155
155
|
return (_jsx("group", { matrix: frameMatrix, matrixAutoUpdate: false, children: content }));
|
|
156
156
|
}
|
|
157
|
-
function useJsonArtifactPayload(url) {
|
|
158
|
-
const [error, setError] = useState(null);
|
|
159
|
-
const [payload, setPayload] = useState(null);
|
|
160
|
-
useEffect(() => {
|
|
161
|
-
setError(null);
|
|
162
|
-
setPayload(null);
|
|
163
|
-
if (!url)
|
|
164
|
-
return;
|
|
165
|
-
const abort = new AbortController();
|
|
166
|
-
void fetch(rewriteLoopbackAssetUrl(url), { signal: abort.signal })
|
|
167
|
-
.then(async (response) => {
|
|
168
|
-
if (!response.ok)
|
|
169
|
-
throw new Error(`Could not load ${url}: ${response.status}`);
|
|
170
|
-
return (await response.json());
|
|
171
|
-
})
|
|
172
|
-
.then((data) => {
|
|
173
|
-
if (!abort.signal.aborted)
|
|
174
|
-
setPayload(data);
|
|
175
|
-
})
|
|
176
|
-
.catch((cause) => {
|
|
177
|
-
if (abort.signal.aborted)
|
|
178
|
-
return;
|
|
179
|
-
setError(cause instanceof Error ? cause : new Error(`Could not load ${url}.`));
|
|
180
|
-
});
|
|
181
|
-
return () => abort.abort();
|
|
182
|
-
}, [url]);
|
|
183
|
-
if (error)
|
|
184
|
-
throw error;
|
|
185
|
-
return payload;
|
|
186
|
-
}
|
|
187
157
|
function useResolvedArtifact(source, artifact) {
|
|
188
158
|
const [error, setError] = useState(null);
|
|
189
159
|
const [url, setUrl] = useState(null);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"device-motion-layer.d.ts","sourceRoot":"","sources":["../../../src/capture/layers/device-motion-layer.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAE,KAAK,gBAAgB,EAA0B,MAAM,eAAe,CAAA;AAE7E,eAAO,MAAM,4BAA4B,IAAI,CAAA;AAE7C,wBAAgB,wBAAwB,CAAC,EACvC,SAAe,EACf,UAAU,GACX,EAAE;IACD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,gBAAgB,CAAA;CAC7B,+
|
|
1
|
+
{"version":3,"file":"device-motion-layer.d.ts","sourceRoot":"","sources":["../../../src/capture/layers/device-motion-layer.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAE,KAAK,gBAAgB,EAA0B,MAAM,eAAe,CAAA;AAE7E,eAAO,MAAM,4BAA4B,IAAI,CAAA;AAE7C,wBAAgB,wBAAwB,CAAC,EACvC,SAAe,EACf,UAAU,GACX,EAAE;IACD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,gBAAgB,CAAA;CAC7B,+BA2CA"}
|
|
@@ -36,7 +36,7 @@ export function CaptureDeviceMotionLayer({ lineWidth = 2.5, trajectory, }) {
|
|
|
36
36
|
deviceRef.current.position.copy(position);
|
|
37
37
|
deviceRef.current.quaternion.slerpQuaternions(fromQuaternion, toQuaternion, frame.alpha);
|
|
38
38
|
});
|
|
39
|
-
return (_jsxs("group", { children: [trajectorySegments.map(({ points, segment }) => (_jsx(CaptureLine, { color: "#
|
|
39
|
+
return (_jsxs("group", { children: [trajectorySegments.map(({ points, segment }) => (_jsx(CaptureLine, { color: "#39ff14", lineWidth: lineWidth, points: points }, segment))), _jsx("group", { ref: deviceRef, children: _jsx(CameraFrustum, { lineWidth: lineWidth }) })] }));
|
|
40
40
|
}
|
|
41
41
|
function CameraFrustum({ lineWidth }) {
|
|
42
42
|
const apex = [0, 0, 0];
|
|
@@ -52,24 +52,22 @@ function CameraFrustum({ lineWidth }) {
|
|
|
52
52
|
[bottomLeft, bottomRight],
|
|
53
53
|
[bottomRight, topRight],
|
|
54
54
|
].flat();
|
|
55
|
-
return (_jsxs("group", { children: [_jsx(CaptureLine, { color: "#
|
|
55
|
+
return (_jsxs("group", { scale: 1.35, children: [_jsx(CaptureLine, { color: "#ffee00", lineWidth: lineWidth, points: points, segments: true }), _jsxs("mesh", { children: [_jsx("sphereGeometry", { args: [0.035, 12, 12] }), _jsx("meshBasicMaterial", { color: "#ffee00", toneMapped: false })] })] }));
|
|
56
56
|
}
|
|
57
|
-
function CaptureLine({ color, lineWidth,
|
|
57
|
+
function CaptureLine({ color, lineWidth, points, segments = false, }) {
|
|
58
58
|
const line = useMemo(() => {
|
|
59
59
|
const geometry = new BufferGeometry().setFromPoints(points.map(([x, y, z]) => new Vector3(x, y, z)));
|
|
60
60
|
const material = new LineBasicMaterial({
|
|
61
61
|
color,
|
|
62
|
-
depthWrite: opacity >= 1,
|
|
63
62
|
linewidth: lineWidth,
|
|
64
|
-
|
|
65
|
-
transparent: opacity < 1,
|
|
63
|
+
toneMapped: false,
|
|
66
64
|
});
|
|
67
65
|
const object = segments
|
|
68
66
|
? new LineSegments(geometry, material)
|
|
69
67
|
: new ThreeLine(geometry, material);
|
|
70
68
|
object.frustumCulled = false;
|
|
71
69
|
return object;
|
|
72
|
-
}, [color, lineWidth,
|
|
70
|
+
}, [color, lineWidth, points, segments]);
|
|
73
71
|
useEffect(() => () => {
|
|
74
72
|
line.geometry.dispose();
|
|
75
73
|
line.material.dispose();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream-rendering.d.ts","sourceRoot":"","sources":["../../src/capture/stream-rendering.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAK7B,MAAM,0BAA0B,CAAA;AAOjC,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,MAAM,CAAA;AAEhD,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,uBAAuB,EAC/B,kBAAkB,GAAE,WAAW,CAAC,MAAM,CAAa,GAClD,OAAO,
|
|
1
|
+
{"version":3,"file":"stream-rendering.d.ts","sourceRoot":"","sources":["../../src/capture/stream-rendering.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAK7B,MAAM,0BAA0B,CAAA;AAOjC,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,MAAM,CAAA;AAEhD,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,uBAAuB,EAC/B,kBAAkB,GAAE,WAAW,CAAC,MAAM,CAAa,GAClD,OAAO,CA4BT;AAID;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,uBAAuB,GAAG,OAAO,CAKlF;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,wBAAwB,GAAG,SAAS,GAAG,OAAO,CAE9F;AAED,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,wBAAwB,GAAG,SAAS,GAC7C,kBAAkB,GAAG,IAAI,CAS3B;AAED,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,wBAAwB,GAAG,SAAS,GAC7C,OAAO,CAGT"}
|
|
@@ -9,8 +9,9 @@ export function isCaptureStreamRenderable(stream, customRendererKeys = new Set()
|
|
|
9
9
|
const layerKey = captureLayerKey(stream);
|
|
10
10
|
if (customRendererKeys.has(layerKey) || customRendererKeys.has(stream.kind))
|
|
11
11
|
return true;
|
|
12
|
-
if (layerKey === 'model')
|
|
12
|
+
if (layerKey === 'model' || layerKey === 'spaceformScene') {
|
|
13
13
|
return isCaptureModelArtifact(stream.artifact);
|
|
14
|
+
}
|
|
14
15
|
if (layerKey === 'deviceMotion') {
|
|
15
16
|
return (stream.availability === 'live' ||
|
|
16
17
|
streamHydratesJsonPayload(stream) ||
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-json-artifact.d.ts","sourceRoot":"","sources":["../../src/capture/use-json-artifact.ts"],"names":[],"mappings":"AAgBA,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAgClE"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { FileLoader } from 'three';
|
|
3
|
+
import { rewriteLoopbackAssetUrl } from './asset-url';
|
|
4
|
+
const requests = new Map();
|
|
5
|
+
function loadJsonArtifact(url) {
|
|
6
|
+
const cached = requests.get(url);
|
|
7
|
+
if (cached)
|
|
8
|
+
return cached;
|
|
9
|
+
const request = new FileLoader().setResponseType('json').loadAsync(url);
|
|
10
|
+
requests.set(url, request);
|
|
11
|
+
// Keep successful downloads across view switches, but let a remount retry failures.
|
|
12
|
+
void request.catch(() => requests.delete(url));
|
|
13
|
+
return request;
|
|
14
|
+
}
|
|
15
|
+
export function useJsonArtifactPayload(url) {
|
|
16
|
+
const resolvedUrl = url ? rewriteLoopbackAssetUrl(url) : null;
|
|
17
|
+
const [result, setResult] = useState(null);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (!resolvedUrl)
|
|
20
|
+
return;
|
|
21
|
+
let active = true;
|
|
22
|
+
void loadJsonArtifact(resolvedUrl).then((payload) => {
|
|
23
|
+
if (active)
|
|
24
|
+
setResult({ url: resolvedUrl, payload });
|
|
25
|
+
}, (cause) => {
|
|
26
|
+
if (active) {
|
|
27
|
+
setResult({
|
|
28
|
+
url: resolvedUrl,
|
|
29
|
+
error: cause instanceof Error ? cause : new Error('Could not load capture data.'),
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
return () => {
|
|
34
|
+
active = false;
|
|
35
|
+
};
|
|
36
|
+
}, [resolvedUrl]);
|
|
37
|
+
if (!result || result.url !== resolvedUrl)
|
|
38
|
+
return null;
|
|
39
|
+
if (result.error)
|
|
40
|
+
throw result.error;
|
|
41
|
+
return result.payload ?? null;
|
|
42
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"glb-scene.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/glb-scene.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,OAAO,EAAkC,MAAM,kBAAkB,CAAA;AAc/E,OAAO,EAAkB,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAqB3E,2EAA2E;AAC3E,MAAM,MAAM,QAAQ,GAAG;IAAE,EAAE,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAE/D,4EAA4E;AAC5E,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAEzE,mFAAmF;AACnF,MAAM,MAAM,QAAQ,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAA;AAE7D;8EAC8E;AAC9E,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAA;CAChD,GAAG,IAAI,CAAA;AA8NR;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,EACvB,GAAG,EACH,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,mBAAmB,GACpB,EAAE;IACD,GAAG,EAAE,MAAM,CAAA;IACX;oFACgF;IAChF,gBAAgB,CAAC,EAAE,kBAAkB,EAAE,CAAA;IACvC;mFAC+E;IAC/E,cAAc,CAAC,EAAE,OAAO,EAAE,CAAA;IAC1B;kFAC8E;IAC9E,YAAY,CAAC,EAAE,OAAO,EAAE,CAAA;IACxB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,IAAI,CAAA;IAC7C,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,WAAW,KAAK,IAAI,CAAA;IAClD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAA;IACzC,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAA;CACtD,+
|
|
1
|
+
{"version":3,"file":"glb-scene.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/glb-scene.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,OAAO,EAAkC,MAAM,kBAAkB,CAAA;AAc/E,OAAO,EAAkB,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAqB3E,2EAA2E;AAC3E,MAAM,MAAM,QAAQ,GAAG;IAAE,EAAE,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAE/D,4EAA4E;AAC5E,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAEzE,mFAAmF;AACnF,MAAM,MAAM,QAAQ,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAA;AAE7D;8EAC8E;AAC9E,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAA;CAChD,GAAG,IAAI,CAAA;AA8NR;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,EACvB,GAAG,EACH,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,mBAAmB,GACpB,EAAE;IACD,GAAG,EAAE,MAAM,CAAA;IACX;oFACgF;IAChF,gBAAgB,CAAC,EAAE,kBAAkB,EAAE,CAAA;IACvC;mFAC+E;IAC/E,cAAc,CAAC,EAAE,OAAO,EAAE,CAAA;IAC1B;kFAC8E;IAC9E,YAAY,CAAC,EAAE,OAAO,EAAE,CAAA;IACxB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,IAAI,CAAA;IAC7C,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,WAAW,KAAK,IAAI,CAAA;IAClD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAA;IACzC,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAA;CACtD,+BAkzBA"}
|
|
@@ -483,7 +483,9 @@ export function GlbScene({ url, interactiveItems, referenceNodes, replaceNodes,
|
|
|
483
483
|
const targetY = baseY + (exploded ? index * EXPLODED_GAP : 0);
|
|
484
484
|
// Snap (not lerp) in walkthrough so the first-person collider, built from
|
|
485
485
|
// these world positions, matches the stacked building immediately.
|
|
486
|
-
node.position.y = walkthroughMode
|
|
486
|
+
node.position.y = walkthroughMode
|
|
487
|
+
? targetY
|
|
488
|
+
: lerp(node.position.y, targetY, Math.min(1, delta * 12));
|
|
487
489
|
// Solo: hidden levels above the soloed one keep casting shadows
|
|
488
490
|
// (shadow-caster-only); below-levels can't block the sun, so plain-hide.
|
|
489
491
|
const hidden = !walkthroughMode && levelMode === 'solo' && Boolean(selectedLevel) && id !== selectedLevel;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"post-processing.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/post-processing.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"post-processing.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/post-processing.tsx"],"names":[],"mappings":"AA8CA,eAAO,MAAM,YAAY;;;CAGxB,CAAA;AAGD,eAAO,MAAM,WAAW;;;;;;;;;;;;;CAavB,CAAA;AAgED,MAAM,MAAM,UAAU,GAAG;IACvB,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,OAAO,CAAA;CACf,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,UAAU,CAAA;CACpB,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;AAS9B,eAAO,MAAM,oBAAoB,EAAE,WAElC,CAAA;AA+CD,QAAA,MAAM,oBAAoB,GAAI,iCAG3B;IACD,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB,SAmnBA,CAAA;AAED,eAAe,oBAAoB,CAAA"}
|
|
@@ -9,6 +9,7 @@ import { backdropGradient, deepSkyColor, horizonHazeColor } from '../../lib/back
|
|
|
9
9
|
import { edgeColorFor, edgeOpacityScaleFor } from '../../lib/edge-style';
|
|
10
10
|
import { PERF_OVERLAY_ENABLED } from '../../lib/gpu-perf';
|
|
11
11
|
import { inkedEdges } from '../../lib/ink-edges';
|
|
12
|
+
import { refreshIsolation } from '../../lib/isolation';
|
|
12
13
|
import { LayerPassIndex, LayerPassNode } from '../../lib/layer-pass';
|
|
13
14
|
import { GRID_LAYER, OVERLAY_LAYER, SCENE_LAYER, ZONE_LAYER } from '../../lib/layers';
|
|
14
15
|
import { mergedOutline } from '../../lib/merged-outline-node';
|
|
@@ -612,6 +613,7 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, disablePostF
|
|
|
612
613
|
inkColorUniform.current.value.set(edgeColorFor(bgHex));
|
|
613
614
|
inkOpacityScaleUniform.current.value = edgeOpacityScaleFor(bgHex);
|
|
614
615
|
const outliner = useViewer.getState().outliner;
|
|
616
|
+
const restoreAtmosphere = refreshIsolation(scene);
|
|
615
617
|
sanitizeOutlineObjects(outliner.selectedObjects);
|
|
616
618
|
sanitizeOutlineObjects(outliner.hoveredObjects);
|
|
617
619
|
if (disablePostFx ||
|
|
@@ -644,6 +646,7 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, disablePostF
|
|
|
644
646
|
}
|
|
645
647
|
finally {
|
|
646
648
|
scene.backgroundNode = previousBackgroundNode;
|
|
649
|
+
restoreAtmosphere();
|
|
647
650
|
}
|
|
648
651
|
return;
|
|
649
652
|
}
|
|
@@ -681,6 +684,9 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, disablePostF
|
|
|
681
684
|
console.error('[viewer/post-processing] Retries exhausted. Rendering without post FX for this session.');
|
|
682
685
|
}
|
|
683
686
|
}
|
|
687
|
+
finally {
|
|
688
|
+
restoreAtmosphere();
|
|
689
|
+
}
|
|
684
690
|
}, 1);
|
|
685
691
|
return null;
|
|
686
692
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selection-manager.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/selection-manager.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"selection-manager.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/selection-manager.tsx"],"names":[],"mappings":"AAqSA,eAAO,MAAM,gBAAgB,mCA0G5B,CAAA"}
|
|
@@ -40,25 +40,15 @@ const pointInPolygonWithTolerance = (x, z, polygon) => {
|
|
|
40
40
|
const expanded = expandPolygon(polygon, EDGE_TOLERANCE);
|
|
41
41
|
return pointInPolygon(x, z, expanded);
|
|
42
42
|
};
|
|
43
|
-
// Check if a node belongs to the selected level (directly or via wall parent)
|
|
44
43
|
const isNodeOnLevel = (node, levelId) => {
|
|
45
44
|
const nodes = useScene.getState().nodes;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if ((node.type === 'item' || node.type === 'window' || node.type === 'door') && node.parentId) {
|
|
51
|
-
const parentNode = nodes[node.parentId];
|
|
52
|
-
if (parentNode?.type === 'wall' && parentNode.parentId === levelId) {
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
// Ceiling/slab/roof-attached items: check if parent structure is on the level
|
|
56
|
-
if ((parentNode?.type === 'ceiling' ||
|
|
57
|
-
parentNode?.type === 'slab' ||
|
|
58
|
-
parentNode?.type === 'roof') &&
|
|
59
|
-
parentNode.parentId === levelId) {
|
|
45
|
+
const seen = new Set();
|
|
46
|
+
let parentId = node.parentId;
|
|
47
|
+
while (parentId && !seen.has(parentId)) {
|
|
48
|
+
if (parentId === levelId)
|
|
60
49
|
return true;
|
|
61
|
-
|
|
50
|
+
seen.add(parentId);
|
|
51
|
+
parentId = nodes[parentId]?.parentId ?? null;
|
|
62
52
|
}
|
|
63
53
|
return false;
|
|
64
54
|
};
|
|
@@ -187,6 +177,7 @@ const getStrategy = () => {
|
|
|
187
177
|
'roof-segment',
|
|
188
178
|
'window',
|
|
189
179
|
'door',
|
|
180
|
+
...getSelectableKinds(),
|
|
190
181
|
],
|
|
191
182
|
handleClick: (node, nativeEvent) => {
|
|
192
183
|
let nodeToSelect = node;
|
|
@@ -224,6 +215,7 @@ const getStrategy = () => {
|
|
|
224
215
|
'roof-segment',
|
|
225
216
|
'window',
|
|
226
217
|
'door',
|
|
218
|
+
...getSelectableKinds(),
|
|
227
219
|
];
|
|
228
220
|
if (!validTypes.includes(node.type))
|
|
229
221
|
return false;
|
package/dist/index.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export { disposeObject3DResources } from './lib/dispose-object3d';
|
|
|
25
25
|
export type { EdgeMode } from './lib/edge-style';
|
|
26
26
|
export { PERF_OVERLAY_ENABLED } from './lib/gpu-perf';
|
|
27
27
|
export { computeHeroFraming, DEFAULT_FRAMING_EXCLUDED_TYPES, type HeroFraming, heroCameraPose, temporarilyHideNodeTypes, unionRegisteredNodeBounds, } from './lib/hero-pose';
|
|
28
|
-
export { applyIsolation, clearIsolation, collectIsolationSubtree, isIsolationActive, } from './lib/isolation';
|
|
28
|
+
export { applyIsolation, clearIsolation, collectIsolationSubtree, isIsolationActive, refreshIsolation, } from './lib/isolation';
|
|
29
29
|
export { configureKtx2Support, ensureKtx2Support } from './lib/ktx2-loader';
|
|
30
30
|
export { LayerPassIndex } from './lib/layer-pass';
|
|
31
31
|
export { BATCHED_LAYER, GRID_LAYER, OVERLAY_LAYER, SCENE_LAYER, SHADOW_ONLY_LAYER, setSurfaceRaycastLayers, ZONE_LAYER, } from './lib/layers';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAO3D,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAA;AACnE,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAC1E,OAAO,EACL,KAAK,YAAY,EACjB,OAAO,IAAI,SAAS,EACpB,KAAK,aAAa,GACnB,MAAM,gCAAgC,CAAA;AACvC,OAAO,EACL,wBAAwB,EACxB,cAAc,EACd,KAAK,kBAAkB,GACxB,MAAM,qCAAqC,CAAA;AAC5C,OAAO,EACL,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,yCAAyC,CAAA;AAChD,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,QAAQ,EACb,QAAQ,EACR,KAAK,cAAc,GACpB,MAAM,+BAA+B,CAAA;AACtC,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,wBAAwB,EACxB,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,eAAe,GAChB,MAAM,gDAAgD,CAAA;AACvD,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,qCAAqC,CAAA;AAClF,OAAO,EACL,oBAAoB,EACpB,WAAW,GACZ,MAAM,qCAAqC,CAAA;AAC5C,OAAO,EACL,eAAe,EACf,KAAK,qBAAqB,EAC1B,kBAAkB,GACnB,MAAM,sCAAsC,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAA;AACxE,OAAO,EACL,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,8CAA8C,CAAA;AACrD,OAAO,EACL,mCAAmC,EACnC,qCAAqC,EACrC,0BAA0B,EAC1B,KAAK,+BAA+B,EACpC,KAAK,8BAA8B,EACnC,KAAK,+BAA+B,EACpC,KAAK,8BAA8B,EACnC,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,0CAA0C,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACnD,OAAO,EAAE,0BAA0B,EAAE,MAAM,uCAAuC,CAAA;AAClF,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAChF,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACjF,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAA;AAIpD,OAAO,EACL,QAAQ,EACR,KAAK,EACL,yBAAyB,EACzB,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,WAAW,GACZ,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAA;AACjE,YAAY,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAChD,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AACrD,OAAO,EACL,kBAAkB,EAClB,8BAA8B,EAC9B,KAAK,WAAW,EAChB,cAAc,EACd,wBAAwB,EACxB,yBAAyB,GAC1B,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,cAAc,EACd,cAAc,EACd,uBAAuB,EACvB,iBAAiB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -35,7 +35,7 @@ export { ADDITION, Brush, computeGeometryBoundsTree, csgEvaluator, csgGeometry,
|
|
|
35
35
|
export { disposeObject3DResources } from './lib/dispose-object3d';
|
|
36
36
|
export { PERF_OVERLAY_ENABLED } from './lib/gpu-perf';
|
|
37
37
|
export { computeHeroFraming, DEFAULT_FRAMING_EXCLUDED_TYPES, heroCameraPose, temporarilyHideNodeTypes, unionRegisteredNodeBounds, } from './lib/hero-pose';
|
|
38
|
-
export { applyIsolation, clearIsolation, collectIsolationSubtree, isIsolationActive, } from './lib/isolation';
|
|
38
|
+
export { applyIsolation, clearIsolation, collectIsolationSubtree, isIsolationActive, refreshIsolation, } from './lib/isolation';
|
|
39
39
|
export { configureKtx2Support, ensureKtx2Support } from './lib/ktx2-loader';
|
|
40
40
|
export { LayerPassIndex } from './lib/layer-pass';
|
|
41
41
|
export { BATCHED_LAYER, GRID_LAYER, OVERLAY_LAYER, SCENE_LAYER, SHADOW_ONLY_LAYER, setSurfaceRaycastLayers, ZONE_LAYER, } from './lib/layers';
|
package/dist/lib/isolation.d.ts
CHANGED
|
@@ -1,39 +1,23 @@
|
|
|
1
1
|
import type { AnyNodeId } from '@pascal-app/core';
|
|
2
2
|
import type { Object3D } from 'three';
|
|
3
|
-
/** True while
|
|
3
|
+
/** True while a subtree is isolated, including before its lazy meshes mount. */
|
|
4
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
|
-
*/
|
|
5
|
+
/** Include synthesized meshes below a selected native root, including hosted openings. */
|
|
14
6
|
export declare function collectIsolationSubtree(ids: ReadonlyArray<string>): Set<Object3D>;
|
|
15
7
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* isolated subtree by taking it off the scene layer.
|
|
19
|
-
*
|
|
20
|
-
* Why layers instead of `obj.visible = false`? Three.js's visibility
|
|
21
|
-
* flag *cascades* — hiding a parent hides every descendant — so we
|
|
22
|
-
* can't hide a host wall while keeping a door rendered inside it.
|
|
23
|
-
* Layer masks are per-object and don't cascade: `WebGLRenderer
|
|
24
|
-
* .projectObject` skips objects whose layer mask doesn't intersect the
|
|
25
|
-
* camera's, but always recurses into their children. So we can hide
|
|
26
|
-
* the wall and the door (hosted under it in the scene graph) still
|
|
27
|
-
* renders, with its local position relative to the wall preserved
|
|
28
|
-
* automatically by the matrix walk.
|
|
29
|
-
*
|
|
30
|
-
* The mask itself belongs to `lib/scene-visibility.ts`, which reconciles
|
|
31
|
-
* isolation with solo's shadow-caster pass, so {@link clearIsolation}
|
|
32
|
-
* gives an object back only what isolation took.
|
|
33
|
-
*
|
|
34
|
-
* Pass `null` to clear isolation (equivalent to calling
|
|
35
|
-
* {@link clearIsolation}).
|
|
8
|
+
* Select native subtrees without mutating the saved scene or cascading visibility
|
|
9
|
+
* through their hosts. The next draw also filters unregistered presentation geometry.
|
|
36
10
|
*/
|
|
37
11
|
export declare function applyIsolation(ids: ReadonlyArray<AnyNodeId> | null): void;
|
|
12
|
+
/**
|
|
13
|
+
* Reconcile immediately before viewport or snapshot rendering. Environment, sky,
|
|
14
|
+
* ground and instance batches can live outside sceneRegistry or mount asynchronously.
|
|
15
|
+
* Lights/cameras remain usable; masks do not cascade, so an isolated door can still
|
|
16
|
+
* render under its hidden wall. Collective renderers fall back to native geometry.
|
|
17
|
+
* The returned cleanup restores fog after the synchronous draw, before atmosphere
|
|
18
|
+
* owners can change it again. Do not hold this cleanup across async GPU readback.
|
|
19
|
+
*/
|
|
20
|
+
export declare function refreshIsolation(scene?: Object3D): () => void;
|
|
21
|
+
/** Restore even detached/unregistered meshes, without undoing solo or batching holds. */
|
|
38
22
|
export declare function clearIsolation(): void;
|
|
39
23
|
//# sourceMappingURL=isolation.d.ts.map
|
|
@@ -1 +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,
|
|
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,EAAS,MAAM,OAAO,CAAA;AAQ5C,gFAAgF;AAChF,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED,0FAA0F;AAC1F,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,QAAQ,CAAC,CASjF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,CAAC,SAAS,CAAC,GAAG,IAAI,GAAG,IAAI,CAOzE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,CAAC,EAAE,QAAQ,GAAG,MAAM,IAAI,CA6B7D;AAED,yFAAyF;AACzF,wBAAgB,cAAc,IAAI,IAAI,CAKrC"}
|
package/dist/lib/isolation.js
CHANGED
|
@@ -1,91 +1,87 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { sceneRegistry } from '@pascal-app/core';
|
|
3
3
|
import { hideFromScene, showInScene } from './scene-visibility';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
/** True while an isolation filter is applied (see {@link applyIsolation}). */
|
|
4
|
+
let isolatedIds = null;
|
|
5
|
+
let heldObjects = new Set();
|
|
6
|
+
let liveScene;
|
|
7
|
+
const noop = () => { };
|
|
8
|
+
/** True while a subtree is isolated, including before its lazy meshes mount. */
|
|
10
9
|
export function isIsolationActive() {
|
|
11
|
-
return
|
|
10
|
+
return isolatedIds !== null;
|
|
12
11
|
}
|
|
13
|
-
/**
|
|
14
|
-
* Compute the union of every isolated subtree's `Object3D` descendants.
|
|
15
|
-
*
|
|
16
|
-
* Pure traversal — exported so future "focus mode" / debug tooling can
|
|
17
|
-
* reuse the same definition of "what's in the isolated set". Each root
|
|
18
|
-
* is walked via `Object3D.traverse` (the live Three.js graph, not the
|
|
19
|
-
* data-model `children` array — those can disagree when systems mount
|
|
20
|
-
* synthesized sub-meshes that the data model doesn't track).
|
|
21
|
-
*/
|
|
12
|
+
/** Include synthesized meshes below a selected native root, including hosted openings. */
|
|
22
13
|
export function collectIsolationSubtree(ids) {
|
|
23
14
|
const keep = new Set();
|
|
24
15
|
for (const id of ids) {
|
|
25
16
|
const root = sceneRegistry.nodes.get(id);
|
|
26
|
-
|
|
27
|
-
continue;
|
|
28
|
-
root.traverse((child) => {
|
|
17
|
+
root?.traverse((child) => {
|
|
29
18
|
keep.add(child);
|
|
30
19
|
});
|
|
31
20
|
}
|
|
32
21
|
return keep;
|
|
33
22
|
}
|
|
34
23
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* isolated subtree by taking it off the scene layer.
|
|
38
|
-
*
|
|
39
|
-
* Why layers instead of `obj.visible = false`? Three.js's visibility
|
|
40
|
-
* flag *cascades* — hiding a parent hides every descendant — so we
|
|
41
|
-
* can't hide a host wall while keeping a door rendered inside it.
|
|
42
|
-
* Layer masks are per-object and don't cascade: `WebGLRenderer
|
|
43
|
-
* .projectObject` skips objects whose layer mask doesn't intersect the
|
|
44
|
-
* camera's, but always recurses into their children. So we can hide
|
|
45
|
-
* the wall and the door (hosted under it in the scene graph) still
|
|
46
|
-
* renders, with its local position relative to the wall preserved
|
|
47
|
-
* automatically by the matrix walk.
|
|
48
|
-
*
|
|
49
|
-
* The mask itself belongs to `lib/scene-visibility.ts`, which reconciles
|
|
50
|
-
* isolation with solo's shadow-caster pass, so {@link clearIsolation}
|
|
51
|
-
* gives an object back only what isolation took.
|
|
52
|
-
*
|
|
53
|
-
* Pass `null` to clear isolation (equivalent to calling
|
|
54
|
-
* {@link clearIsolation}).
|
|
24
|
+
* Select native subtrees without mutating the saved scene or cascading visibility
|
|
25
|
+
* through their hosts. The next draw also filters unregistered presentation geometry.
|
|
55
26
|
*/
|
|
56
27
|
export function applyIsolation(ids) {
|
|
57
28
|
if (ids == null || ids.length === 0) {
|
|
58
29
|
clearIsolation();
|
|
59
30
|
return;
|
|
60
31
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
// hide it and every descendant — *except* descendants that are
|
|
64
|
-
// themselves in `keep` (a kept node nested under a non-kept host:
|
|
65
|
-
// the isolated door under the hidden wall).
|
|
66
|
-
for (const [, obj] of sceneRegistry.nodes) {
|
|
67
|
-
if (keep.has(obj))
|
|
68
|
-
continue;
|
|
69
|
-
hideRecursive(obj, keep);
|
|
70
|
-
}
|
|
71
|
-
isolationActive = true;
|
|
32
|
+
isolatedIds = [...ids];
|
|
33
|
+
refreshIsolation();
|
|
72
34
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Reconcile immediately before viewport or snapshot rendering. Environment, sky,
|
|
37
|
+
* ground and instance batches can live outside sceneRegistry or mount asynchronously.
|
|
38
|
+
* Lights/cameras remain usable; masks do not cascade, so an isolated door can still
|
|
39
|
+
* render under its hidden wall. Collective renderers fall back to native geometry.
|
|
40
|
+
* The returned cleanup restores fog after the synchronous draw, before atmosphere
|
|
41
|
+
* owners can change it again. Do not hold this cleanup across async GPU readback.
|
|
42
|
+
*/
|
|
43
|
+
export function refreshIsolation(scene) {
|
|
44
|
+
if (!isolatedIds)
|
|
45
|
+
return noop;
|
|
46
|
+
if (scene)
|
|
47
|
+
liveScene = scene;
|
|
48
|
+
const keep = collectIsolationSubtree(isolatedIds);
|
|
49
|
+
const visited = new Set();
|
|
50
|
+
const hidden = new Set();
|
|
51
|
+
const visit = (object) => {
|
|
52
|
+
if (visited.has(object))
|
|
53
|
+
return;
|
|
54
|
+
visited.add(object);
|
|
55
|
+
const flags = object;
|
|
56
|
+
if (keep.has(object) || flags.isLight || flags.isCamera)
|
|
57
|
+
return;
|
|
58
|
+
hideFromScene(object, 'isolated');
|
|
59
|
+
hidden.add(object);
|
|
60
|
+
};
|
|
61
|
+
liveScene?.traverse(visit);
|
|
62
|
+
// Also covers not-yet-attached registered roots and imperative callers without a scene.
|
|
63
|
+
for (const object of sceneRegistry.nodes.values())
|
|
64
|
+
object.traverse(visit);
|
|
65
|
+
for (const object of heldObjects)
|
|
66
|
+
if (!hidden.has(object))
|
|
67
|
+
showInScene(object, 'isolated');
|
|
68
|
+
heldObjects = hidden;
|
|
69
|
+
const atmosphere = scene;
|
|
70
|
+
if (!atmosphere?.isScene)
|
|
71
|
+
return noop;
|
|
72
|
+
const { fog, fogNode } = atmosphere;
|
|
73
|
+
atmosphere.fog = null;
|
|
74
|
+
atmosphere.fogNode = null;
|
|
75
|
+
return () => {
|
|
76
|
+
atmosphere.fog = fog;
|
|
77
|
+
atmosphere.fogNode = fogNode;
|
|
78
|
+
};
|
|
80
79
|
}
|
|
80
|
+
/** Restore even detached/unregistered meshes, without undoing solo or batching holds. */
|
|
81
81
|
export function clearIsolation() {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
showInScene(child, 'isolated');
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
isolationActive = false;
|
|
82
|
+
for (const object of heldObjects)
|
|
83
|
+
showInScene(object, 'isolated');
|
|
84
|
+
heldObjects.clear();
|
|
85
|
+
isolatedIds = null;
|
|
86
|
+
liveScene = undefined;
|
|
91
87
|
}
|
|
@@ -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,EAGnB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,WAAW,EACjB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAE,uBAAuB,EAA4B,MAAM,cAAc,CAAA;AAOhF,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,UAAU,CAAA;AAChD,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,WAAW,CAAA;AAEjE,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAQpD,CAAA;AAID,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAQrD,CAAA;AAED,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAQpD,CAAA;AAED,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAQzD,CAAA;AAED,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAK5E,CAAA;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,WAAW,EACnB,YAAY,CAAC,EAAE,MAAM,GACpB,MAAM,CAKR;AAOD,eAAO,MAAM,aAAa,yBAKxB,CAAA;AAsBF,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AA2ED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAWtF;AAED,wBAAgB,aAAa,CAAC,QAAQ,CAAC,EAAE,cAAc,GAAG,MAAM,CAK/D;AA6SD,wBAAgB,8BAA8B,CAC5C,aAAa,EAAE,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,EAAE,EAChD,MAAM,EAAE,qBAAqB,GAAG,IAAI,GAAG,SAAS,QAcjD;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,qBAAqB,EAC7B,OAAO,GAAE,aAA0B,GAClC,KAAK,CAAC,QAAQ,CAchB;AAED,wBAAgB,2BAA2B,CACzC,cAAc,CAAC,EAAE,MAAM,EACvB,OAAO,GAAE,aAA0B,GAClC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAIvB;AAED,wBAAgB,cAAc,CAC5B,QAAQ,CAAC,EAAE,cAAc,EACzB,OAAO,GAAE,aAA0B,GAClC,KAAK,CAAC,QAAQ,CAqChB;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,cAAc,EAAE,MAAM,CAAC,eAAe,EAAE,aAAa,CAAC,GAAG,SAAS,EAClE,OAAO,GAAE,aAA0B,GAClC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAOvB;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,aAA0B,EACnC,SAAS,SAAM,GACd,KAAK,CAAC,QAAQ,
|
|
1
|
+
{"version":3,"file":"materials.d.ts","sourceRoot":"","sources":["../../src/lib/materials.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/materials.js
CHANGED
|
@@ -475,7 +475,8 @@ export function resolveSlotDefaultMaterial(slotDefault, shading = 'rendered', ro
|
|
|
475
475
|
return (createMaterialFromPresetRef(slotDefault, shading) ??
|
|
476
476
|
cachedDefaultMaterial(`slot-#ffffff-${roughness}`, '#ffffff', roughness, shading));
|
|
477
477
|
}
|
|
478
|
-
|
|
478
|
+
const color = slotDefault.startsWith('#') ? slotDefault.toLowerCase() : slotDefault;
|
|
479
|
+
return cachedDefaultMaterial(`slot-${color}-${roughness}`, color, roughness, shading);
|
|
479
480
|
}
|
|
480
481
|
export function createDefaultMaterial(color = '#ffffff', roughness = 0.9, shading = 'rendered', side = THREE.FrontSide) {
|
|
481
482
|
const resolvedSide = resolveNodeMaterialSide(side);
|
|
@@ -60,10 +60,12 @@ function applyHold(obj, hold) {
|
|
|
60
60
|
// A batched wall is both drawn and shadowed by the merged mesh, so it stays
|
|
61
61
|
// out of the shadow pass too — enabling the shadow-only bit would submit its
|
|
62
62
|
// triangles a second time, on top of the copy the batch already casts.
|
|
63
|
+
if (hold.reasons.has('isolated'))
|
|
64
|
+
obj.layers.disable(SHADOW_ONLY_LAYER);
|
|
63
65
|
if (hold.reasons.has('batched') || hold.reasons.has('wall-batched')) {
|
|
64
66
|
obj.layers.enable(BATCHED_LAYER);
|
|
65
67
|
return;
|
|
66
68
|
}
|
|
67
|
-
if (hold.reasons.has('shadow-only'))
|
|
69
|
+
if (!hold.reasons.has('isolated') && hold.reasons.has('shadow-only'))
|
|
68
70
|
obj.layers.enable(SHADOW_ONLY_LAYER);
|
|
69
71
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Camera, type Scene } from 'three';
|
|
2
2
|
import { type WebGPURenderer } from 'three/webgpu';
|
|
3
|
+
import type { SceneAtmosphereSource } from '../components/viewer/scene-atmosphere';
|
|
3
4
|
import { type EdgeMode } from './edge-style';
|
|
4
5
|
export declare const THUMBNAIL_WIDTH = 1920;
|
|
5
6
|
export declare const THUMBNAIL_HEIGHT = 1080;
|
|
@@ -43,9 +44,10 @@ export type SnapshotPipeline = {
|
|
|
43
44
|
}) => Promise<SnapshotCaptureResult>;
|
|
44
45
|
dispose: () => void;
|
|
45
46
|
};
|
|
46
|
-
export declare function createSnapshotPipeline({ renderer, scene, camera, }: {
|
|
47
|
+
export declare function createSnapshotPipeline({ renderer, scene, camera, atmosphere, }: {
|
|
47
48
|
renderer: WebGPURenderer;
|
|
48
49
|
scene: Scene;
|
|
49
50
|
camera: Camera;
|
|
51
|
+
atmosphere?: SceneAtmosphereSource | null;
|
|
50
52
|
}): Promise<SnapshotPipeline | null>;
|
|
51
53
|
//# sourceMappingURL=snapshot-pipeline.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snapshot-pipeline.d.ts","sourceRoot":"","sources":["../../src/lib/snapshot-pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAkB,KAAK,KAAK,EAAoB,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"snapshot-pipeline.d.ts","sourceRoot":"","sources":["../../src/lib/snapshot-pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAkB,KAAK,KAAK,EAAoB,MAAM,OAAO,CAAA;AAoBjF,OAAO,EAKL,KAAK,cAAc,EACpB,MAAM,cAAc,CAAA;AAErB,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAA;AAElF,OAAO,EAAE,KAAK,QAAQ,EAAqC,MAAM,cAAc,CAAA;AAK/E,eAAO,MAAM,eAAe,OAAO,CAAA;AACnC,eAAO,MAAM,gBAAgB,OAAO,CAAA;AAEpC;;;;;GAKG;AACH,eAAO,MAAM,aAAa,eAAe,CAAA;AACzC,eAAO,MAAM,gBAAgB,MAAM,CAAA;AAEnC,eAAO,MAAM,iBAAiB,OAAO,CAAA;AAUrC,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,CAAA;AAElE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,IAAI,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,gBAAgB,EAAE,CAAC,EACjB,KAAK,EACL,WAAW,EACX,KAAK,EACL,KAAK,EACL,MAAM,GACP,EAAE;QACD,KAAK,EAAE,MAAM,CAAA;QACb,WAAW,EAAE,OAAO,CAAA;QACpB,KAAK,EAAE,OAAO,CAAA;QACd,KAAK,EAAE,QAAQ,CAAA;QACf,MAAM,EAAE,MAAM,CAAA;KACf,KAAK,IAAI,CAAA;IACV,OAAO,EAAE,CAAC,EACR,WAAW,EACX,UAAU,EACV,YAAY,GACb,EAAE;QACD,WAAW,CAAC,EAAE,mBAAmB,CAAA;QACjC,UAAU,CAAC,EAAE,kBAAkB,CAAA;QAC/B,YAAY,CAAC,EAAE,YAAY,CAAA;KAC5B,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAA;IACpC,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB,CAAA;AAgBD,wBAAsB,sBAAsB,CAAC,EAC3C,QAAQ,EACR,KAAK,EACL,MAAM,EACN,UAAiB,GAClB,EAAE;IACD,QAAQ,EAAE,cAAc,CAAA;IACxB,KAAK,EAAE,KAAK,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAA;CAC1C,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CA0SnC"}
|
|
@@ -2,8 +2,8 @@ import { Color, Matrix4, UnsignedByteType } from 'three';
|
|
|
2
2
|
import { ssgi } from 'three/addons/tsl/display/SSGINode.js';
|
|
3
3
|
import { denoise } from 'three/examples/jsm/tsl/display/DenoiseNode.js';
|
|
4
4
|
import { fxaa } from 'three/examples/jsm/tsl/display/FXAANode.js';
|
|
5
|
-
import { convertToTexture, diffuseColor, float, mix, mrt, normalView, output,
|
|
6
|
-
import { RenderPipeline, RenderTarget } from 'three/webgpu';
|
|
5
|
+
import { convertToTexture, diffuseColor, float, mix, mrt, normalView, output, sample, saturation, screenUV, smoothstep, uniform, vec3, vec4, } from 'three/tsl';
|
|
6
|
+
import { PassNode, RenderPipeline, RenderTarget, } from 'three/webgpu';
|
|
7
7
|
import { GRADE_PARAMS, SSGI_PARAMS } from '../components/viewer/post-processing';
|
|
8
8
|
import { backdropGradient, deepSkyColor, horizonHazeColor } from './backdrop';
|
|
9
9
|
import { edgeColorFor, edgeOpacityScaleFor } from './edge-style';
|
|
@@ -29,7 +29,22 @@ function clampSnapshotSize(width, height) {
|
|
|
29
29
|
const scale = SNAPSHOT_MAX_EDGE / maxEdge;
|
|
30
30
|
return { w: Math.round(width * scale), h: Math.round(height * scale) };
|
|
31
31
|
}
|
|
32
|
-
|
|
32
|
+
class SnapshotScenePass extends PassNode {
|
|
33
|
+
updateBefore(frame) {
|
|
34
|
+
const renderer = frame.renderer;
|
|
35
|
+
const clearAlpha = renderer.getClearAlpha();
|
|
36
|
+
// RTTNode resets clear alpha in r186. The geometry mask must be cleared
|
|
37
|
+
// inside this pass, not inherited from the outer snapshot render.
|
|
38
|
+
renderer.setClearAlpha(0);
|
|
39
|
+
try {
|
|
40
|
+
return super.updateBefore(frame);
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
renderer.setClearAlpha(clearAlpha);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export async function createSnapshotPipeline({ renderer, scene, camera, atmosphere = null, }) {
|
|
33
48
|
try {
|
|
34
49
|
if (renderer.init)
|
|
35
50
|
await renderer.init();
|
|
@@ -49,10 +64,9 @@ export async function createSnapshotPipeline({ renderer, scene, camera, }) {
|
|
|
49
64
|
const inkColorUniform = uniform(new Color('#1a1d24'));
|
|
50
65
|
const inkOpacityUniform = uniform(0.5);
|
|
51
66
|
const inkOpacityScaleUniform = uniform(1);
|
|
52
|
-
// pass
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
const scenePass = pass(scene, camera);
|
|
67
|
+
// The scene pass owns MRT and its transparent clear, including when nested
|
|
68
|
+
// inside FXAA's intermediate render target.
|
|
69
|
+
const scenePass = new SnapshotScenePass('color', scene, camera);
|
|
56
70
|
scenePass.setMRT(mrt({
|
|
57
71
|
output,
|
|
58
72
|
diffuseColor,
|
|
@@ -111,13 +125,15 @@ export async function createSnapshotPipeline({ renderer, scene, camera, }) {
|
|
|
111
125
|
const ndc = vec4(screenUV.x.mul(2).sub(1), float(1).sub(screenUV.y).mul(2).sub(1), 1, 1);
|
|
112
126
|
const viewRay = bgProjInvUniform.mul(ndc);
|
|
113
127
|
const worldDir = bgCamWorldUniform.mul(vec4(viewRay.xyz, 0)).xyz.normalize();
|
|
114
|
-
const ungradedBgGradient =
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
128
|
+
const ungradedBgGradient = atmosphere
|
|
129
|
+
? atmosphere.skyRadiance(worldDir)
|
|
130
|
+
: backdropGradient({
|
|
131
|
+
dirY: worldDir.y,
|
|
132
|
+
background: bgColorUniform,
|
|
133
|
+
haze: bgHazeUniform,
|
|
134
|
+
sky: bgSkyUniform,
|
|
135
|
+
skyDeep: bgSkyDeepUniform,
|
|
136
|
+
});
|
|
121
137
|
const bgGradient = mix(ungradedBgGradient, gradeRgb(ungradedBgGradient), gradeMixUniform);
|
|
122
138
|
const alpha = scenePassColor.a;
|
|
123
139
|
const finalOutput = vec4(mix(sceneRgb, mix(bgGradient, sceneRgb, alpha), bgMixUniform), mix(alpha, float(1), bgMixUniform));
|
|
@@ -159,8 +175,6 @@ export async function createSnapshotPipeline({ renderer, scene, camera, }) {
|
|
|
159
175
|
renderTarget.setSize(captureWidth, captureHeight);
|
|
160
176
|
}
|
|
161
177
|
try {
|
|
162
|
-
;
|
|
163
|
-
renderer.setClearAlpha(0);
|
|
164
178
|
renderer.setRenderTarget(renderTarget);
|
|
165
179
|
pipeline.render();
|
|
166
180
|
}
|
|
@@ -268,6 +282,8 @@ export async function createSnapshotPipeline({ renderer, scene, camera, }) {
|
|
|
268
282
|
dispose: () => {
|
|
269
283
|
pipeline.dispose();
|
|
270
284
|
renderTarget.dispose();
|
|
285
|
+
scenePass.dispose();
|
|
286
|
+
giPass.dispose();
|
|
271
287
|
},
|
|
272
288
|
};
|
|
273
289
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AnyNode, BaseNode, BuildingNode, LevelNode, ZoneNode } from '@pascal-app/core';
|
|
1
|
+
import type { AnyNode, BaseNode, BuildingNode, LevelNode, UnitNode, ZoneNode } from '@pascal-app/core';
|
|
2
2
|
import type { Object3D } from 'three';
|
|
3
3
|
import type { EdgeMode } from '../lib/edge-style';
|
|
4
4
|
import type { ColorPreset, RenderShading } from '../lib/materials';
|
|
@@ -17,6 +17,8 @@ type Outliner = {
|
|
|
17
17
|
};
|
|
18
18
|
type ViewerState = {
|
|
19
19
|
selection: SelectionPath;
|
|
20
|
+
focusedUnitId: UnitNode['id'] | null;
|
|
21
|
+
setFocusedUnit: (id: UnitNode['id'] | null) => void;
|
|
20
22
|
previewSelectedIds: BaseNode['id'][];
|
|
21
23
|
setPreviewSelectedIds: (ids: BaseNode['id'][]) => void;
|
|
22
24
|
/** Host-owned selection highlights rendered through the viewer's native
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-viewer.d.ts","sourceRoot":"","sources":["../../src/store/use-viewer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"use-viewer.d.ts","sourceRoot":"","sources":["../../src/store/use-viewer.ts"],"names":[],"mappings":"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"}
|
package/dist/store/use-viewer.js
CHANGED
|
@@ -122,6 +122,8 @@ function normalizePersistedViewerState(value) {
|
|
|
122
122
|
}
|
|
123
123
|
const useViewer = create()(persist((set) => ({
|
|
124
124
|
selection: { buildingId: null, levelId: null, zoneId: null, selectedIds: [] },
|
|
125
|
+
focusedUnitId: null,
|
|
126
|
+
setFocusedUnit: (focusedUnitId) => set({ focusedUnitId }),
|
|
125
127
|
previewSelectedIds: [],
|
|
126
128
|
setPreviewSelectedIds: (ids) => set({ previewSelectedIds: ids }),
|
|
127
129
|
externalSelectedIds: [],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"floor-elevation-system.d.ts","sourceRoot":"","sources":["../../../src/systems/floor-elevation/floor-elevation-system.tsx"],"names":[],"mappings":"AAyCA;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,oBAAoB,
|
|
1
|
+
{"version":3,"file":"floor-elevation-system.d.ts","sourceRoot":"","sources":["../../../src/systems/floor-elevation/floor-elevation-system.tsx"],"names":[],"mappings":"AAyCA;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,oBAAoB,YAiFhC,CAAA"}
|
|
@@ -64,6 +64,9 @@ export const FloorElevationSystem = () => {
|
|
|
64
64
|
const position = effectiveNode.position;
|
|
65
65
|
if (!position)
|
|
66
66
|
return;
|
|
67
|
+
if (!(def.geometry || def.system) && dirtyNodes.has(id)) {
|
|
68
|
+
clearDirty(id);
|
|
69
|
+
}
|
|
67
70
|
// `applies === false` means the kind opts OUT of floor stacking for this
|
|
68
71
|
// node: its Y belongs to a host frame (a wall/ceiling-mounted item, a
|
|
69
72
|
// cabinet module inside a run, a wall duct terminal). `getFloorPlacedElevation`
|
|
@@ -73,6 +76,10 @@ export const FloorElevationSystem = () => {
|
|
|
73
76
|
// the host frame's own elevation.
|
|
74
77
|
if (floorPlaced.applies && !floorPlaced.applies(effectiveNode))
|
|
75
78
|
return;
|
|
79
|
+
// Hosted meshes inherit elevation from their parent (and possibly a surface group).
|
|
80
|
+
// Their live transform can be world-space, so it cannot replace the mesh's local Y.
|
|
81
|
+
if (!effectiveNode.parentId || nodes[effectiveNode.parentId]?.type !== 'level')
|
|
82
|
+
return;
|
|
76
83
|
// This system is the single drag-time authority for floor-stack mesh Y:
|
|
77
84
|
// tools publish base positions to live stores, renderers may
|
|
78
85
|
// reconcile that base Y onto the group, then this presentation system
|
|
@@ -90,9 +97,6 @@ export const FloorElevationSystem = () => {
|
|
|
90
97
|
maxElevation: liveTransform?.supportElevationCap,
|
|
91
98
|
});
|
|
92
99
|
mesh.position.y = visualPosition[1];
|
|
93
|
-
if (!(def.geometry || def.system) && dirtyNodes.has(id)) {
|
|
94
|
-
clearDirty(id);
|
|
95
|
-
}
|
|
96
100
|
};
|
|
97
101
|
dirtyNodes.forEach((id) => {
|
|
98
102
|
applyLift(id);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"geometry-system.d.ts","sourceRoot":"","sources":["../../../src/systems/geometry/geometry-system.tsx"],"names":[],"mappings":"AAkBA,OAAO,EAAa,KAAK,KAAK,EAA4B,KAAK,QAAQ,EAAE,MAAM,OAAO,CAAA;AAUtF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,cAAc,YAuM1B,CAAA;
|
|
1
|
+
{"version":3,"file":"geometry-system.d.ts","sourceRoot":"","sources":["../../../src/systems/geometry/geometry-system.tsx"],"names":[],"mappings":"AAkBA,OAAO,EAAa,KAAK,KAAK,EAA4B,KAAK,QAAQ,EAAE,MAAM,OAAO,CAAA;AAUtF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,cAAc,YAuM1B,CAAA;AAiJD,eAAe,cAAc,CAAA;AAE7B,MAAM,MAAM,uBAAuB,GAAG;IACpC,KAAK,EAAE,KAAK,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;CACZ,CAAA;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAU7D;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,uBAAuB,CAAC,EAC3C,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,MAAM,GACV,OAAO,CAKT"}
|
|
@@ -212,7 +212,11 @@ function liveChildOverrideKey(node) {
|
|
|
212
212
|
return '';
|
|
213
213
|
const overrides = useLiveNodeOverrides.getState().overrides;
|
|
214
214
|
const entries = [];
|
|
215
|
+
const childTypes = nodeRegistry.get(node.type)?.geometryChildTypes;
|
|
216
|
+
const nodes = useScene.getState().nodes;
|
|
215
217
|
for (const childId of childIds) {
|
|
218
|
+
if (childTypes && !childTypes.includes(nodes[childId]?.type ?? ''))
|
|
219
|
+
continue;
|
|
216
220
|
const override = overrides.get(childId);
|
|
217
221
|
if (override)
|
|
218
222
|
entries.push([childId, override]);
|
|
@@ -10,7 +10,7 @@ export declare const DOUBLE_HUNG_BOTTOM_SASH_NAME = "double-hung-bottom-sash";
|
|
|
10
10
|
export declare const LOUVERED_WINDOW_SLATS_NAME = "louvered-window-slats";
|
|
11
11
|
export declare const AWNING_WINDOW_SASH_NAME = "awning-window-sash";
|
|
12
12
|
export declare const HOPPER_WINDOW_SASH_NAME = "hopper-window-sash";
|
|
13
|
-
export declare const pendingWindowAnimationRebuilds: Set<`building_${string}` | `level_${string}` | `elevator_${string}` | `
|
|
13
|
+
export declare const pendingWindowAnimationRebuilds: Set<`building_${string}` | `level_${string}` | `elevator_${string}` | `unit_${string}` | `block_${string}` | `ceiling_${string}` | `item_${string}` | `procedural-item_${string}` | `column_${string}` | `construction-dimension_${string}` | `wall_${string}` | `roof_${string}` | `slab_${string}` | `fence_${string}` | `structural-grid_${string}` | `imesh_${string}` | `zone_${string}` | `stair_${string}` | `scan_${string}` | `guide_${string}` | `measurement_${string}` | `spawn_${string}` | `shelf_${string}` | `duct-segment_${string}` | `duct-fitting_${string}` | `duct-terminal_${string}` | `hvac-equipment_${string}` | `lineset_${string}` | `liquid-line_${string}` | `pipe-segment_${string}` | `pipe-fitting_${string}` | `pipe-trap_${string}` | `site_${string}` | `leanto_${string}` | `door_${string}` | `window_${string}` | `cabinet_${string}` | `cabinet-module_${string}` | `rseg_${string}` | `sseg_${string}` | `bvent_${string}` | `rvent_${string}` | `tvent_${string}` | `cupola_${string}` | `eyebrow-vent_${string}` | `gutter_${string}` | `chimney_${string}` | `solarpanel_${string}` | `skylight_${string}` | `dormer_${string}` | `downspout_${string}`>;
|
|
14
14
|
export declare const WindowSystem: () => null;
|
|
15
15
|
/**
|
|
16
16
|
* Build a fresh window mesh for preview/ghost rendering.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"window-system.d.ts","sourceRoot":"","sources":["../../../src/systems/window/window-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EAYL,KAAK,UAAU,EAChB,MAAM,kBAAkB,CAAA;AAGzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAyB9B,eAAO,MAAM,yBAAyB,yBAAyB,CAAA;AAC/D,eAAO,MAAM,8BAA8B,8BAA8B,CAAA;AACzE,eAAO,MAAM,+BAA+B,+BAA+B,CAAA;AAC3E,eAAO,MAAM,gCAAgC,gCAAgC,CAAA;AAC7E,eAAO,MAAM,4BAA4B,4BAA4B,CAAA;AACrE,eAAO,MAAM,yBAAyB,yBAAyB,CAAA;AAC/D,eAAO,MAAM,4BAA4B,4BAA4B,CAAA;AACrE,eAAO,MAAM,0BAA0B,0BAA0B,CAAA;AACjE,eAAO,MAAM,uBAAuB,uBAAuB,CAAA;AAC3D,eAAO,MAAM,uBAAuB,uBAAuB,CAAA;AAS3D,eAAO,MAAM,8BAA8B,
|
|
1
|
+
{"version":3,"file":"window-system.d.ts","sourceRoot":"","sources":["../../../src/systems/window/window-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EAYL,KAAK,UAAU,EAChB,MAAM,kBAAkB,CAAA;AAGzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAyB9B,eAAO,MAAM,yBAAyB,yBAAyB,CAAA;AAC/D,eAAO,MAAM,8BAA8B,8BAA8B,CAAA;AACzE,eAAO,MAAM,+BAA+B,+BAA+B,CAAA;AAC3E,eAAO,MAAM,gCAAgC,gCAAgC,CAAA;AAC7E,eAAO,MAAM,4BAA4B,4BAA4B,CAAA;AACrE,eAAO,MAAM,yBAAyB,yBAAyB,CAAA;AAC/D,eAAO,MAAM,4BAA4B,4BAA4B,CAAA;AACrE,eAAO,MAAM,0BAA0B,0BAA0B,CAAA;AACjE,eAAO,MAAM,uBAAuB,uBAAuB,CAAA;AAC3D,eAAO,MAAM,uBAAuB,uBAAuB,CAAA;AAS3D,eAAO,MAAM,8BAA8B,uoCAAuB,CAAA;AAElE,eAAO,MAAM,YAAY,YAoHxB,CAAA;AA28GD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,UAAU,GAAG,KAAK,CAAC,IAAI,CAInE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zone-system.d.ts","sourceRoot":"","sources":["../../../src/systems/zone/zone-system.tsx"],"names":[],"mappings":"AAUA,eAAO,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"zone-system.d.ts","sourceRoot":"","sources":["../../../src/systems/zone/zone-system.tsx"],"names":[],"mappings":"AAUA,eAAO,MAAM,UAAU,YAqHtB,CAAA"}
|
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
import { sceneRegistry, useScene } from '@pascal-app/core';
|
|
2
2
|
import { useFrame } from '@react-three/fiber';
|
|
3
|
-
import { useRef } from 'react';
|
|
3
|
+
import { useMemo, useRef } from 'react';
|
|
4
4
|
import { MathUtils } from 'three';
|
|
5
5
|
import useViewer from '../../store/use-viewer';
|
|
6
6
|
const TRANSITION_DURATION = 400; // ms
|
|
7
7
|
const EXIT_DEBOUNCE_MS = 50; // ignore rapid exit→re-enter within this window
|
|
8
8
|
export const ZoneSystem = () => {
|
|
9
|
+
const focusedUnitId = useViewer((state) => state.focusedUnitId);
|
|
10
|
+
const focusedUnitMembers = useScene((state) => {
|
|
11
|
+
const unit = focusedUnitId ? state.nodes[focusedUnitId] : undefined;
|
|
12
|
+
return unit?.type === 'unit' ? unit.members : undefined;
|
|
13
|
+
});
|
|
14
|
+
const focusedZoneIds = useMemo(() => new Set(focusedUnitMembers), [focusedUnitMembers]);
|
|
15
|
+
const selection = useViewer((state) => state.selection);
|
|
16
|
+
const showZones = useViewer((state) => state.showZones);
|
|
17
|
+
const lastFocusRef = useRef(null);
|
|
18
|
+
const lastFocusedZoneIdsRef = useRef(null);
|
|
19
|
+
const lastSelectionRef = useRef(null);
|
|
20
|
+
const lastShowZonesRef = useRef(showZones);
|
|
9
21
|
const lastHighlightedZoneRef = useRef(null);
|
|
10
22
|
const lastChangeTimeRef = useRef(0);
|
|
11
23
|
const isTransitioningRef = useRef(false);
|
|
@@ -49,6 +61,17 @@ export const ZoneSystem = () => {
|
|
|
49
61
|
lastChangeTimeRef.current = clock.elapsedTime * 1000;
|
|
50
62
|
isTransitioningRef.current = true;
|
|
51
63
|
}
|
|
64
|
+
if (focusedUnitId !== lastFocusRef.current ||
|
|
65
|
+
focusedZoneIds !== lastFocusedZoneIdsRef.current ||
|
|
66
|
+
selection !== lastSelectionRef.current ||
|
|
67
|
+
showZones !== lastShowZonesRef.current) {
|
|
68
|
+
lastFocusRef.current = focusedUnitId;
|
|
69
|
+
lastFocusedZoneIdsRef.current = focusedZoneIds;
|
|
70
|
+
lastSelectionRef.current = selection;
|
|
71
|
+
lastShowZonesRef.current = showZones;
|
|
72
|
+
lastChangeTimeRef.current = clock.elapsedTime * 1000;
|
|
73
|
+
isTransitioningRef.current = true;
|
|
74
|
+
}
|
|
52
75
|
// Skip frame if not transitioning
|
|
53
76
|
if (!isTransitioningRef.current)
|
|
54
77
|
return;
|
|
@@ -63,7 +86,10 @@ export const ZoneSystem = () => {
|
|
|
63
86
|
const zone = sceneRegistry.nodes.get(zoneId);
|
|
64
87
|
if (!zone)
|
|
65
88
|
return;
|
|
66
|
-
const isHighlighted = zoneId === highlightedZone
|
|
89
|
+
const isHighlighted = zoneId === highlightedZone ||
|
|
90
|
+
zoneId === selection.zoneId ||
|
|
91
|
+
selection.selectedIds.includes(zoneId) ||
|
|
92
|
+
focusedZoneIds.has(zoneId);
|
|
67
93
|
const targetOpacity = isHighlighted ? 1 : 0;
|
|
68
94
|
const walls = zone.getObjectByName('walls');
|
|
69
95
|
if (walls) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pascal-app/viewer",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "3D viewer component for Pascal building editor",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"prepublishOnly": "npm run build"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
|
-
"@pascal-app/core": "^1.0.
|
|
36
|
+
"@pascal-app/core": "^1.0.1",
|
|
37
37
|
"@react-three/drei": "^10",
|
|
38
38
|
"@react-three/fiber": "^9",
|
|
39
39
|
"react": "^18 || ^19",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"zustand": "^5"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@pascal-app/core": "^1.0.
|
|
49
|
+
"@pascal-app/core": "^1.0.1",
|
|
50
50
|
"@pascal/typescript-config": "*",
|
|
51
51
|
"@react-three/test-renderer": "^9.1.0",
|
|
52
52
|
"@types/node": "^22",
|