@pascal-app/viewer 0.5.0 → 0.6.0

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 (65) hide show
  1. package/dist/components/renderers/building/building-renderer.d.ts.map +1 -1
  2. package/dist/components/renderers/ceiling/ceiling-renderer.d.ts.map +1 -1
  3. package/dist/components/renderers/ceiling/ceiling-renderer.js +4 -3
  4. package/dist/components/renderers/door/door-renderer.d.ts.map +1 -1
  5. package/dist/components/renderers/door/door-renderer.js +5 -2
  6. package/dist/components/renderers/fence/fence-renderer.d.ts +5 -0
  7. package/dist/components/renderers/fence/fence-renderer.d.ts.map +1 -0
  8. package/dist/components/renderers/fence/fence-renderer.js +29 -0
  9. package/dist/components/renderers/node-renderer.d.ts.map +1 -1
  10. package/dist/components/renderers/node-renderer.js +2 -1
  11. package/dist/components/renderers/roof/roof-renderer.d.ts.map +1 -1
  12. package/dist/components/renderers/roof/roof-renderer.js +31 -9
  13. package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts.map +1 -1
  14. package/dist/components/renderers/roof-segment/roof-segment-renderer.js +42 -8
  15. package/dist/components/renderers/site/site-renderer.d.ts.map +1 -1
  16. package/dist/components/renderers/site/site-renderer.js +7 -1
  17. package/dist/components/renderers/slab/slab-renderer.d.ts.map +1 -1
  18. package/dist/components/renderers/slab/slab-renderer.js +37 -8
  19. package/dist/components/renderers/stair/stair-renderer.d.ts.map +1 -1
  20. package/dist/components/renderers/stair/stair-renderer.js +605 -5
  21. package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts.map +1 -1
  22. package/dist/components/renderers/stair-segment/stair-segment-renderer.js +37 -8
  23. package/dist/components/renderers/wall/wall-renderer.d.ts.map +1 -1
  24. package/dist/components/renderers/wall/wall-renderer.js +4 -9
  25. package/dist/components/renderers/window/window-renderer.d.ts.map +1 -1
  26. package/dist/components/renderers/window/window-renderer.js +5 -2
  27. package/dist/components/viewer/frame-limiter.d.ts +6 -0
  28. package/dist/components/viewer/frame-limiter.d.ts.map +1 -0
  29. package/dist/components/viewer/frame-limiter.js +31 -0
  30. package/dist/components/viewer/ground-occluder.d.ts.map +1 -1
  31. package/dist/components/viewer/ground-occluder.js +5 -1
  32. package/dist/components/viewer/index.d.ts.map +1 -1
  33. package/dist/components/viewer/index.js +12 -12
  34. package/dist/components/viewer/post-processing.d.ts.map +1 -1
  35. package/dist/components/viewer/post-processing.js +72 -28
  36. package/dist/components/viewer/selection-manager.d.ts.map +1 -1
  37. package/dist/components/viewer/selection-manager.js +21 -2
  38. package/dist/hooks/use-node-events.d.ts +5 -1
  39. package/dist/hooks/use-node-events.d.ts.map +1 -1
  40. package/dist/hooks/use-node-events.js +2 -0
  41. package/dist/index.d.ts +1 -0
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +1 -0
  44. package/dist/lib/materials.d.ts +4 -1
  45. package/dist/lib/materials.d.ts.map +1 -1
  46. package/dist/lib/materials.js +178 -2
  47. package/dist/lib/merged-outline-node.d.ts.map +1 -1
  48. package/dist/lib/merged-outline-node.js +11 -4
  49. package/dist/store/use-viewer.d.ts +2 -2
  50. package/dist/systems/guide/guide-system.d.ts.map +1 -1
  51. package/dist/systems/guide/guide-system.js +26 -1
  52. package/dist/systems/roof/roof-materials.d.ts +5 -0
  53. package/dist/systems/roof/roof-materials.d.ts.map +1 -0
  54. package/dist/systems/roof/roof-materials.js +29 -0
  55. package/dist/systems/scan/scan-system.d.ts.map +1 -1
  56. package/dist/systems/scan/scan-system.js +26 -1
  57. package/dist/systems/stair/stair-materials.d.ts +7 -0
  58. package/dist/systems/stair/stair-materials.d.ts.map +1 -0
  59. package/dist/systems/stair/stair-materials.js +33 -0
  60. package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
  61. package/dist/systems/wall/wall-cutout.js +39 -124
  62. package/dist/systems/wall/wall-materials.d.ts +16 -0
  63. package/dist/systems/wall/wall-materials.d.ts.map +1 -0
  64. package/dist/systems/wall/wall-materials.js +148 -0
  65. package/package.json +4 -4
@@ -1,20 +1,49 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useRegistry, useScene } from '@pascal-app/core';
3
- import { useLayoutEffect, useMemo, useRef } from 'react';
3
+ import { useEffect, useLayoutEffect, useMemo, useRef } from 'react';
4
+ import * as THREE from 'three';
4
5
  import { useNodeEvents } from '../../../hooks/use-node-events';
5
- import { createMaterial, DEFAULT_STAIR_MATERIAL } from '../../../lib/materials';
6
+ import { getStraightStairSegmentBodyMaterials } from '../../../systems/stair/stair-materials';
6
7
  export const StairSegmentRenderer = ({ node }) => {
7
8
  const ref = useRef(null);
9
+ const nodes = useScene((state) => state.nodes);
8
10
  useRegistry(node.id, 'stair-segment', ref);
9
11
  useLayoutEffect(() => {
10
12
  useScene.getState().markDirty(node.id);
11
13
  }, [node.id]);
12
14
  const handlers = useNodeEvents(node, 'stair-segment');
15
+ const parentNode = node.parentId ? nodes[node.parentId] : undefined;
13
16
  const material = useMemo(() => {
14
- const mat = node.material;
15
- if (!mat)
16
- return DEFAULT_STAIR_MATERIAL;
17
- return createMaterial(mat);
18
- }, [node.material, node.material?.preset, node.material?.properties, node.material?.texture]);
19
- return (_jsx("mesh", { material: material, position: node.position, ref: ref, "rotation-y": node.rotation, visible: node.visible, ...handlers, children: _jsx("boxGeometry", { args: [0, 0, 0] }) }));
17
+ return getStraightStairSegmentBodyMaterials(node, parentNode);
18
+ }, [
19
+ node.materialPreset,
20
+ node.material,
21
+ node.material?.preset,
22
+ node.material?.properties,
23
+ node.material?.texture,
24
+ parentNode?.materialPreset,
25
+ parentNode?.material,
26
+ parentNode?.material?.preset,
27
+ parentNode?.material?.properties,
28
+ parentNode?.material?.texture,
29
+ parentNode?.railingMaterialPreset,
30
+ parentNode?.railingMaterial,
31
+ parentNode?.sideMaterialPreset,
32
+ parentNode?.sideMaterial,
33
+ parentNode?.treadMaterialPreset,
34
+ parentNode?.treadMaterial,
35
+ ]);
36
+ const placeholderGeometry = useMemo(() => {
37
+ const geometry = new THREE.BufferGeometry();
38
+ geometry.setAttribute('position', new THREE.Float32BufferAttribute([], 3));
39
+ geometry.addGroup(0, 0, 0);
40
+ geometry.addGroup(0, 0, 1);
41
+ return geometry;
42
+ }, []);
43
+ useEffect(() => {
44
+ return () => {
45
+ placeholderGeometry.dispose();
46
+ };
47
+ }, [placeholderGeometry]);
48
+ return (_jsx("mesh", { geometry: placeholderGeometry, material: material, position: node.position, ref: ref, "rotation-y": node.rotation, visible: node.visible, ...handlers }));
20
49
  };
@@ -1 +1 @@
1
- {"version":3,"file":"wall-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/wall/wall-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAOvE,eAAO,MAAM,YAAY,GAAI,UAAU;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,4CA6BxD,CAAA"}
1
+ {"version":3,"file":"wall-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/wall/wall-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAOvE,eAAO,MAAM,YAAY,GAAI,UAAU;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,4CAwBxD,CAAA"}
@@ -1,8 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useRegistry, useScene } from '@pascal-app/core';
3
- import { useLayoutEffect, useMemo, useRef } from 'react';
3
+ import { useLayoutEffect, useRef } from 'react';
4
4
  import { useNodeEvents } from '../../../hooks/use-node-events';
5
- import { createMaterial, DEFAULT_WALL_MATERIAL } from '../../../lib/materials';
5
+ import { getVisibleWallMaterials } from '../../../systems/wall/wall-materials';
6
6
  import { NodeRenderer } from '../node-renderer';
7
7
  export const WallRenderer = ({ node }) => {
8
8
  const ref = useRef(null);
@@ -11,11 +11,6 @@ export const WallRenderer = ({ node }) => {
11
11
  useScene.getState().markDirty(node.id);
12
12
  }, [node.id]);
13
13
  const handlers = useNodeEvents(node, 'wall');
14
- const material = useMemo(() => {
15
- const mat = node.material;
16
- if (!mat)
17
- return DEFAULT_WALL_MATERIAL;
18
- return createMaterial(mat);
19
- }, [node.material, node.material?.preset, node.material?.properties, node.material?.texture]);
20
- return (_jsxs("mesh", { castShadow: true, material: material, receiveShadow: true, ref: ref, visible: node.visible, children: [_jsx("boxGeometry", { args: [0, 0, 0] }), _jsx("mesh", { name: "collision-mesh", visible: false, ...handlers, children: _jsx("boxGeometry", { args: [0, 0, 0] }) }), node.children.map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId)))] }));
14
+ const material = getVisibleWallMaterials(node);
15
+ return (_jsxs("mesh", { castShadow: true, material: material, receiveShadow: true, ref: ref, visible: node.visible, children: [_jsx("boxGeometry", { args: [0, 0, 0] }), _jsx("mesh", { name: "collision-mesh", visible: false, ...handlers, children: _jsx("boxGeometry", { args: [0, 0, 0] }) }), node.children.map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, `${node.id}:${childId}`)))] }));
21
16
  };
@@ -1 +1 @@
1
- {"version":3,"file":"window-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/window/window-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAM/D,eAAO,MAAM,cAAc,GAAI,UAAU;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,4CA2B5D,CAAA"}
1
+ {"version":3,"file":"window-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/window/window-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAMzE,eAAO,MAAM,cAAc,GAAI,UAAU;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,4CA8B5D,CAAA"}
@@ -1,11 +1,14 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useRegistry } from '@pascal-app/core';
3
- import { useMemo, useRef } from 'react';
2
+ import { useRegistry, useScene } from '@pascal-app/core';
3
+ import { useLayoutEffect, useMemo, useRef } from 'react';
4
4
  import { useNodeEvents } from '../../../hooks/use-node-events';
5
5
  import { createMaterial, DEFAULT_WINDOW_MATERIAL } from '../../../lib/materials';
6
6
  export const WindowRenderer = ({ node }) => {
7
7
  const ref = useRef(null);
8
8
  useRegistry(node.id, 'window', ref);
9
+ useLayoutEffect(() => {
10
+ useScene.getState().markDirty(node.id);
11
+ }, [node.id]);
9
12
  const handlers = useNodeEvents(node, 'window');
10
13
  const isTransient = !!node.metadata?.isTransient;
11
14
  const material = useMemo(() => {
@@ -0,0 +1,6 @@
1
+ type FrameLimiterProps = {
2
+ fps?: number;
3
+ };
4
+ declare const FrameLimiter: React.FC<FrameLimiterProps>;
5
+ export default FrameLimiter;
6
+ //# sourceMappingURL=frame-limiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame-limiter.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/frame-limiter.tsx"],"names":[],"mappings":"AAGA,KAAK,iBAAiB,GAAG;IACvB,GAAG,CAAC,EAAE,MAAM,CAAA;CACb,CAAA;AAED,QAAA,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAgC7C,CAAA;AAED,eAAe,YAAY,CAAA"}
@@ -0,0 +1,31 @@
1
+ import { useThree } from '@react-three/fiber';
2
+ import { useLayoutEffect } from 'react';
3
+ const FrameLimiter = ({ fps = 50 }) => {
4
+ const { advance, set, frameloop: initFrameloop } = useThree();
5
+ useLayoutEffect(() => {
6
+ let elapsed = 0;
7
+ let then = 0;
8
+ let i = 0;
9
+ let raf = null;
10
+ const interval = 1000 / fps;
11
+ function tick(t) {
12
+ raf = requestAnimationFrame(tick);
13
+ elapsed = t - then;
14
+ if (elapsed > interval) {
15
+ advance(i);
16
+ i += elapsed / 1000 - (elapsed % interval) / 1000;
17
+ then = t - (elapsed % interval);
18
+ }
19
+ }
20
+ set({ frameloop: 'never' });
21
+ raf = requestAnimationFrame(tick);
22
+ return () => {
23
+ if (raf) {
24
+ cancelAnimationFrame(raf);
25
+ }
26
+ set({ frameloop: initFrameloop });
27
+ };
28
+ }, [fps, advance, set, initFrameloop]);
29
+ return null;
30
+ };
31
+ export default FrameLimiter;
@@ -1 +1 @@
1
- {"version":3,"file":"ground-occluder.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/ground-occluder.tsx"],"names":[],"mappings":"AAMA,eAAO,MAAM,cAAc,+CA+F1B,CAAA"}
1
+ {"version":3,"file":"ground-occluder.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/ground-occluder.tsx"],"names":[],"mappings":"AAMA,eAAO,MAAM,cAAc,+CAuG1B,CAAA"}
@@ -31,7 +31,11 @@ export const GroundOccluder = () => {
31
31
  // reveal their footprint on the level-zero ground material.
32
32
  const polygons = [];
33
33
  Object.values(nodes).forEach((node) => {
34
- if (!(node.type === 'slab' && node.visible && node.polygon.length >= 3)) {
34
+ if (!(node.type === 'slab' &&
35
+ node.visible &&
36
+ node.polygon.length >= 3 &&
37
+ // Only recessed slabs should punch through the ground plane.
38
+ (node.elevation ?? 0.05) < 0)) {
35
39
  return;
36
40
  }
37
41
  if (Number.isFinite(lowestLevelIndex)) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/index.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAkB,KAAK,kBAAkB,EAAsB,MAAM,oBAAoB,CAAA;AAEhG,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AA0CrC,OAAO,QAAQ,oBAAoB,CAAC;IAClC,UAAU,aAAc,SAAQ,kBAAkB,CAAC,OAAO,KAAK,CAAC;KAAG;CACpE;AA+BD,UAAU,WAAW;IACnB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC1B,gBAAgB,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;IACvC,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED,QAAA,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CA8DjC,CAAA;AASD,eAAe,MAAM,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/index.tsx"],"names":[],"mappings":"AAcA,OAAO,EAAkB,KAAK,kBAAkB,EAAsB,MAAM,oBAAoB,CAAA;AAEhG,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AA2CrC,OAAO,QAAQ,oBAAoB,CAAC;IAClC,UAAU,aAAc,SAAQ,kBAAkB,CAAC,OAAO,KAAK,CAAC;KAAG;CACpE;AAwCD,UAAU,WAAW;IACnB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC1B,gBAAgB,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;IACvC,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED,QAAA,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CAsEjC,CAAA;AASD,eAAe,MAAM,CAAA"}
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { CeilingSystem, DoorSystem, ItemSystem, RoofSystem, SlabSystem, StairSystem, WallSystem, WindowSystem, } from '@pascal-app/core';
3
+ import { CeilingSystem, DoorSystem, FenceSystem, ItemSystem, RoofSystem, SlabSystem, StairSystem, WallSystem, WindowSystem, } from '@pascal-app/core';
4
4
  import { Bvh } from '@react-three/drei';
5
5
  import { Canvas, extend, useFrame, useThree } from '@react-three/fiber';
6
6
  import { useEffect, useMemo, useRef } from 'react';
@@ -13,6 +13,7 @@ import { ScanSystem } from '../../systems/scan/scan-system';
13
13
  import { WallCutout } from '../../systems/wall/wall-cutout';
14
14
  import { ZoneSystem } from '../../systems/zone/zone-system';
15
15
  import { SceneRenderer } from '../renderers/scene-renderer';
16
+ import FrameLimiter from './frame-limiter';
16
17
  import { Lights } from './lights';
17
18
  import { PerfMonitor } from './perf-monitor';
18
19
  import PostProcessing from './post-processing';
@@ -40,13 +41,6 @@ function AnimatedBackground({ isDark }) {
40
41
  return null;
41
42
  }
42
43
  extend(THREE);
43
- /**
44
- * Monitors the WebGPU device for loss events and logs them.
45
- * WebGPU device loss can happen when:
46
- * - Tab is backgrounded and OS reclaims GPU
47
- * - Driver crash or GPU reset
48
- * - Browser security policy kills the context
49
- */
50
44
  function GPUDeviceWatcher() {
51
45
  const gl = useThree((s) => s.gl);
52
46
  useEffect(() => {
@@ -55,7 +49,7 @@ function GPUDeviceWatcher() {
55
49
  if (!device)
56
50
  return;
57
51
  device.lost.then((info) => {
58
- console.error(`[viewer] WebGPU device lost: reason="${info.reason}", message="${info.message}". ` +
52
+ console.error(`[viewer] WebGPU device lost: reason="${info.reason ?? 'unknown'}", message="${info.message ?? ''}". ` +
59
53
  'The page must be reloaded to recover the GPU context.');
60
54
  });
61
55
  }, [gl]);
@@ -63,18 +57,24 @@ function GPUDeviceWatcher() {
63
57
  }
64
58
  const Viewer = ({ children, selectionManager = 'default', perf = false, }) => {
65
59
  const theme = useViewer((state) => state.theme);
66
- return (_jsxs(Canvas, { camera: { position: [50, 50, 50], fov: 50 }, className: `transition-colors duration-700 ${theme === 'dark' ? 'bg-[#1f2433]' : 'bg-[#fafafa]'}`, dpr: [1, 1.5], gl: (props) => {
60
+ return (_jsxs(Canvas, { camera: { position: [50, 50, 50], fov: 50 }, className: `transition-colors duration-700 ${theme === 'dark' ? 'bg-[#1f2433]' : 'bg-[#fafafa]'}`, dpr: [1, 1.5], frameloop: "never", gl: async (props) => {
67
61
  const renderer = new THREE.WebGPURenderer(props);
68
62
  renderer.toneMapping = THREE.ACESFilmicToneMapping;
69
63
  renderer.toneMappingExposure = 0.9;
70
- // renderer.init() // Only use when using <DebugRenderer />
64
+ // Awaiting init() is required when the browser falls back to the
65
+ // WebGL2 backend (Safari without the WebGPU flag, older Chrome on
66
+ // machines without a WebGPU device). In native WebGPU mode the
67
+ // init resolves almost instantly. Without this await, the first
68
+ // render throws "Renderer: .render() called before the backend is
69
+ // initialized" from the post-processing fallback path.
70
+ await renderer.init();
71
71
  return renderer;
72
72
  }, resize: {
73
73
  debounce: 100,
74
74
  }, shadows: {
75
75
  type: THREE.PCFShadowMap,
76
76
  enabled: true,
77
- }, children: [_jsx(ViewerCamera, {}), _jsx(Lights, {}), _jsx(Bvh, { children: _jsx(SceneRenderer, {}) }), _jsx(LevelSystem, {}), _jsx(GuideSystem, {}), _jsx(ScanSystem, {}), _jsx(WallCutout, {}), _jsx(CeilingSystem, {}), _jsx(DoorSystem, {}), _jsx(ItemSystem, {}), _jsx(RoofSystem, {}), _jsx(SlabSystem, {}), _jsx(StairSystem, {}), _jsx(WallSystem, {}), _jsx(WindowSystem, {}), _jsx(ZoneSystem, {}), _jsx(PostProcessing, {}), _jsx(GPUDeviceWatcher, {}), _jsx(ItemLightSystem, {}), selectionManager === 'default' && _jsx(SelectionManager, {}), perf && _jsx(PerfMonitor, {}), children] }));
77
+ }, children: [_jsx(FrameLimiter, { fps: 50 }), _jsx(ViewerCamera, {}), _jsx(Lights, {}), _jsx(Bvh, { children: _jsx(SceneRenderer, {}) }), _jsx(LevelSystem, {}), _jsx(GuideSystem, {}), _jsx(ScanSystem, {}), _jsx(WallCutout, {}), _jsx(CeilingSystem, {}), _jsx(DoorSystem, {}), _jsx(FenceSystem, {}), _jsx(ItemSystem, {}), _jsx(RoofSystem, {}), _jsx(SlabSystem, {}), _jsx(StairSystem, {}), _jsx(WallSystem, {}), _jsx(WindowSystem, {}), _jsx(ZoneSystem, {}), _jsx(PostProcessing, {}), _jsx(GPUDeviceWatcher, {}), _jsx(ItemLightSystem, {}), selectionManager === 'default' && _jsx(SelectionManager, {}), perf && _jsx(PerfMonitor, {}), children] }));
78
78
  };
79
79
  const DebugRenderer = () => {
80
80
  useFrame(({ gl, scene, camera }) => {
@@ -1 +1 @@
1
- {"version":3,"file":"post-processing.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/post-processing.tsx"],"names":[],"mappings":"AA4BA,eAAO,MAAM,WAAW;;;;;;;;;;;;;CAavB,CAAA;AAQD,QAAA,MAAM,oBAAoB,YAoQzB,CAAA;AAED,eAAe,oBAAoB,CAAA"}
1
+ {"version":3,"file":"post-processing.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/post-processing.tsx"],"names":[],"mappings":"AA4BA,eAAO,MAAM,WAAW;;;;;;;;;;;;;CAavB,CAAA;AAuBD,QAAA,MAAM,oBAAoB,YAoSzB,CAAA;AAED,eAAe,oBAAoB,CAAA"}
@@ -27,12 +27,23 @@ const MAX_PIPELINE_RETRIES = 3;
27
27
  const RETRY_DELAY_MS = 500;
28
28
  const DARK_BG = '#1f2433';
29
29
  const LIGHT_BG = '#ffffff';
30
+ function sanitizeOutlineObjects(objects) {
31
+ let nextIndex = 0;
32
+ for (const object of objects) {
33
+ if (!(object && typeof object.id === 'number' && object.parent)) {
34
+ continue;
35
+ }
36
+ objects[nextIndex] = object;
37
+ nextIndex++;
38
+ }
39
+ objects.length = nextIndex;
40
+ }
30
41
  const PostProcessingPasses = () => {
31
42
  const { gl: renderer, scene, camera } = useThree();
32
43
  const renderPipelineRef = useRef(null);
33
44
  const hasPipelineErrorRef = useRef(false);
34
45
  const retryCountRef = useRef(0);
35
- const [isInitialized, setIsInitialized] = useState(false);
46
+ const rebuildTimeoutRef = useRef(null);
36
47
  // Background color uniform — updated every frame via lerp, read by the TSL pipeline.
37
48
  // Initialised from the current theme so there's no flash on first render.
38
49
  const initBg = useViewer.getState().theme === 'dark' ? DARK_BG : LIGHT_BG;
@@ -51,45 +62,60 @@ const PostProcessingPasses = () => {
51
62
  // Bump this to force a pipeline rebuild (used by retry logic)
52
63
  const [pipelineVersion, setPipelineVersion] = useState(0);
53
64
  const requestPipelineRebuild = useCallback(() => {
65
+ if (rebuildTimeoutRef.current !== null) {
66
+ clearTimeout(rebuildTimeoutRef.current);
67
+ rebuildTimeoutRef.current = null;
68
+ }
54
69
  setPipelineVersion((v) => v + 1);
55
70
  }, []);
56
- // Renderer initialization
71
+ // Reset retry state when project changes
72
+ useEffect(() => {
73
+ // Intentionally touch projectId so the effect reruns on project switches.
74
+ void projectId;
75
+ retryCountRef.current = 0;
76
+ if (rebuildTimeoutRef.current !== null) {
77
+ clearTimeout(rebuildTimeoutRef.current);
78
+ rebuildTimeoutRef.current = null;
79
+ }
80
+ }, [projectId]);
57
81
  useEffect(() => {
58
- let mounted = true;
59
- const initRenderer = async () => {
60
- try {
61
- if (renderer && renderer.init) {
62
- await renderer.init();
63
- }
64
- if (mounted) {
65
- setIsInitialized(true);
66
- }
67
- }
68
- catch (error) {
69
- console.error('[viewer] Failed to initialize renderer for post-processing.', error);
70
- if (mounted) {
71
- setIsInitialized(false);
72
- }
73
- }
74
- };
75
- initRenderer();
76
82
  return () => {
77
- mounted = false;
83
+ if (rebuildTimeoutRef.current !== null) {
84
+ clearTimeout(rebuildTimeoutRef.current);
85
+ rebuildTimeoutRef.current = null;
86
+ }
78
87
  };
79
- }, [renderer]);
80
- // Reset retry count when project changes
81
- useEffect(() => {
82
- retryCountRef.current = 0;
83
88
  }, []);
84
89
  // Build / rebuild the post-processing pipeline
85
90
  useEffect(() => {
86
- if (!(renderer && scene && camera && isInitialized)) {
91
+ // Intentionally touch these so React/biome treat project switches and retry bumps
92
+ // as explicit rebuild triggers instead of accidental extra dependencies.
93
+ void projectId;
94
+ void pipelineVersion;
95
+ if (!(renderer && scene && camera)) {
87
96
  return;
88
97
  }
89
98
  hasPipelineErrorRef.current = false;
99
+ // WebGPU availability check: SSGI, denoise, and RenderPipeline are all
100
+ // WebGPU-only APIs. When the browser falls back to WebGL2 (no
101
+ // `navigator.gpu`, or the device couldn't be created), building the
102
+ // pipeline either throws silently or produces a broken output where
103
+ // the scene renders for a few frames and then goes black as the retry
104
+ // loop fights the direct-render fallback path. Short-circuit here so
105
+ // `useFrame` uses the direct `renderer.render(scene, camera)` path
106
+ // exclusively and never attempts the TSL pipeline.
107
+ const hasWebGPU = typeof navigator !== 'undefined' && 'gpu' in navigator;
108
+ if (!hasWebGPU) {
109
+ console.warn('[viewer] WebGPU unavailable — rendering without post-processing (SSGI, outlines, denoise).');
110
+ hasPipelineErrorRef.current = true;
111
+ renderPipelineRef.current = null;
112
+ return;
113
+ }
90
114
  // Clear outliner arrays synchronously to prevent stale Object3D refs
91
115
  // from the previous project leaking into the new pipeline's outline passes.
92
116
  const outliner = useViewer.getState().outliner;
117
+ sanitizeOutlineObjects(outliner.selectedObjects);
118
+ sanitizeOutlineObjects(outliner.hoveredObjects);
93
119
  outliner.selectedObjects.length = 0;
94
120
  outliner.hoveredObjects.length = 0;
95
121
  try {
@@ -179,6 +205,7 @@ const PostProcessingPasses = () => {
179
205
  const renderPipeline = new RenderPipeline(renderer);
180
206
  renderPipeline.outputNode = finalOutput;
181
207
  renderPipelineRef.current = renderPipeline;
208
+ retryCountRef.current = 0;
182
209
  }
183
210
  catch (error) {
184
211
  hasPipelineErrorRef.current = true;
@@ -194,13 +221,27 @@ const PostProcessingPasses = () => {
194
221
  }
195
222
  renderPipelineRef.current = null;
196
223
  };
197
- }, [renderer, scene, camera, hoverHighlightMode, isInitialized, zoneLayers]);
224
+ }, [renderer, scene, camera, hoverHighlightMode, zoneLayers, projectId, pipelineVersion]);
198
225
  useFrame((_, delta) => {
199
226
  // Animate background colour toward the current theme target (same lerp as AnimatedBackground)
200
227
  bgTarget.current.set(useViewer.getState().theme === 'dark' ? DARK_BG : LIGHT_BG);
201
228
  bgCurrent.current.lerp(bgTarget.current, Math.min(delta, 0.1) * 4);
202
229
  bgUniform.current.value.copy(bgCurrent.current);
230
+ const outliner = useViewer.getState().outliner;
231
+ sanitizeOutlineObjects(outliner.selectedObjects);
232
+ sanitizeOutlineObjects(outliner.hoveredObjects);
203
233
  if (hasPipelineErrorRef.current || !renderPipelineRef.current) {
234
+ try {
235
+ if (renderer.setClearAlpha) {
236
+ ;
237
+ renderer.setClearAlpha(1);
238
+ }
239
+ ;
240
+ renderer.render(scene, camera);
241
+ }
242
+ catch (fallbackError) {
243
+ console.error('[viewer] Fallback render failed.', fallbackError);
244
+ }
204
245
  return;
205
246
  }
206
247
  try {
@@ -221,7 +262,10 @@ const PostProcessingPasses = () => {
221
262
  // Auto-retry: schedule a pipeline rebuild if we haven't exceeded the retry limit
222
263
  retryCountRef.current++;
223
264
  console.warn(`[viewer] Scheduling post-processing rebuild (attempt ${retryCountRef.current}/${MAX_PIPELINE_RETRIES})`);
224
- setTimeout(requestPipelineRebuild, RETRY_DELAY_MS);
265
+ if (rebuildTimeoutRef.current !== null) {
266
+ clearTimeout(rebuildTimeoutRef.current);
267
+ }
268
+ rebuildTimeoutRef.current = setTimeout(requestPipelineRebuild, RETRY_DELAY_MS);
225
269
  }
226
270
  else {
227
271
  console.error('[viewer] Post-processing retries exhausted. Rendering without post FX for this session.');
@@ -1 +1 @@
1
- {"version":3,"file":"selection-manager.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/selection-manager.tsx"],"names":[],"mappings":"AAwQA,eAAO,MAAM,gBAAgB,+CAsE5B,CAAA"}
1
+ {"version":3,"file":"selection-manager.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/selection-manager.tsx"],"names":[],"mappings":"AAiRA,eAAO,MAAM,gBAAgB,+CA2E5B,CAAA"}
@@ -88,6 +88,12 @@ const isNodeInZone = (node, levelId, zoneId) => {
88
88
  const endIn = pointInPolygonWithTolerance(wall.end[0], wall.end[1], zone.polygon);
89
89
  return startIn || endIn;
90
90
  }
91
+ if (node.type === 'fence') {
92
+ const fence = node;
93
+ const startIn = pointInPolygonWithTolerance(fence.start[0], fence.start[1], zone.polygon);
94
+ const endIn = pointInPolygonWithTolerance(fence.end[0], fence.end[1], zone.polygon);
95
+ return startIn || endIn;
96
+ }
91
97
  if (node.type === 'slab' || node.type === 'ceiling') {
92
98
  const poly = node.polygon;
93
99
  if (!poly?.length)
@@ -164,7 +170,7 @@ const getStrategy = () => {
164
170
  }
165
171
  // Zone selected -> can select/hover contents (walls, items, slabs, ceilings, roofs, windows, doors)
166
172
  return {
167
- types: ['wall', 'item', 'slab', 'ceiling', 'roof', 'roof-segment', 'window', 'door'],
173
+ types: ['wall', 'fence', 'item', 'slab', 'ceiling', 'roof', 'roof-segment', 'window', 'door'],
168
174
  handleClick: (node, nativeEvent) => {
169
175
  let nodeToSelect = node;
170
176
  if (node.type === 'roof-segment' && node.parentId) {
@@ -191,6 +197,7 @@ const getStrategy = () => {
191
197
  isValid: (node) => {
192
198
  const validTypes = [
193
199
  'wall',
200
+ 'fence',
194
201
  'item',
195
202
  'slab',
196
203
  'ceiling',
@@ -215,6 +222,10 @@ export const SelectionManager = () => {
215
222
  return;
216
223
  if (strategy.isValid(event.node)) {
217
224
  event.stopPropagation();
225
+ if (event.node.type === 'slab') {
226
+ useViewer.setState({ hoveredId: null });
227
+ return;
228
+ }
218
229
  useViewer.setState({ hoveredId: event.node.id });
219
230
  }
220
231
  };
@@ -245,6 +256,7 @@ export const SelectionManager = () => {
245
256
  'level',
246
257
  'zone',
247
258
  'wall',
259
+ 'fence',
248
260
  'item',
249
261
  'slab',
250
262
  'ceiling',
@@ -303,10 +315,14 @@ const OutlinerSync = () => {
303
315
  const selection = useViewer((s) => s.selection);
304
316
  const hoveredId = useViewer((s) => s.hoveredId);
305
317
  const outliner = useViewer((s) => s.outliner);
318
+ const nodes = useScene((s) => s.nodes);
306
319
  useEffect(() => {
307
320
  // Sync selected objects
308
321
  outliner.selectedObjects.length = 0;
309
322
  for (const id of selection.selectedIds) {
323
+ const node = nodes[id];
324
+ if (node?.type === 'slab')
325
+ continue;
310
326
  const obj = sceneRegistry.nodes.get(id);
311
327
  if (obj)
312
328
  outliner.selectedObjects.push(obj);
@@ -314,10 +330,13 @@ const OutlinerSync = () => {
314
330
  // Sync hovered objects
315
331
  outliner.hoveredObjects.length = 0;
316
332
  if (hoveredId) {
333
+ const hoveredNode = nodes[hoveredId];
334
+ if (hoveredNode?.type === 'slab')
335
+ return;
317
336
  const obj = sceneRegistry.nodes.get(hoveredId);
318
337
  if (obj)
319
338
  outliner.hoveredObjects.push(obj);
320
339
  }
321
- }, [selection, hoveredId, outliner]);
340
+ }, [selection, hoveredId, outliner, nodes]);
322
341
  return null;
323
342
  };
@@ -1,4 +1,4 @@
1
- import { type BuildingEvent, type BuildingNode, type CeilingEvent, type CeilingNode, type DoorEvent, type DoorNode, type ItemEvent, type ItemNode, type LevelEvent, type LevelNode, type RoofEvent, type RoofNode, type RoofSegmentEvent, type RoofSegmentNode, type SiteEvent, type SiteNode, type SlabEvent, type SlabNode, type StairEvent, type StairNode, type StairSegmentEvent, type StairSegmentNode, type WallEvent, type WallNode, type WindowEvent, type WindowNode, type ZoneEvent, type ZoneNode } from '@pascal-app/core';
1
+ import { type BuildingEvent, type BuildingNode, type CeilingEvent, type CeilingNode, type DoorEvent, type DoorNode, type FenceEvent, type FenceNode, type ItemEvent, type ItemNode, type LevelEvent, type LevelNode, type RoofEvent, type RoofNode, type RoofSegmentEvent, type RoofSegmentNode, type SiteEvent, type SiteNode, type SlabEvent, type SlabNode, type StairEvent, type StairNode, type StairSegmentEvent, type StairSegmentNode, type WallEvent, type WallNode, type WindowEvent, type WindowNode, type ZoneEvent, type ZoneNode } from '@pascal-app/core';
2
2
  import type { ThreeEvent } from '@react-three/fiber';
3
3
  type NodeConfig = {
4
4
  site: {
@@ -13,6 +13,10 @@ type NodeConfig = {
13
13
  node: WallNode;
14
14
  event: WallEvent;
15
15
  };
16
+ fence: {
17
+ node: FenceNode;
18
+ event: FenceEvent;
19
+ };
16
20
  building: {
17
21
  node: BuildingNode;
18
22
  event: BuildingEvent;
@@ -1 +1 @@
1
- {"version":3,"file":"use-node-events.d.ts","sourceRoot":"","sources":["../../src/hooks/use-node-events.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,QAAQ,EAGb,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,QAAQ,EACd,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAGpD,KAAK,UAAU,GAAG;IAChB,IAAI,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,KAAK,EAAE,SAAS,CAAA;KAAE,CAAA;IAC1C,IAAI,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,KAAK,EAAE,SAAS,CAAA;KAAE,CAAA;IAC1C,IAAI,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,KAAK,EAAE,SAAS,CAAA;KAAE,CAAA;IAC1C,QAAQ,EAAE;QAAE,IAAI,EAAE,YAAY,CAAC;QAAC,KAAK,EAAE,aAAa,CAAA;KAAE,CAAA;IACtD,KAAK,EAAE;QAAE,IAAI,EAAE,SAAS,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAE,CAAA;IAC7C,IAAI,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,KAAK,EAAE,SAAS,CAAA;KAAE,CAAA;IAC1C,IAAI,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,KAAK,EAAE,SAAS,CAAA;KAAE,CAAA;IAC1C,OAAO,EAAE;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,KAAK,EAAE,YAAY,CAAA;KAAE,CAAA;IACnD,IAAI,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,KAAK,EAAE,SAAS,CAAA;KAAE,CAAA;IAC1C,cAAc,EAAE;QAAE,IAAI,EAAE,eAAe,CAAC;QAAC,KAAK,EAAE,gBAAgB,CAAA;KAAE,CAAA;IAClE,KAAK,EAAE;QAAE,IAAI,EAAE,SAAS,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAE,CAAA;IAC7C,eAAe,EAAE;QAAE,IAAI,EAAE,gBAAgB,CAAC;QAAC,KAAK,EAAE,iBAAiB,CAAA;KAAE,CAAA;IACrE,MAAM,EAAE;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,KAAK,EAAE,WAAW,CAAA;KAAE,CAAA;IAChD,IAAI,EAAE;QAAE,IAAI,EAAE,QAAQ,CAAC;QAAC,KAAK,EAAE,SAAS,CAAA;KAAE,CAAA;CAC3C,CAAA;AAED,KAAK,QAAQ,GAAG,MAAM,UAAU,CAAA;AAEhC,wBAAgB,aAAa,CAAC,CAAC,SAAS,QAAQ,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC;uBAiB/D,UAAU,CAAC,YAAY,CAAC;qBAK1B,UAAU,CAAC,YAAY,CAAC;iBAQ5B,UAAU,CAAC,YAAY,CAAC;wBAIjB,UAAU,CAAC,YAAY,CAAC;wBAIxB,UAAU,CAAC,YAAY,CAAC;uBAIzB,UAAU,CAAC,YAAY,CAAC;uBAIxB,UAAU,CAAC,YAAY,CAAC;uBAIxB,UAAU,CAAC,YAAY,CAAC;EAK9C"}
1
+ {"version":3,"file":"use-node-events.d.ts","sourceRoot":"","sources":["../../src/hooks/use-node-events.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,8 @@ export function useNodeEvents(node, type) {
9
9
  position: [e.point.x, e.point.y, e.point.z],
10
10
  localPosition: [localPoint.x, localPoint.y, localPoint.z],
11
11
  normal: e.face ? [e.face.normal.x, e.face.normal.y, e.face.normal.z] : undefined,
12
+ faceIndex: e.faceIndex ?? undefined,
13
+ object: e.object,
12
14
  stopPropagation: () => e.stopPropagation(),
13
15
  nativeEvent: e,
14
16
  };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { default as Viewer } from './components/viewer';
2
+ export { SSGI_PARAMS } from './components/viewer/post-processing';
2
3
  export { WalkthroughControls } from './components/viewer/walkthrough-controls';
3
4
  export { ASSETS_CDN_URL, resolveAssetUrl, resolveCdnUrl } from './lib/asset-url';
4
5
  export { SCENE_LAYER, ZONE_LAYER } from './lib/layers';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAA;AAC9E,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAChF,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACtD,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,cAAc,EACd,wBAAwB,EACxB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,uBAAuB,EACvB,eAAe,GAChB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AACzD,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAA;AAC5E,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,qCAAqC,CAAA;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,0CAA0C,CAAA;AAC9E,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAChF,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACtD,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,cAAc,EACd,wBAAwB,EACxB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,uBAAuB,EACvB,eAAe,GAChB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AACzD,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAA;AAC5E,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAA"}
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { default as Viewer } from './components/viewer';
2
+ export { SSGI_PARAMS } from './components/viewer/post-processing';
2
3
  export { WalkthroughControls } from './components/viewer/walkthrough-controls';
3
4
  export { ASSETS_CDN_URL, resolveAssetUrl, resolveCdnUrl } from './lib/asset-url';
4
5
  export { SCENE_LAYER, ZONE_LAYER } from './lib/layers';
@@ -1,5 +1,8 @@
1
- import { type MaterialSchema } from '@pascal-app/core';
1
+ import { type MaterialPresetPayload, type MaterialSchema } from '@pascal-app/core';
2
2
  import * as THREE from 'three';
3
+ export declare function applyMaterialPresetToMaterials(materialInput: THREE.Material | THREE.Material[], preset: MaterialPresetPayload | null | undefined): void;
4
+ export declare function createMaterialFromPreset(preset: MaterialPresetPayload): THREE.MeshStandardMaterial;
5
+ export declare function createMaterialFromPresetRef(materialPreset?: string): THREE.MeshStandardMaterial | null;
3
6
  export declare function createMaterial(material?: MaterialSchema): THREE.MeshStandardMaterial;
4
7
  export declare function createDefaultMaterial(color?: string, roughness?: number): THREE.MeshStandardMaterial;
5
8
  export declare const DEFAULT_WALL_MATERIAL: THREE.MeshStandardMaterial;
@@ -1 +1 @@
1
- {"version":3,"file":"materials.d.ts","sourceRoot":"","sources":["../../src/lib/materials.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,KAAK,cAAc,EAAmB,MAAM,kBAAkB,CAAA;AAChG,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAc9B,wBAAgB,cAAc,CAAC,QAAQ,CAAC,EAAE,cAAc,GAAG,KAAK,CAAC,oBAAoB,CAmBpF;AAED,wBAAgB,qBAAqB,CACnC,KAAK,SAAY,EACjB,SAAS,SAAM,GACd,KAAK,CAAC,oBAAoB,CAO5B;AAED,eAAO,MAAM,qBAAqB,4BAAwC,CAAA;AAC1E,eAAO,MAAM,qBAAqB,4BAAwC,CAAA;AAC1E,eAAO,MAAM,qBAAqB,4BAAwC,CAAA;AAC1E,eAAO,MAAM,uBAAuB,4BAOlC,CAAA;AACF,eAAO,MAAM,wBAAwB,4BAAyC,CAAA;AAC9E,eAAO,MAAM,qBAAqB,4BAAyC,CAAA;AAC3E,eAAO,MAAM,sBAAsB,4BAAwC,CAAA;AAE3E,wBAAgB,eAAe,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,GAAG,IAAI,CAE9D;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAKzC"}
1
+ {"version":3,"file":"materials.d.ts","sourceRoot":"","sources":["../../src/lib/materials.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,qBAAqB,EAE1B,KAAK,cAAc,EAGpB,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AA2M9B,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,CAAC,MAAM,EAAE,qBAAqB,GAAG,KAAK,CAAC,oBAAoB,CAWlG;AAED,wBAAgB,2BAA2B,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,oBAAoB,GAAG,IAAI,CAItG;AAED,wBAAgB,cAAc,CAAC,QAAQ,CAAC,EAAE,cAAc,GAAG,KAAK,CAAC,oBAAoB,CAsBpF;AAED,wBAAgB,qBAAqB,CACnC,KAAK,SAAY,EACjB,SAAS,SAAM,GACd,KAAK,CAAC,oBAAoB,CAO5B;AAED,eAAO,MAAM,qBAAqB,4BAAwC,CAAA;AAC1E,eAAO,MAAM,qBAAqB,4BAAwC,CAAA;AAC1E,eAAO,MAAM,qBAAqB,4BAAwC,CAAA;AAC1E,eAAO,MAAM,uBAAuB,4BAOlC,CAAA;AACF,eAAO,MAAM,wBAAwB,4BAAyC,CAAA;AAC9E,eAAO,MAAM,qBAAqB,4BAAyC,CAAA;AAC3E,eAAO,MAAM,sBAAsB,4BAAwC,CAAA;AAE3E,wBAAgB,eAAe,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,GAAG,IAAI,CAE9D;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAWzC"}