@pascal-app/viewer 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/viewer/index.d.ts +12 -0
- package/dist/components/viewer/index.d.ts.map +1 -1
- package/dist/components/viewer/index.js +28 -7
- package/dist/components/viewer/viewer-camera.d.ts +15 -1
- package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
- package/dist/components/viewer/viewer-camera.js +43 -5
- package/dist/index.d.ts +7 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -5
- package/dist/lib/csg-utils.d.ts +1 -1
- package/dist/lib/csg-utils.d.ts.map +1 -1
- package/dist/lib/csg-utils.js +1 -1
- package/dist/lib/renderer-capability.d.ts +2 -1
- package/dist/lib/renderer-capability.d.ts.map +1 -1
- package/dist/lib/renderer-capability.js +2 -2
- package/dist/systems/door/door-system.d.ts.map +1 -1
- package/dist/systems/door/door-system.js +18 -5
- package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -1
- package/dist/systems/floor-elevation/floor-elevation-system.js +115 -0
- package/dist/systems/level/level-system.js +14 -14
- package/dist/systems/level/level-utils.d.ts +25 -1
- package/dist/systems/level/level-utils.d.ts.map +1 -1
- package/dist/systems/level/level-utils.js +25 -4
- package/dist/systems/wall/wall-cutout-cache.d.ts +4 -1
- package/dist/systems/wall/wall-cutout-cache.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout-cache.js +16 -7
- package/dist/systems/wall/wall-cutout.d.ts +3 -1
- package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout.js +5 -5
- package/dist/systems/wall/wall-materials.d.ts +11 -2
- package/dist/systems/wall/wall-materials.d.ts.map +1 -1
- package/dist/systems/wall/wall-materials.js +24 -18
- package/dist/systems/wall/wall-system.d.ts +15 -2
- package/dist/systems/wall/wall-system.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.js +20 -21
- package/dist/systems/window/window-system.d.ts.map +1 -1
- package/dist/systems/window/window-system.js +3 -2
- package/dist/xr/presentation-context.d.ts +7 -0
- package/dist/xr/presentation-context.d.ts.map +1 -0
- package/dist/xr/presentation-context.js +10 -0
- package/package.json +4 -4
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type AnyNodeId } from '@pascal-app/core';
|
|
2
2
|
import { type ThreeElement } from '@react-three/fiber';
|
|
3
|
+
import { type ComponentType } from 'react';
|
|
3
4
|
import * as THREE from 'three/webgpu';
|
|
4
5
|
import type { ColorPreset, RenderShading } from '../../lib/materials';
|
|
5
6
|
import { type RenderContext } from '../../store/use-viewer';
|
|
@@ -9,6 +10,15 @@ declare module '@react-three/fiber' {
|
|
|
9
10
|
lineBasicNodeMaterial: ThreeElement<typeof THREE.LineBasicNodeMaterial>;
|
|
10
11
|
}
|
|
11
12
|
}
|
|
13
|
+
export interface ViewerImmersiveSession {
|
|
14
|
+
Session: ComponentType<{
|
|
15
|
+
children: React.ReactNode;
|
|
16
|
+
}>;
|
|
17
|
+
Scene: ComponentType<{
|
|
18
|
+
children: React.ReactNode;
|
|
19
|
+
}>;
|
|
20
|
+
onError?: (cause: unknown) => void;
|
|
21
|
+
}
|
|
12
22
|
interface ViewerProps {
|
|
13
23
|
children?: React.ReactNode;
|
|
14
24
|
hoverStyles?: HoverStyles;
|
|
@@ -69,6 +79,8 @@ interface ViewerProps {
|
|
|
69
79
|
disablePostFx?: boolean;
|
|
70
80
|
/** Keep the mounted renderer/context warm without advancing scene frames. */
|
|
71
81
|
renderPaused?: boolean;
|
|
82
|
+
/** Host-provided immersive XR session wrappers for the main scene. */
|
|
83
|
+
immersive?: ViewerImmersiveSession;
|
|
72
84
|
}
|
|
73
85
|
/** Imperative handle exposed via `ref` on `<Viewer>`. */
|
|
74
86
|
export type ViewerHandle = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/index.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,SAAS,EAMf,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAkB,KAAK,YAAY,EAAsB,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/index.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,SAAS,EAMf,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAkB,KAAK,YAAY,EAAsB,MAAM,oBAAoB,CAAA;AAC1F,OAAO,EACL,KAAK,aAAa,EAOnB,MAAM,OAAO,CAAA;AACd,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAKrC,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAKrE,OAAkB,EAAE,KAAK,aAAa,EAAE,MAAM,wBAAwB,CAAA;AActE,OAAuB,EAAwB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAY1F,OAAO,QAAQ,oBAAoB,CAAC;IAOlC,UAAU,aAAa;QACrB,qBAAqB,EAAE,YAAY,CAAC,OAAO,KAAK,CAAC,qBAAqB,CAAC,CAAA;KACxE;CACF;AAoRD,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,aAAa,CAAC;QAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;KAAE,CAAC,CAAA;IACrD,KAAK,EAAE,aAAa,CAAC;QAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;KAAE,CAAC,CAAA;IACnD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;CACnC;AAED,UAAU,WAAW;IACnB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC1B,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,gBAAgB,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;IACvC,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,aAAa,CAAC,EAAE,aAAa,CAAA;IAC7B,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,aAAa,CAAC,EAAE;QACd,OAAO,CAAC,EAAE,aAAa,CAAA;QACvB,QAAQ,CAAC,EAAE,OAAO,CAAA;QAClB,WAAW,CAAC,EAAE,WAAW,CAAA;KAC1B,CAAA;IACD;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,CAAA;IAC5B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IACtC,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IAC7C;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,6EAA6E;IAC7E,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,sEAAsE;IACtE,SAAS,CAAC,EAAE,sBAAsB,CAAA;CACnC;AAED,yDAAyD;AACzD,MAAM,MAAM,YAAY,GAAG;IACzB;;;;;OAKG;IACH,WAAW,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,IAAI,GAAG,IAAI,CAAA;CAC3C,CAAA;AAED,QAAA,MAAM,MAAM,sGAuPV,CAAA;AAyEF,eAAe,MAAM,CAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx,
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { nodeRegistry, RoofElevationSystem, StairOpeningSystem, sceneRegistry, useScene, } from '@pascal-app/core';
|
|
4
4
|
import { Canvas, extend, useFrame, useThree } from '@react-three/fiber';
|
|
5
5
|
import { forwardRef, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState, } from 'react';
|
|
@@ -17,6 +17,7 @@ import { FloorElevationSystem } from '../../systems/floor-elevation/floor-elevat
|
|
|
17
17
|
import { GeometrySystem } from '../../systems/geometry/geometry-system';
|
|
18
18
|
import { PerfActionSettleSystem } from '../../systems/perf-action-settle/perf-action-settle-system';
|
|
19
19
|
import { subscribeWallBuildInteractions } from '../../systems/wall/wall-build-lifecycle';
|
|
20
|
+
import { ImmersiveXRPresentationProvider } from '../../xr/presentation-context';
|
|
20
21
|
import { ErrorBoundary } from '../error-boundary';
|
|
21
22
|
import { SceneRenderer } from '../renderers/scene-renderer';
|
|
22
23
|
import { BATCH_SPIKE_ENABLED, BatchedMeshSpike } from './batched-mesh-spike';
|
|
@@ -94,7 +95,7 @@ function installEmptyDrawGuard(renderer) {
|
|
|
94
95
|
renderer.renderObject(object, scene, camera, geometry, material, group, lightsNode, clippingContext, passId);
|
|
95
96
|
});
|
|
96
97
|
}
|
|
97
|
-
function GPUDeviceWatcher() {
|
|
98
|
+
function GPUDeviceWatcher({ intentionalWebGL = false }) {
|
|
98
99
|
const gl = useThree((s) => s.gl);
|
|
99
100
|
useEffect(() => {
|
|
100
101
|
// Detect KTX2 transcode support as soon as the renderer exists, so catalog
|
|
@@ -103,13 +104,15 @@ function GPUDeviceWatcher() {
|
|
|
103
104
|
ensureKtx2Support(gl);
|
|
104
105
|
const backend = gl.backend;
|
|
105
106
|
const device = backend?.device;
|
|
106
|
-
if (!device) {
|
|
107
|
+
if (!device && !intentionalWebGL) {
|
|
107
108
|
console.warn('[viewer] No WebGPU device on backend — running on a fallback renderer.', {
|
|
108
109
|
backend: backend?.constructor?.name ?? 'unknown',
|
|
109
110
|
rendererType: gl.constructor?.name ?? 'unknown',
|
|
110
111
|
});
|
|
111
112
|
return;
|
|
112
113
|
}
|
|
114
|
+
if (!device)
|
|
115
|
+
return;
|
|
113
116
|
console.log('[viewer] WebGPU device ready', {
|
|
114
117
|
label: device.label,
|
|
115
118
|
features: Array.from(device.features ?? []),
|
|
@@ -127,7 +130,7 @@ function GPUDeviceWatcher() {
|
|
|
127
130
|
return () => {
|
|
128
131
|
device.removeEventListener?.('uncapturederror', onUncapturedError);
|
|
129
132
|
};
|
|
130
|
-
}, [gl]);
|
|
133
|
+
}, [gl, intentionalWebGL]);
|
|
131
134
|
return null;
|
|
132
135
|
}
|
|
133
136
|
function ToneMappingExposure() {
|
|
@@ -148,6 +151,10 @@ function ToneMappingExposure() {
|
|
|
148
151
|
}, -1);
|
|
149
152
|
return null;
|
|
150
153
|
}
|
|
154
|
+
function ImmersiveXRBackground() {
|
|
155
|
+
const background = useViewer((state) => getSceneTheme(state.sceneTheme).background);
|
|
156
|
+
return _jsx("color", { args: [background], attach: "background" });
|
|
157
|
+
}
|
|
151
158
|
function hasPendingSceneBuildWork() {
|
|
152
159
|
const { dirtyNodes, nodes, rootNodeIds } = useScene.getState();
|
|
153
160
|
for (const id of dirtyNodes) {
|
|
@@ -226,7 +233,7 @@ function SceneReadyTracker({ onSceneReadyChange, sceneReadyKey, sceneReadyMaxWai
|
|
|
226
233
|
}, 10);
|
|
227
234
|
return null;
|
|
228
235
|
}
|
|
229
|
-
const Viewer = forwardRef(function Viewer({ children, hoverStyles = DEFAULT_HOVER_STYLES, selectionManager = 'default', perf = false, useBvh = true, renderContext = 'editor', transparent, defaultRender, isolate, sceneReadyKey, onSceneReadyChange, sceneReadyMaxWaitMs, maxFps = 50, disablePostFx = false, renderPaused = false, }, ref) {
|
|
236
|
+
const Viewer = forwardRef(function Viewer({ children, hoverStyles = DEFAULT_HOVER_STYLES, selectionManager = 'default', perf = false, useBvh = true, renderContext = 'editor', transparent, defaultRender, isolate, sceneReadyKey, onSceneReadyChange, sceneReadyMaxWaitMs, maxFps = 50, disablePostFx = false, renderPaused = false, immersive, }, ref) {
|
|
230
237
|
useEffect(() => {
|
|
231
238
|
if (nodeRegistry.size === 0) {
|
|
232
239
|
console.warn('[viewer] Node registry is empty. Install @pascal-app/nodes and call await loadPlugin(builtinPlugin) before mounting <Viewer>.');
|
|
@@ -313,16 +320,25 @@ const Viewer = forwardRef(function Viewer({ children, hoverStyles = DEFAULT_HOVE
|
|
|
313
320
|
if (showGpuFallback)
|
|
314
321
|
onSceneReadyChange?.(true);
|
|
315
322
|
}, [showGpuFallback, onSceneReadyChange]);
|
|
323
|
+
useEffect(() => {
|
|
324
|
+
if (!immersive)
|
|
325
|
+
return;
|
|
326
|
+
const timeout = window.setTimeout(() => window.dispatchEvent(new Event('resize')), 0);
|
|
327
|
+
return () => window.clearTimeout(timeout);
|
|
328
|
+
}, [immersive]);
|
|
329
|
+
const ImmersiveSession = immersive?.Session;
|
|
330
|
+
const immersiveActive = immersive != null;
|
|
316
331
|
if (showGpuFallback) {
|
|
317
332
|
return _jsx(UnsupportedGpuViewerFallback, {});
|
|
318
333
|
}
|
|
319
|
-
return (_jsxs(_Fragment, { children: [(perf || PERF_OVERLAY_ENABLED) && _jsx(PerfPanel, {}),
|
|
334
|
+
return (_jsxs(_Fragment, { children: [(perf || PERF_OVERLAY_ENABLED) && _jsx(PerfPanel, {}), _jsx(Canvas, { ref: subscribeWallBuildInteractions, camera: { position: [50, 50, 50], fov: 50 }, className: `transition-colors duration-700 ${transparentBackground ? 'bg-transparent' : isDark ? 'bg-[#1f2433]' : 'bg-[#fafafa]'}`, dpr: [1, maxDpr], events: pointerEvents, frameloop: "never", gl: ((props) => {
|
|
320
335
|
const canvas = props.canvas;
|
|
321
336
|
const cached = canvas ? WEBGPU_RENDERER_CACHE.get(canvas) : undefined;
|
|
322
337
|
if (cached)
|
|
323
338
|
return cached;
|
|
324
339
|
const promise = (async () => {
|
|
325
340
|
const result = await initializeGpuRenderer({
|
|
341
|
+
forceWebGL: immersiveActive,
|
|
326
342
|
// Supplying `device` makes three skip its own `requestAdapter`,
|
|
327
343
|
// so R3F's `powerPreference` only reaches the GPU if we forward it.
|
|
328
344
|
powerPreference: props.powerPreference,
|
|
@@ -366,6 +382,11 @@ const Viewer = forwardRef(function Viewer({ children, hoverStyles = DEFAULT_HOVE
|
|
|
366
382
|
}, shadows: {
|
|
367
383
|
type: THREE.PCFShadowMap,
|
|
368
384
|
enabled: shadowsEnabled,
|
|
369
|
-
}, children:
|
|
385
|
+
}, children: _jsx(ImmersiveXRPresentationProvider, { enabled: immersiveActive, children: ImmersiveSession ? (_jsx(ImmersiveSession, { children: _jsx(ViewerScene, { disablePostFx: true, hoverStyles: hoverStyles, immersiveXR: true, onRenderError: immersive.onError, onSceneReadyChange: onSceneReadyChange, perf: perf, sceneReadyKey: sceneReadyKey, sceneReadyMaxWaitMs: sceneReadyMaxWaitMs, selectionManager: selectionManager, useBvh: useBvh, SceneWrapper: immersive.Scene, children: children }) })) : (_jsxs(_Fragment, { children: [_jsx(FrameLimiter, { fps: maxFps, paused: renderPaused }), _jsx(ViewerScene, { disablePostFx: disablePostFx, hoverStyles: hoverStyles, onSceneReadyChange: onSceneReadyChange, perf: perf, sceneReadyKey: sceneReadyKey, sceneReadyMaxWaitMs: sceneReadyMaxWaitMs, selectionManager: selectionManager, useBvh: useBvh, children: children })] })) }) }, immersiveActive ? 'webgl-xr' : 'webgpu')] }));
|
|
370
386
|
});
|
|
387
|
+
function ViewerScene({ children, disablePostFx, hoverStyles, immersiveXR = false, onRenderError, onSceneReadyChange, perf, sceneReadyKey, sceneReadyMaxWaitMs, selectionManager, useBvh, SceneWrapper, }) {
|
|
388
|
+
const renderedScene = useBvh ? (_jsx(SceneBvh, { children: _jsx(SceneRenderer, {}) })) : (_jsx(SceneRenderer, {}));
|
|
389
|
+
const spatialScene = (_jsxs(_Fragment, { children: [renderedScene, _jsx(FloorElevationSystem, {}), _jsx(GeometrySystem, {}), _jsx(StairOpeningSystem, {}), _jsx(RoofElevationSystem, {}), _jsx(RegisteredSystems, {}), BATCH_SPIKE_ENABLED && _jsx(BatchedMeshSpike, {}), children] }));
|
|
390
|
+
return (_jsxs(_Fragment, { children: [_jsx(ViewerCamera, { immersiveXR: immersiveXR }), immersiveXR && _jsx(ImmersiveXRBackground, {}), _jsx(PointerRaycastLayers, {}), _jsx(GPUDeviceWatcher, { intentionalWebGL: immersiveXR }), _jsx(ToneMappingExposure, {}), _jsx(SceneReadyTracker, { onSceneReadyChange: onSceneReadyChange, sceneReadyKey: sceneReadyKey, sceneReadyMaxWaitMs: sceneReadyMaxWaitMs }), _jsxs(ErrorBoundary, { fallback: null, onError: onRenderError, scope: "viewer-scene", children: [_jsx(Lights, {}), SceneWrapper ? _jsx(SceneWrapper, { children: spatialScene }) : spatialScene, !immersiveXR && _jsx(PostProcessing, { disablePostFx: disablePostFx, hoverStyles: hoverStyles }), selectionManager === 'default' && _jsx(SelectionManager, {}), (perf || PERF_OVERLAY_ENABLED) && _jsx(PerfMonitor, {}), (perf || PERF_OVERLAY_ENABLED) && _jsx(PerfActionSettleSystem, {})] })] }));
|
|
391
|
+
}
|
|
371
392
|
export default Viewer;
|
|
@@ -1,2 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
import type { Layers } from 'three';
|
|
2
|
+
export declare function enableImmersiveXRViewLayers(cameraLayers: Layers, raycasterLayers: Layers): () => void;
|
|
3
|
+
export declare function viewerCameraClipping(immersiveXR: boolean): {
|
|
4
|
+
far: number;
|
|
5
|
+
near: number;
|
|
6
|
+
};
|
|
7
|
+
export declare function applyViewerCameraClipping(camera: {
|
|
8
|
+
far: number;
|
|
9
|
+
near: number;
|
|
10
|
+
updateProjectionMatrix(): void;
|
|
11
|
+
}, immersiveXR: boolean): void;
|
|
12
|
+
export declare function viewerUsesPerspectiveCamera(cameraMode: string, immersiveXR: boolean): boolean;
|
|
13
|
+
export declare const ViewerCamera: ({ immersiveXR }: {
|
|
14
|
+
immersiveXR?: boolean;
|
|
15
|
+
}) => import("react").JSX.Element;
|
|
2
16
|
//# sourceMappingURL=viewer-camera.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"viewer-camera.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/viewer-camera.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"viewer-camera.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/viewer-camera.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AAOnC,wBAAgB,2BAA2B,CAAC,YAAY,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,cAWxF;AAcD,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,OAAO;;;EAExD;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,sBAAsB,IAAI,IAAI,CAAA;CAAE,EACrE,WAAW,EAAE,OAAO,QAMrB;AAED,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,WAEnF;AAED,eAAO,MAAM,YAAY,GAAI,iBAAyB;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAE,gCAqB9E,CAAA"}
|
|
@@ -1,10 +1,48 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { OrthographicCamera, PerspectiveCamera } from '@react-three/drei';
|
|
3
|
+
import { useThree } from '@react-three/fiber';
|
|
4
|
+
import { useEffect } from 'react';
|
|
5
|
+
import { GRID_LAYER, OVERLAY_LAYER, ZONE_LAYER } from '../../lib/layers';
|
|
3
6
|
import useViewer from '../../store/use-viewer';
|
|
4
7
|
import { useSceneGroundReplacement } from './scene-ground-replacement';
|
|
5
|
-
|
|
8
|
+
const IMMERSIVE_XR_VISIBLE_LAYERS = [OVERLAY_LAYER, ZONE_LAYER, GRID_LAYER];
|
|
9
|
+
export function enableImmersiveXRViewLayers(cameraLayers, raycasterLayers) {
|
|
10
|
+
const cameraMask = cameraLayers.mask;
|
|
11
|
+
const raycasterMask = raycasterLayers.mask;
|
|
12
|
+
for (const layer of IMMERSIVE_XR_VISIBLE_LAYERS) {
|
|
13
|
+
cameraLayers.enable(layer);
|
|
14
|
+
raycasterLayers.enable(layer);
|
|
15
|
+
}
|
|
16
|
+
return () => {
|
|
17
|
+
cameraLayers.mask = cameraMask;
|
|
18
|
+
raycasterLayers.mask = raycasterMask;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function ImmersiveXRViewLayers({ enabled }) {
|
|
22
|
+
const camera = useThree((state) => state.camera);
|
|
23
|
+
const raycaster = useThree((state) => state.raycaster);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (!enabled)
|
|
26
|
+
return;
|
|
27
|
+
return enableImmersiveXRViewLayers(camera.layers, raycaster.layers);
|
|
28
|
+
}, [camera, enabled, raycaster]);
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
export function viewerCameraClipping(immersiveXR) {
|
|
32
|
+
return immersiveXR ? { far: 10_000, near: 0.001 } : { far: 1000, near: 0.1 };
|
|
33
|
+
}
|
|
34
|
+
export function applyViewerCameraClipping(camera, immersiveXR) {
|
|
35
|
+
const clipping = viewerCameraClipping(immersiveXR);
|
|
36
|
+
camera.far = clipping.far;
|
|
37
|
+
camera.near = clipping.near;
|
|
38
|
+
camera.updateProjectionMatrix();
|
|
39
|
+
}
|
|
40
|
+
export function viewerUsesPerspectiveCamera(cameraMode, immersiveXR) {
|
|
41
|
+
return immersiveXR || cameraMode === 'perspective';
|
|
42
|
+
}
|
|
43
|
+
export const ViewerCamera = ({ immersiveXR = false }) => {
|
|
6
44
|
const cameraMode = useViewer((state) => state.cameraMode);
|
|
7
|
-
|
|
8
|
-
const far = useSceneGroundReplacement() ? 20_000 :
|
|
9
|
-
return cameraMode
|
|
45
|
+
const clipping = viewerCameraClipping(immersiveXR);
|
|
46
|
+
const far = useSceneGroundReplacement() ? 20_000 : clipping.far;
|
|
47
|
+
return (_jsxs(_Fragment, { children: [viewerUsesPerspectiveCamera(cameraMode, immersiveXR) ? (_jsx(PerspectiveCamera, { far: far, fov: 50, makeDefault: true, near: clipping.near, position: [10, 10, 10] })) : (_jsx(OrthographicCamera, { far: 1000, makeDefault: true, near: -1000, position: [10, 10, 10], zoom: 20 })), _jsx(ImmersiveXRViewLayers, { enabled: immersiveXR })] }));
|
|
10
48
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type { SurfaceRole } from '@pascal-app/core';
|
|
2
2
|
export { ErrorBoundary } from './components/error-boundary';
|
|
3
3
|
export { NodeRenderer } from './components/renderers/node-renderer';
|
|
4
|
-
export { default as Viewer, type ViewerHandle } from './components/viewer';
|
|
4
|
+
export { default as Viewer, type ViewerHandle, type ViewerImmersiveSession, } from './components/viewer';
|
|
5
5
|
export { type BVHEcctrlApi, default as BVHEcctrl, type MovementInput, } from './components/viewer/bvh-ecctrl';
|
|
6
6
|
export { buildGlbInteractiveItems, GlbInteractive, type GlbInteractiveItem, } from './components/viewer/glb-interactive';
|
|
7
7
|
export { buildGlbReferenceNodes, buildGlbReplaceNodes, } from './components/viewer/glb-reference-nodes';
|
|
@@ -20,7 +20,7 @@ export { useNodeEvents } from './hooks/use-node-events';
|
|
|
20
20
|
export { ASSETS_CDN_URL, resolveAssetUrl, resolveCdnUrl } from './lib/asset-url';
|
|
21
21
|
export { backdropGradient, deepSkyColor, horizonHazeColor } from './lib/backdrop';
|
|
22
22
|
export { applyWorldScaleBoxUVs } from './lib/box-uv';
|
|
23
|
-
export { ADDITION, Brush, computeGeometryBoundsTree, csgEvaluator, csgGeometry, csgMaterials, INTERSECTION, prepareBrushForCSG, SUBTRACTION, } from './lib/csg-utils';
|
|
23
|
+
export { ADDITION, Brush, computeGeometryBoundsTree, csgEvaluator, csgGeometry, csgMaterials, Evaluator, ensureRenderableGeometryAttributes, INTERSECTION, prepareBrushForCSG, SUBTRACTION, } from './lib/csg-utils';
|
|
24
24
|
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';
|
|
@@ -63,11 +63,13 @@ export { ScanSystem } from './systems/scan/scan-system';
|
|
|
63
63
|
export { generateSlabGeometry } from './systems/slab/slab-system';
|
|
64
64
|
export { getStairBodyMaterials, getStairRailingMaterial, getStraightStairSegmentBodyMaterials, type StairBodyMaterials, } from './systems/stair/stair-materials';
|
|
65
65
|
export { StairSystem } from './systems/stair/stair-system';
|
|
66
|
-
export { buildOpeningCutoutGeometry, getOpeningCutoutBottomPadding, hasFlatOpeningCutoutBottom, } from './systems/wall/opening-cutout-geometry';
|
|
66
|
+
export { buildOpeningCutoutGeometry, buildOpeningCutoutShape, getOpeningCutoutBottomPadding, hasFlatOpeningCutoutBottom, } from './systems/wall/opening-cutout-geometry';
|
|
67
67
|
export { getWallHideState, WallCutout } from './systems/wall/wall-cutout';
|
|
68
|
-
export {
|
|
69
|
-
export {
|
|
68
|
+
export { WallCutoutCache, type WallCutoutViewerState, type WallCutoutViewerStore, } from './systems/wall/wall-cutout-cache';
|
|
69
|
+
export { getMaterialsForWall, getVisibleWallMaterials, type WallMaterialOverride, type WallMaterials, type WallMaterialsResolver, } from './systems/wall/wall-materials';
|
|
70
|
+
export { drainRebuiltWalls, generateExtrudedWall, getPendingWallRebuildCount, isWallInitialBuildActive, runWallBuildFrame, type WallGeometryAdapter, type WallGeometryAdapterContext, WallSystem, } from './systems/wall/wall-system';
|
|
70
71
|
export { poseWindowMovingParts, WindowAnimationSystem, } from './systems/window/window-animation-system';
|
|
71
72
|
export { buildWindowPreviewMesh, WindowSystem } from './systems/window/window-system';
|
|
72
73
|
export { ZoneSystem } from './systems/zone/zone-system';
|
|
74
|
+
export { useImmersiveXRPresentation } from './xr/presentation-context';
|
|
73
75
|
//# sourceMappingURL=index.d.ts.map
|
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,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ export { ErrorBoundary } from './components/error-boundary';
|
|
|
10
10
|
// `@pascal-app/nodes/<kind>/renderer.tsx` and are loaded by the registry
|
|
11
11
|
// — no per-kind re-exports needed.
|
|
12
12
|
export { NodeRenderer } from './components/renderers/node-renderer';
|
|
13
|
-
export { default as Viewer } from './components/viewer';
|
|
13
|
+
export { default as Viewer, } from './components/viewer';
|
|
14
14
|
export { default as BVHEcctrl, } from './components/viewer/bvh-ecctrl';
|
|
15
15
|
export { buildGlbInteractiveItems, GlbInteractive, } from './components/viewer/glb-interactive';
|
|
16
16
|
export { buildGlbReferenceNodes, buildGlbReplaceNodes, } from './components/viewer/glb-reference-nodes';
|
|
@@ -31,7 +31,7 @@ export { applyWorldScaleBoxUVs } from './lib/box-uv';
|
|
|
31
31
|
// CSG primitives — used by chimney's roof-trim and other kinds whose
|
|
32
32
|
// geometry subtracts pieces against their host. Lives in viewer
|
|
33
33
|
// because three-bvh-csg / three-mesh-bvh are viewer-only deps.
|
|
34
|
-
export { ADDITION, Brush, computeGeometryBoundsTree, csgEvaluator, csgGeometry, csgMaterials, INTERSECTION, prepareBrushForCSG, SUBTRACTION, } from './lib/csg-utils';
|
|
34
|
+
export { ADDITION, Brush, computeGeometryBoundsTree, csgEvaluator, csgGeometry, csgMaterials, Evaluator, ensureRenderableGeometryAttributes, INTERSECTION, prepareBrushForCSG, SUBTRACTION, } from './lib/csg-utils';
|
|
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';
|
|
@@ -92,14 +92,16 @@ export { StairSystem } from './systems/stair/stair-system';
|
|
|
92
92
|
// Pure opening-cutout profile math shared by the wall CSG pipeline and
|
|
93
93
|
// roof-wall opening cuts in `@pascal-app/nodes` — keeps shaped holes
|
|
94
94
|
// (arch / rounded / frameless opening) identical across both hosts.
|
|
95
|
-
export { buildOpeningCutoutGeometry, getOpeningCutoutBottomPadding, hasFlatOpeningCutoutBottom, } from './systems/wall/opening-cutout-geometry';
|
|
95
|
+
export { buildOpeningCutoutGeometry, buildOpeningCutoutShape, getOpeningCutoutBottomPadding, hasFlatOpeningCutoutBottom, } from './systems/wall/opening-cutout-geometry';
|
|
96
96
|
export { getWallHideState, WallCutout } from './systems/wall/wall-cutout';
|
|
97
|
-
export {
|
|
97
|
+
export { WallCutoutCache, } from './systems/wall/wall-cutout-cache';
|
|
98
|
+
export { getMaterialsForWall, getVisibleWallMaterials, } from './systems/wall/wall-materials';
|
|
98
99
|
// Wall internals re-exported so `@pascal-app/nodes`' registry-driven wall
|
|
99
100
|
// definition can compose them into `def.system` without duplicating the
|
|
100
101
|
// 800+ lines of CSG / mitering logic during Phase 3. These exports are
|
|
101
102
|
// removed in Phase 6 when the legacy mount points are deleted.
|
|
102
|
-
export { drainRebuiltWalls, getPendingWallRebuildCount, isWallInitialBuildActive, WallSystem, } from './systems/wall/wall-system';
|
|
103
|
+
export { drainRebuiltWalls, generateExtrudedWall, getPendingWallRebuildCount, isWallInitialBuildActive, runWallBuildFrame, WallSystem, } from './systems/wall/wall-system';
|
|
103
104
|
export { poseWindowMovingParts, WindowAnimationSystem, } from './systems/window/window-animation-system';
|
|
104
105
|
export { buildWindowPreviewMesh, WindowSystem } from './systems/window/window-system';
|
|
105
106
|
export { ZoneSystem } from './systems/zone/zone-system';
|
|
107
|
+
export { useImmersiveXRPresentation } from './xr/presentation-context';
|
package/dist/lib/csg-utils.d.ts
CHANGED
|
@@ -7,5 +7,5 @@ export declare const csgEvaluator: Evaluator;
|
|
|
7
7
|
export declare function computeGeometryBoundsTree(geometry: THREE.BufferGeometry): void;
|
|
8
8
|
export declare function prepareBrushForCSG(brush: Brush): void;
|
|
9
9
|
export declare function subtractCsgBrush(left: Brush, right: Brush, evaluator: Evaluator): Brush;
|
|
10
|
-
export { ADDITION, Brush, INTERSECTION, SUBTRACTION } from 'three-bvh-csg';
|
|
10
|
+
export { ADDITION, Brush, Evaluator, INTERSECTION, SUBTRACTION } from 'three-bvh-csg';
|
|
11
11
|
//# sourceMappingURL=csg-utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"csg-utils.d.ts","sourceRoot":"","sources":["../../src/lib/csg-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,KAAK,KAAK,EAAE,SAAS,EAAe,MAAM,eAAe,CAAA;AA4ClE,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,KAAK,CAAC,cAAc,GAC7B,KAAK,CAAC,cAAc,CAoCtB;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAAC,cAAc,CAE9D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAAC,QAAQ,EAAE,CAG3D;AAED,eAAO,MAAM,YAAY,WAAkB,CAAA;AAK3C,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,KAAK,CAAC,cAAc,QAMvE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,QAI9C;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,GAAG,KAAK,CAIvF;AAID,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA"}
|
|
1
|
+
{"version":3,"file":"csg-utils.d.ts","sourceRoot":"","sources":["../../src/lib/csg-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,KAAK,KAAK,EAAE,SAAS,EAAe,MAAM,eAAe,CAAA;AA4ClE,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,KAAK,CAAC,cAAc,GAC7B,KAAK,CAAC,cAAc,CAoCtB;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAAC,cAAc,CAE9D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAAC,QAAQ,EAAE,CAG3D;AAED,eAAO,MAAM,YAAY,WAAkB,CAAA;AAK3C,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,KAAK,CAAC,cAAc,QAMvE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,KAAK,QAI9C;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,GAAG,KAAK,CAIvF;AAID,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA"}
|
package/dist/lib/csg-utils.js
CHANGED
|
@@ -95,4 +95,4 @@ export function subtractCsgBrush(left, right, evaluator) {
|
|
|
95
95
|
}
|
|
96
96
|
// Re-export Brush + SUBTRACTION + ADDITION + INTERSECTION so kinds don't need a
|
|
97
97
|
// direct `three-bvh-csg` dependency.
|
|
98
|
-
export { ADDITION, Brush, INTERSECTION, SUBTRACTION } from 'three-bvh-csg';
|
|
98
|
+
export { ADDITION, Brush, Evaluator, INTERSECTION, SUBTRACTION } from 'three-bvh-csg';
|
|
@@ -45,8 +45,9 @@ export declare function detectRendererCapability({ canvas, gpu, powerPreference,
|
|
|
45
45
|
powerPreference?: RendererPowerPreference;
|
|
46
46
|
webgpuTimeoutMs?: number;
|
|
47
47
|
}): Promise<RendererCapability>;
|
|
48
|
-
export declare function initializeGpuRenderer<Renderer extends InitializableRenderer>({ createRenderer, gpu, powerPreference, probeCanvas, webgpuTimeoutMs, }: {
|
|
48
|
+
export declare function initializeGpuRenderer<Renderer extends InitializableRenderer>({ createRenderer, forceWebGL, gpu, powerPreference, probeCanvas, webgpuTimeoutMs, }: {
|
|
49
49
|
createRenderer: (parameters: RendererBackendParameters) => Renderer;
|
|
50
|
+
forceWebGL?: boolean;
|
|
50
51
|
gpu?: RendererGpu | null;
|
|
51
52
|
powerPreference?: RendererPowerPreference;
|
|
52
53
|
probeCanvas?: RendererCapabilityCanvas | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderer-capability.d.ts","sourceRoot":"","sources":["../../src/lib/renderer-capability.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,wBAAwB,GAAG;IACrC,UAAU,CAAC,SAAS,EAAE,QAAQ,GAAG,OAAO,CAAA;CACzC,CAAA;AAED,oGAAoG;AACpG,MAAM,MAAM,uBAAuB,GAAG,kBAAkB,GAAG,WAAW,CAAA;AAEtE,KAAK,kBAAkB,GAAG;IACxB,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAA;IAC3B,aAAa,CAAC,UAAU,CAAC,EAAE;QAAE,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CAC9E,CAAA;AAED,KAAK,WAAW,GAAG;IACjB,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAA;CACtF,CAAA;AAED,MAAM,MAAM,kBAAkB,GAC1B;IAAE,OAAO,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GAC3D;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GACzC;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,CAAA;AAE9C,MAAM,MAAM,yBAAyB,GAAG;IACtC,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB,CAAA;AAED,KAAK,qBAAqB,GAAG;IAC3B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;CAC7B,CAAA;AAED,MAAM,MAAM,4BAA4B,CAAC,QAAQ,IAC7C;IAAE,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GACpE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,CAAA;AAiD9C,wBAAsB,wBAAwB,CAAC,EAC7C,MAAwB,EACxB,GAAkB,EAClB,eAAe,EACf,eAAkD,GACnD,GAAE;IACD,MAAM,CAAC,EAAE,wBAAwB,GAAG,IAAI,CAAA;IACxC,GAAG,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;IACxB,eAAe,CAAC,EAAE,uBAAuB,CAAA;IACzC,eAAe,CAAC,EAAE,MAAM,CAAA;CACpB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA2BnC;AAED,wBAAsB,qBAAqB,CAAC,QAAQ,SAAS,qBAAqB,EAAE,EAClF,cAAc,EACd,GAAG,EACH,eAAe,EACf,WAA6B,EAC7B,eAAkD,GACnD,EAAE;IACD,cAAc,EAAE,CAAC,UAAU,EAAE,yBAAyB,KAAK,QAAQ,CAAA;IACnE,GAAG,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;IACxB,eAAe,CAAC,EAAE,uBAAuB,CAAA;IACzC,WAAW,CAAC,EAAE,wBAAwB,GAAG,IAAI,CAAA;IAC7C,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB,GAAG,OAAO,CAAC,4BAA4B,CAAC,QAAQ,CAAC,CAAC,CAyClD"}
|
|
1
|
+
{"version":3,"file":"renderer-capability.d.ts","sourceRoot":"","sources":["../../src/lib/renderer-capability.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,wBAAwB,GAAG;IACrC,UAAU,CAAC,SAAS,EAAE,QAAQ,GAAG,OAAO,CAAA;CACzC,CAAA;AAED,oGAAoG;AACpG,MAAM,MAAM,uBAAuB,GAAG,kBAAkB,GAAG,WAAW,CAAA;AAEtE,KAAK,kBAAkB,GAAG;IACxB,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAA;IAC3B,aAAa,CAAC,UAAU,CAAC,EAAE;QAAE,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CAC9E,CAAA;AAED,KAAK,WAAW,GAAG;IACjB,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAA;CACtF,CAAA;AAED,MAAM,MAAM,kBAAkB,GAC1B;IAAE,OAAO,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GAC3D;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GACzC;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,CAAA;AAE9C,MAAM,MAAM,yBAAyB,GAAG;IACtC,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB,CAAA;AAED,KAAK,qBAAqB,GAAG;IAC3B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;CAC7B,CAAA;AAED,MAAM,MAAM,4BAA4B,CAAC,QAAQ,IAC7C;IAAE,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GACpE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,CAAA;AAiD9C,wBAAsB,wBAAwB,CAAC,EAC7C,MAAwB,EACxB,GAAkB,EAClB,eAAe,EACf,eAAkD,GACnD,GAAE;IACD,MAAM,CAAC,EAAE,wBAAwB,GAAG,IAAI,CAAA;IACxC,GAAG,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;IACxB,eAAe,CAAC,EAAE,uBAAuB,CAAA;IACzC,eAAe,CAAC,EAAE,MAAM,CAAA;CACpB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA2BnC;AAED,wBAAsB,qBAAqB,CAAC,QAAQ,SAAS,qBAAqB,EAAE,EAClF,cAAc,EACd,UAAkB,EAClB,GAAG,EACH,eAAe,EACf,WAA6B,EAC7B,eAAkD,GACnD,EAAE;IACD,cAAc,EAAE,CAAC,UAAU,EAAE,yBAAyB,KAAK,QAAQ,CAAA;IACnE,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,GAAG,CAAC,EAAE,WAAW,GAAG,IAAI,CAAA;IACxB,eAAe,CAAC,EAAE,uBAAuB,CAAA;IACzC,WAAW,CAAC,EAAE,wBAAwB,GAAG,IAAI,CAAA;IAC7C,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB,GAAG,OAAO,CAAC,4BAA4B,CAAC,QAAQ,CAAC,CAAC,CAyClD"}
|
|
@@ -71,10 +71,10 @@ export async function detectRendererCapability({ canvas = browserCanvas(), gpu =
|
|
|
71
71
|
}
|
|
72
72
|
return { error: capabilityError, status: 'unsupported' };
|
|
73
73
|
}
|
|
74
|
-
export async function initializeGpuRenderer({ createRenderer, gpu, powerPreference, probeCanvas = browserCanvas(), webgpuTimeoutMs = WEBGPU_INITIALIZATION_TIMEOUT_MS, }) {
|
|
74
|
+
export async function initializeGpuRenderer({ createRenderer, forceWebGL = false, gpu, powerPreference, probeCanvas = browserCanvas(), webgpuTimeoutMs = WEBGPU_INITIALIZATION_TIMEOUT_MS, }) {
|
|
75
75
|
const capability = await detectRendererCapability({
|
|
76
76
|
canvas: probeCanvas,
|
|
77
|
-
gpu,
|
|
77
|
+
gpu: forceWebGL ? null : gpu,
|
|
78
78
|
powerPreference,
|
|
79
79
|
webgpuTimeoutMs,
|
|
80
80
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"door-system.d.ts","sourceRoot":"","sources":["../../../src/systems/door/door-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,QAAQ,EAYd,MAAM,kBAAkB,CAAA;AAGzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AA2D9B,eAAO,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"door-system.d.ts","sourceRoot":"","sources":["../../../src/systems/door/door-system.tsx"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,QAAQ,EAYd,MAAM,kBAAkB,CAAA;AAGzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AA2D9B,eAAO,MAAM,UAAU,YAuHtB,CAAA;AA0pCD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,QAAQ,EACd,IAAI,EAAE,KAAK,CAAC,QAAQ,GAAG,SAAS,EAChC,KAAK,EAAE,MAAM,GACZ,OAAO,CA6GT;AAyuCD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,QAAQ,GAAG,KAAK,CAAC,IAAI,CAI/D"}
|
|
@@ -154,8 +154,9 @@ export const DoorSystem = () => {
|
|
|
154
154
|
// Rebuild the parent wall so its cutout reflects the updated door geometry
|
|
155
155
|
// Avoid triggering expensive wall CSG rebuilds while the door is being interactively moved/duplicated.
|
|
156
156
|
// The editor tools will request a final wall rebuild on commit.
|
|
157
|
-
const
|
|
158
|
-
|
|
157
|
+
const metadata = effectiveNode.metadata;
|
|
158
|
+
const deferParentRebuild = !!metadata?.isTransient || !!metadata?.deferParentRebuild;
|
|
159
|
+
if (!deferParentRebuild && effectiveNode.parentId) {
|
|
159
160
|
useScene.getState().dirtyNodes.add(effectiveNode.parentId);
|
|
160
161
|
}
|
|
161
162
|
}
|
|
@@ -777,9 +778,21 @@ function addDoorLeaf(mesh, { leafWidth, leafHeight, leafCenterX, leafCenterY, le
|
|
|
777
778
|
const hingeH = 0.1;
|
|
778
779
|
const hingeW = 0.024;
|
|
779
780
|
const hingeD = leafDepth + 0.016;
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
781
|
+
const hingeRadii = roundedBoundary?.radii ?? openingTopRadii;
|
|
782
|
+
const cornerDrop = openingShape === 'rounded'
|
|
783
|
+
? hingeSide === 'left'
|
|
784
|
+
? hingeRadii.topLeft
|
|
785
|
+
: hingeRadii.topRight
|
|
786
|
+
: openingShape === 'arch'
|
|
787
|
+
? getClampedArchHeight(leafWidth, leafHeight, archHeight)
|
|
788
|
+
: 0;
|
|
789
|
+
// Hinges need their full height supported by the straight jamb below the curve.
|
|
790
|
+
const straightEdgeTop = leafTop - cornerDrop;
|
|
791
|
+
for (const hingeY of [leafBottom + 0.25, (leafBottom + leafTop) / 2, leafTop - 0.25]) {
|
|
792
|
+
if (hingeY + hingeH / 2 > straightEdgeTop + 1e-6)
|
|
793
|
+
continue;
|
|
794
|
+
addBox(mesh, hardwareMaterial, hingeW, hingeH, hingeD, hingeMarkerX, hingeY, 0);
|
|
795
|
+
}
|
|
783
796
|
}
|
|
784
797
|
// When the leaf is swung open it projects into the room and would otherwise
|
|
785
798
|
// win a top-down selection click over the floor beneath it. Drop only the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"floor-elevation-system.d.ts","sourceRoot":"","sources":["../../../src/systems/floor-elevation/floor-elevation-system.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"floor-elevation-system.d.ts","sourceRoot":"","sources":["../../../src/systems/floor-elevation/floor-elevation-system.tsx"],"names":[],"mappings":"AA8DA;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,oBAAoB,YA2LhC,CAAA"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { getEffectiveNode, getFloorStackedPosition, nodeRegistry, sceneRegistry, useLiveNodeOverrides, useLiveTransforms, useScene, } from '@pascal-app/core';
|
|
2
2
|
import { useFrame } from '@react-three/fiber';
|
|
3
|
+
import { useEffect, useMemo } from 'react';
|
|
4
|
+
import { Euler, Matrix4, Quaternion, Vector3 } from 'three';
|
|
3
5
|
function withLiveTransform(node, liveTransform) {
|
|
4
6
|
if (!liveTransform)
|
|
5
7
|
return node;
|
|
@@ -15,6 +17,16 @@ function withLiveTransform(node, liveTransform) {
|
|
|
15
17
|
...(rotation !== undefined ? { rotation } : {}),
|
|
16
18
|
};
|
|
17
19
|
}
|
|
20
|
+
function restoreMountedExitPose(saved) {
|
|
21
|
+
saved.mesh.matrixAutoUpdate = saved.autoUpdate;
|
|
22
|
+
saved.mesh.scale.copy(saved.scale);
|
|
23
|
+
if (!saved.mesh.parent || saved.mesh.parent === saved.parent) {
|
|
24
|
+
saved.mesh.position.copy(saved.position);
|
|
25
|
+
saved.mesh.quaternion.copy(saved.quaternion);
|
|
26
|
+
}
|
|
27
|
+
saved.mesh.updateMatrix();
|
|
28
|
+
saved.mesh.updateMatrixWorld(true);
|
|
29
|
+
}
|
|
18
30
|
/**
|
|
19
31
|
* Generic floor-elevation system.
|
|
20
32
|
*
|
|
@@ -36,6 +48,56 @@ function withLiveTransform(node, liveTransform) {
|
|
|
36
48
|
export const FloorElevationSystem = () => {
|
|
37
49
|
const dirtyNodes = useScene((s) => s.dirtyNodes);
|
|
38
50
|
const clearDirty = useScene((s) => s.clearDirty);
|
|
51
|
+
const preview = useMemo(() => ({
|
|
52
|
+
local: new Matrix4(),
|
|
53
|
+
target: new Matrix4(),
|
|
54
|
+
position: new Vector3(),
|
|
55
|
+
rotation: new Euler(),
|
|
56
|
+
quaternion: new Quaternion(),
|
|
57
|
+
unit: new Vector3(1, 1, 1),
|
|
58
|
+
saved: new Map(),
|
|
59
|
+
}), []);
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
const restore = (id) => {
|
|
62
|
+
const saved = preview.saved.get(id);
|
|
63
|
+
if (!saved)
|
|
64
|
+
return;
|
|
65
|
+
restoreMountedExitPose(saved);
|
|
66
|
+
preview.saved.delete(id);
|
|
67
|
+
};
|
|
68
|
+
const sync = () => {
|
|
69
|
+
const { nodes } = useScene.getState();
|
|
70
|
+
const overrides = useLiveNodeOverrides.getState().overrides;
|
|
71
|
+
for (const id of preview.saved.keys()) {
|
|
72
|
+
const node = nodes[id];
|
|
73
|
+
const patch = overrides.get(id);
|
|
74
|
+
if (!node || !patch?.parentId || patch.parentId === node.parentId)
|
|
75
|
+
restore(id);
|
|
76
|
+
}
|
|
77
|
+
for (const [id, patch] of overrides) {
|
|
78
|
+
const node = nodes[id];
|
|
79
|
+
if (!node || !patch.parentId || patch.parentId === node.parentId || preview.saved.has(id))
|
|
80
|
+
continue;
|
|
81
|
+
const mesh = sceneRegistry.nodes.get(id);
|
|
82
|
+
if (!mesh?.parent)
|
|
83
|
+
continue;
|
|
84
|
+
preview.saved.set(id, {
|
|
85
|
+
mesh,
|
|
86
|
+
parent: mesh.parent,
|
|
87
|
+
position: mesh.position.clone(),
|
|
88
|
+
quaternion: mesh.quaternion.clone(),
|
|
89
|
+
scale: mesh.scale.clone(),
|
|
90
|
+
autoUpdate: mesh.matrixAutoUpdate,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
const unsubscribe = useLiveNodeOverrides.subscribe(sync);
|
|
95
|
+
return () => {
|
|
96
|
+
unsubscribe();
|
|
97
|
+
for (const id of preview.saved.keys())
|
|
98
|
+
restore(id);
|
|
99
|
+
};
|
|
100
|
+
}, [preview]);
|
|
39
101
|
useFrame(() => {
|
|
40
102
|
// Nodes with a live preview (override / transform) are reapplied EVERY
|
|
41
103
|
// frame, not only while dirty: the React commit that rebinds the group's
|
|
@@ -64,6 +126,8 @@ export const FloorElevationSystem = () => {
|
|
|
64
126
|
const position = effectiveNode.position;
|
|
65
127
|
if (!position)
|
|
66
128
|
return;
|
|
129
|
+
if (effectiveNode.parentId !== node.parentId)
|
|
130
|
+
return;
|
|
67
131
|
if (!(def.geometry || def.system) && dirtyNodes.has(id)) {
|
|
68
132
|
clearDirty(id);
|
|
69
133
|
}
|
|
@@ -110,5 +174,56 @@ export const FloorElevationSystem = () => {
|
|
|
110
174
|
applyLift(id);
|
|
111
175
|
});
|
|
112
176
|
}, 1);
|
|
177
|
+
// PostProcessing draws at priority 1 after this system; later callbacks would show one wrong frame per move.
|
|
178
|
+
useFrame(() => {
|
|
179
|
+
const nodes = useScene.getState().nodes;
|
|
180
|
+
for (const [id, patch] of useLiveNodeOverrides.getState().overrides) {
|
|
181
|
+
const node = nodes[id];
|
|
182
|
+
if (!node || !patch.parentId || patch.parentId === node.parentId)
|
|
183
|
+
continue;
|
|
184
|
+
const effective = getEffectiveNode(node);
|
|
185
|
+
if (effective.parentId === node.parentId || !effective.position)
|
|
186
|
+
continue;
|
|
187
|
+
if (!effective.parentId || nodes[effective.parentId]?.type !== 'level')
|
|
188
|
+
continue;
|
|
189
|
+
const mesh = sceneRegistry.nodes.get(id);
|
|
190
|
+
const level = sceneRegistry.nodes.get(effective.parentId);
|
|
191
|
+
if (!mesh?.parent || !level)
|
|
192
|
+
continue;
|
|
193
|
+
const previous = preview.saved.get(id);
|
|
194
|
+
if (previous?.mesh !== mesh) {
|
|
195
|
+
if (previous)
|
|
196
|
+
restoreMountedExitPose(previous);
|
|
197
|
+
preview.saved.set(id, {
|
|
198
|
+
mesh,
|
|
199
|
+
parent: mesh.parent,
|
|
200
|
+
position: mesh.position.clone(),
|
|
201
|
+
quaternion: mesh.quaternion.clone(),
|
|
202
|
+
scale: mesh.scale.clone(),
|
|
203
|
+
autoUpdate: mesh.matrixAutoUpdate,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
const position = getFloorStackedPosition({
|
|
207
|
+
node: effective,
|
|
208
|
+
nodes,
|
|
209
|
+
position: effective.position,
|
|
210
|
+
});
|
|
211
|
+
const rotation = typeof effective.rotation === 'number'
|
|
212
|
+
? [0, effective.rotation, 0]
|
|
213
|
+
: (effective.rotation ?? [0, 0, 0]);
|
|
214
|
+
level.updateWorldMatrix(true, false);
|
|
215
|
+
mesh.parent.updateWorldMatrix(true, false);
|
|
216
|
+
// The override's parent is logical; the mesh still inherits the mounted host and surface wrapper.
|
|
217
|
+
preview.local
|
|
218
|
+
.copy(mesh.parent.matrixWorld)
|
|
219
|
+
.invert()
|
|
220
|
+
.multiply(level.matrixWorld)
|
|
221
|
+
.multiply(preview.target.compose(preview.position.fromArray(position), preview.quaternion.setFromEuler(preview.rotation.set(rotation[0], rotation[1], rotation[2])), preview.unit));
|
|
222
|
+
// Keep the full matrix: a rotated child beneath nonuniform scale can require shear.
|
|
223
|
+
mesh.matrixAutoUpdate = false;
|
|
224
|
+
mesh.matrix.copy(preview.local);
|
|
225
|
+
mesh.updateMatrixWorld(true);
|
|
226
|
+
}
|
|
227
|
+
}, 1);
|
|
113
228
|
return null;
|
|
114
229
|
};
|