@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.
Files changed (41) hide show
  1. package/dist/components/viewer/index.d.ts +12 -0
  2. package/dist/components/viewer/index.d.ts.map +1 -1
  3. package/dist/components/viewer/index.js +28 -7
  4. package/dist/components/viewer/viewer-camera.d.ts +15 -1
  5. package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
  6. package/dist/components/viewer/viewer-camera.js +43 -5
  7. package/dist/index.d.ts +7 -5
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +7 -5
  10. package/dist/lib/csg-utils.d.ts +1 -1
  11. package/dist/lib/csg-utils.d.ts.map +1 -1
  12. package/dist/lib/csg-utils.js +1 -1
  13. package/dist/lib/renderer-capability.d.ts +2 -1
  14. package/dist/lib/renderer-capability.d.ts.map +1 -1
  15. package/dist/lib/renderer-capability.js +2 -2
  16. package/dist/systems/door/door-system.d.ts.map +1 -1
  17. package/dist/systems/door/door-system.js +18 -5
  18. package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -1
  19. package/dist/systems/floor-elevation/floor-elevation-system.js +115 -0
  20. package/dist/systems/level/level-system.js +14 -14
  21. package/dist/systems/level/level-utils.d.ts +25 -1
  22. package/dist/systems/level/level-utils.d.ts.map +1 -1
  23. package/dist/systems/level/level-utils.js +25 -4
  24. package/dist/systems/wall/wall-cutout-cache.d.ts +4 -1
  25. package/dist/systems/wall/wall-cutout-cache.d.ts.map +1 -1
  26. package/dist/systems/wall/wall-cutout-cache.js +16 -7
  27. package/dist/systems/wall/wall-cutout.d.ts +3 -1
  28. package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
  29. package/dist/systems/wall/wall-cutout.js +5 -5
  30. package/dist/systems/wall/wall-materials.d.ts +11 -2
  31. package/dist/systems/wall/wall-materials.d.ts.map +1 -1
  32. package/dist/systems/wall/wall-materials.js +24 -18
  33. package/dist/systems/wall/wall-system.d.ts +15 -2
  34. package/dist/systems/wall/wall-system.d.ts.map +1 -1
  35. package/dist/systems/wall/wall-system.js +20 -21
  36. package/dist/systems/window/window-system.d.ts.map +1 -1
  37. package/dist/systems/window/window-system.js +3 -2
  38. package/dist/xr/presentation-context.d.ts +7 -0
  39. package/dist/xr/presentation-context.d.ts.map +1 -0
  40. package/dist/xr/presentation-context.js +10 -0
  41. 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;AAS1F,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;AAatE,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;AA8QD,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;CACvB;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,sGA0PV,CAAA;AAEF,eAAe,MAAM,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, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
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, {}), _jsxs(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) => {
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: [_jsx(FrameLimiter, { fps: maxFps, paused: renderPaused }), _jsx(ViewerCamera, {}), _jsx(PointerRaycastLayers, {}), _jsx(GPUDeviceWatcher, {}), _jsx(ToneMappingExposure, {}), _jsx(SceneReadyTracker, { onSceneReadyChange: onSceneReadyChange, sceneReadyKey: sceneReadyKey, sceneReadyMaxWaitMs: sceneReadyMaxWaitMs }), _jsxs(ErrorBoundary, { fallback: null, scope: "viewer-scene", children: [_jsx(Lights, {}), useBvh ? (_jsx(SceneBvh, { children: _jsx(SceneRenderer, {}) })) : (_jsx(SceneRenderer, {})), _jsx(FloorElevationSystem, {}), _jsx(GeometrySystem, {}), _jsx(StairOpeningSystem, {}), _jsx(RoofElevationSystem, {}), _jsx(RegisteredSystems, {}), _jsx(PostProcessing, { disablePostFx: disablePostFx, hoverStyles: hoverStyles }), selectionManager === 'default' && _jsx(SelectionManager, {}), (perf || PERF_OVERLAY_ENABLED) && _jsx(PerfMonitor, {}), (perf || PERF_OVERLAY_ENABLED) && _jsx(PerfActionSettleSystem, {}), BATCH_SPIKE_ENABLED && _jsx(BatchedMeshSpike, {}), 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
- export declare const ViewerCamera: () => import("react").JSX.Element;
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":"AAIA,eAAO,MAAM,YAAY,mCAUxB,CAAA"}
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
- export const ViewerCamera = () => {
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
- // Exterior ground can include a coarse ocean horizon beyond the local terrain.
8
- const far = useSceneGroundReplacement() ? 20_000 : 1000;
9
- return cameraMode === 'perspective' ? (_jsx(PerspectiveCamera, { far: far, fov: 50, makeDefault: true, near: 0.1, position: [10, 10, 10] })) : (_jsx(OrthographicCamera, { far: 1000, makeDefault: true, near: -1000, position: [10, 10, 10], zoom: 20 }));
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 { getVisibleWallMaterials } from './systems/wall/wall-materials';
69
- export { drainRebuiltWalls, getPendingWallRebuildCount, isWallInitialBuildActive, WallSystem, } from './systems/wall/wall-system';
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
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
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 { getVisibleWallMaterials } from './systems/wall/wall-materials';
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';
@@ -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"}
@@ -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,YAsHtB,CAAA;AAupCD;;;;;;;;;;;;;;;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"}
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 isTransient = !!node.metadata?.isTransient;
158
- if (!isTransient && effectiveNode.parentId) {
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
- addBox(mesh, hardwareMaterial, hingeW, hingeH, hingeD, hingeMarkerX, leafBottom + 0.25, 0);
781
- addBox(mesh, hardwareMaterial, hingeW, hingeH, hingeD, hingeMarkerX, (leafBottom + leafTop) / 2, 0);
782
- addBox(mesh, hardwareMaterial, hingeW, hingeH, hingeD, hingeMarkerX, leafTop - 0.25, 0);
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":"AAyCA;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,oBAAoB,YAiFhC,CAAA"}
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
  };