@pascal-app/viewer 0.5.1 → 0.7.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 (121) hide show
  1. package/dist/components/error-boundary.d.ts +7 -3
  2. package/dist/components/error-boundary.d.ts.map +1 -1
  3. package/dist/components/error-boundary.js +3 -1
  4. package/dist/components/renderers/ceiling/ceiling-renderer.d.ts.map +1 -1
  5. package/dist/components/renderers/ceiling/ceiling-renderer.js +21 -4
  6. package/dist/components/renderers/column/column-renderer.d.ts +5 -0
  7. package/dist/components/renderers/column/column-renderer.d.ts.map +1 -0
  8. package/dist/components/renderers/column/column-renderer.js +594 -0
  9. package/dist/components/renderers/door/door-renderer.d.ts.map +1 -1
  10. package/dist/components/renderers/door/door-renderer.js +8 -10
  11. package/dist/components/renderers/fence/fence-renderer.d.ts.map +1 -1
  12. package/dist/components/renderers/fence/fence-renderer.js +16 -2
  13. package/dist/components/renderers/guide/guide-renderer.js +1 -1
  14. package/dist/components/renderers/item/item-renderer.d.ts.map +1 -1
  15. package/dist/components/renderers/item/item-renderer.js +2 -1
  16. package/dist/components/renderers/node-renderer.d.ts.map +1 -1
  17. package/dist/components/renderers/node-renderer.js +3 -1
  18. package/dist/components/renderers/roof/roof-renderer.d.ts.map +1 -1
  19. package/dist/components/renderers/roof/roof-renderer.js +19 -9
  20. package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts.map +1 -1
  21. package/dist/components/renderers/roof-segment/roof-segment-renderer.js +27 -8
  22. package/dist/components/renderers/site/site-renderer.d.ts.map +1 -1
  23. package/dist/components/renderers/site/site-renderer.js +7 -1
  24. package/dist/components/renderers/slab/slab-renderer.d.ts.map +1 -1
  25. package/dist/components/renderers/slab/slab-renderer.js +48 -7
  26. package/dist/components/renderers/spawn/spawn-renderer.d.ts +5 -0
  27. package/dist/components/renderers/spawn/spawn-renderer.d.ts.map +1 -0
  28. package/dist/components/renderers/spawn/spawn-renderer.js +32 -0
  29. package/dist/components/renderers/stair/stair-renderer.d.ts.map +1 -1
  30. package/dist/components/renderers/stair/stair-renderer.js +154 -39
  31. package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts.map +1 -1
  32. package/dist/components/renderers/stair-segment/stair-segment-renderer.js +22 -10
  33. package/dist/components/renderers/wall/wall-renderer.d.ts.map +1 -1
  34. package/dist/components/renderers/wall/wall-renderer.js +4 -9
  35. package/dist/components/renderers/window/window-renderer.d.ts.map +1 -1
  36. package/dist/components/renderers/window/window-renderer.js +5 -2
  37. package/dist/components/viewer/frame-limiter.d.ts +6 -0
  38. package/dist/components/viewer/frame-limiter.d.ts.map +1 -0
  39. package/dist/components/viewer/frame-limiter.js +31 -0
  40. package/dist/components/viewer/ground-occluder.d.ts.map +1 -1
  41. package/dist/components/viewer/ground-occluder.js +5 -1
  42. package/dist/components/viewer/index.d.ts +2 -0
  43. package/dist/components/viewer/index.d.ts.map +1 -1
  44. package/dist/components/viewer/index.js +90 -20
  45. package/dist/components/viewer/post-processing.d.ts +13 -1
  46. package/dist/components/viewer/post-processing.d.ts.map +1 -1
  47. package/dist/components/viewer/post-processing.js +98 -44
  48. package/dist/components/viewer/selection-manager.d.ts.map +1 -1
  49. package/dist/components/viewer/selection-manager.js +31 -3
  50. package/dist/hooks/use-node-events.d.ts +9 -1
  51. package/dist/hooks/use-node-events.d.ts.map +1 -1
  52. package/dist/hooks/use-node-events.js +2 -0
  53. package/dist/index.d.ts +6 -2
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +5 -2
  56. package/dist/lib/materials.d.ts +7 -1
  57. package/dist/lib/materials.d.ts.map +1 -1
  58. package/dist/lib/materials.js +192 -2
  59. package/dist/lib/merged-outline-node.d.ts +2 -0
  60. package/dist/lib/merged-outline-node.d.ts.map +1 -1
  61. package/dist/lib/merged-outline-node.js +36 -9
  62. package/dist/store/use-viewer.d.ts +6 -6
  63. package/dist/store/use-viewer.d.ts.map +1 -1
  64. package/dist/store/use-viewer.js +2 -2
  65. package/dist/systems/ceiling/ceiling-system.d.ts +8 -0
  66. package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -0
  67. package/dist/systems/ceiling/ceiling-system.js +91 -0
  68. package/dist/systems/column/column-geometry.d.ts +22 -0
  69. package/dist/systems/column/column-geometry.d.ts.map +1 -0
  70. package/dist/systems/column/column-geometry.js +108 -0
  71. package/dist/systems/door/door-animation-system.d.ts +2 -0
  72. package/dist/systems/door/door-animation-system.d.ts.map +1 -0
  73. package/dist/systems/door/door-animation-system.js +53 -0
  74. package/dist/systems/door/door-system.d.ts +2 -0
  75. package/dist/systems/door/door-system.d.ts.map +1 -0
  76. package/dist/systems/door/door-system.js +867 -0
  77. package/dist/systems/fence/fence-system.d.ts +2 -0
  78. package/dist/systems/fence/fence-system.d.ts.map +1 -0
  79. package/dist/systems/fence/fence-system.js +185 -0
  80. package/dist/systems/item/item-system.d.ts +2 -0
  81. package/dist/systems/item/item-system.d.ts.map +1 -0
  82. package/dist/systems/item/item-system.js +44 -0
  83. package/dist/systems/item-mesh-metadata/compute-item-mesh-metadata.d.ts +14 -0
  84. package/dist/systems/item-mesh-metadata/compute-item-mesh-metadata.d.ts.map +1 -0
  85. package/dist/systems/item-mesh-metadata/compute-item-mesh-metadata.js +155 -0
  86. package/dist/systems/item-mesh-metadata/item-mesh-metadata-system.d.ts +6 -0
  87. package/dist/systems/item-mesh-metadata/item-mesh-metadata-system.d.ts.map +1 -0
  88. package/dist/systems/item-mesh-metadata/item-mesh-metadata-system.js +75 -0
  89. package/dist/systems/item-mesh-metadata/sync-request.d.ts +7 -0
  90. package/dist/systems/item-mesh-metadata/sync-request.d.ts.map +1 -0
  91. package/dist/systems/item-mesh-metadata/sync-request.js +25 -0
  92. package/dist/systems/roof/roof-materials.d.ts +5 -0
  93. package/dist/systems/roof/roof-materials.d.ts.map +1 -0
  94. package/dist/systems/roof/roof-materials.js +46 -0
  95. package/dist/systems/roof/roof-system.d.ts +16 -0
  96. package/dist/systems/roof/roof-system.d.ts.map +1 -0
  97. package/dist/systems/roof/roof-system.js +797 -0
  98. package/dist/systems/slab/slab-system.d.ts +8 -0
  99. package/dist/systems/slab/slab-system.d.ts.map +1 -0
  100. package/dist/systems/slab/slab-system.js +155 -0
  101. package/dist/systems/stair/stair-materials.d.ts +7 -0
  102. package/dist/systems/stair/stair-materials.d.ts.map +1 -0
  103. package/dist/systems/stair/stair-materials.js +56 -0
  104. package/dist/systems/stair/stair-system.d.ts +2 -0
  105. package/dist/systems/stair/stair-system.d.ts.map +1 -0
  106. package/dist/systems/stair/stair-system.js +772 -0
  107. package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
  108. package/dist/systems/wall/wall-cutout.js +4 -123
  109. package/dist/systems/wall/wall-materials.d.ts +16 -0
  110. package/dist/systems/wall/wall-materials.d.ts.map +1 -0
  111. package/dist/systems/wall/wall-materials.js +148 -0
  112. package/dist/systems/wall/wall-system.d.ts +11 -0
  113. package/dist/systems/wall/wall-system.d.ts.map +1 -0
  114. package/dist/systems/wall/wall-system.js +592 -0
  115. package/dist/systems/window/window-animation-system.d.ts +2 -0
  116. package/dist/systems/window/window-animation-system.d.ts.map +1 -0
  117. package/dist/systems/window/window-animation-system.js +135 -0
  118. package/dist/systems/window/window-system.d.ts +12 -0
  119. package/dist/systems/window/window-system.d.ts.map +1 -0
  120. package/dist/systems/window/window-system.js +1430 -0
  121. package/package.json +6 -4
@@ -2,11 +2,25 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useRegistry, useScene } from '@pascal-app/core';
3
3
  import { useLayoutEffect, useMemo, useRef } from 'react';
4
4
  import { useNodeEvents } from '../../../hooks/use-node-events';
5
- import { DEFAULT_STAIR_MATERIAL } from '../../../lib/materials';
5
+ import { createMaterial, createMaterialFromPresetRef, DEFAULT_STAIR_MATERIAL, } from '../../../lib/materials';
6
6
  export const FenceRenderer = ({ node }) => {
7
7
  const ref = useRef(null);
8
8
  const handlers = useNodeEvents(node, 'fence');
9
- const material = useMemo(() => DEFAULT_STAIR_MATERIAL, []);
9
+ const material = useMemo(() => {
10
+ const presetMaterial = createMaterialFromPresetRef(node.materialPreset);
11
+ if (presetMaterial)
12
+ return presetMaterial;
13
+ const mat = node.material;
14
+ if (!mat)
15
+ return DEFAULT_STAIR_MATERIAL;
16
+ return createMaterial(mat);
17
+ }, [
18
+ node.materialPreset,
19
+ node.material,
20
+ node.material?.preset,
21
+ node.material?.properties,
22
+ node.material?.texture,
23
+ ]);
10
24
  useRegistry(node.id, 'fence', ref);
11
25
  useLayoutEffect(() => {
12
26
  useScene.getState().markDirty(node.id);
@@ -12,7 +12,7 @@ export const GuideRenderer = ({ node }) => {
12
12
  const ref = useRef(null);
13
13
  useRegistry(node.id, 'guide', ref);
14
14
  const resolvedUrl = useAssetUrl(node.url);
15
- return (_jsx("group", { position: node.position, ref: ref, rotation: [0, node.rotation[1], 0], visible: showGuides, children: resolvedUrl && (_jsx(Suspense, { children: _jsx(GuidePlane, { opacity: node.opacity, scale: node.scale, url: resolvedUrl }) })) }));
15
+ return (_jsx("group", { position: node.position, ref: ref, rotation: [0, node.rotation[1], 0], visible: showGuides && node.visible !== false, children: resolvedUrl && (_jsx(Suspense, { children: _jsx(GuidePlane, { opacity: node.opacity, scale: node.scale, url: resolvedUrl }) })) }));
16
16
  };
17
17
  const GuidePlane = ({ url, scale, opacity }) => {
18
18
  const tex = useLoader(TextureLoader, url);
@@ -1 +1 @@
1
- {"version":3,"file":"item-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/item/item-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,QAAQ,EAKd,MAAM,kBAAkB,CAAA;AAkCzB,eAAO,MAAM,YAAY,GAAI,UAAU;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,4CAiBxD,CAAA"}
1
+ {"version":3,"file":"item-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/item/item-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,QAAQ,EAKd,MAAM,kBAAkB,CAAA;AAmCzB,eAAO,MAAM,YAAY,GAAI,UAAU;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,4CAiBxD,CAAA"}
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { baseMaterial, glassMaterial, useInteractive, useRegistry, useScene, } from '@pascal-app/core';
2
+ import { useInteractive, useRegistry, useScene, } from '@pascal-app/core';
3
3
  import { useAnimations } from '@react-three/drei';
4
4
  import { Clone } from '@react-three/drei/core/Clone';
5
5
  import { useGLTF } from '@react-three/drei/core/Gltf';
@@ -10,6 +10,7 @@ import { positionLocal, smoothstep, time } from 'three/tsl';
10
10
  import { MeshStandardNodeMaterial } from 'three/webgpu';
11
11
  import { useNodeEvents } from '../../../hooks/use-node-events';
12
12
  import { resolveCdnUrl } from '../../../lib/asset-url';
13
+ import { baseMaterial, glassMaterial } from '../../../lib/materials';
13
14
  import { useItemLightPool } from '../../../store/use-item-light-pool';
14
15
  import { ErrorBoundary } from '../../error-boundary';
15
16
  import { NodeRenderer } from '../node-renderer';
@@ -1 +1 @@
1
- {"version":3,"file":"node-renderer.d.ts","sourceRoot":"","sources":["../../../src/components/renderers/node-renderer.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,OAAO,EAAY,MAAM,kBAAkB,CAAA;AAmBzD,eAAO,MAAM,YAAY,GAAI,YAAY;IAAE,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,mDA0BjE,CAAA"}
1
+ {"version":3,"file":"node-renderer.d.ts","sourceRoot":"","sources":["../../../src/components/renderers/node-renderer.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,OAAO,EAAY,MAAM,kBAAkB,CAAA;AAqBzD,eAAO,MAAM,YAAY,GAAI,YAAY;IAAE,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,mDA4BjE,CAAA"}
@@ -3,6 +3,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
3
3
  import { useScene } from '@pascal-app/core';
4
4
  import { BuildingRenderer } from './building/building-renderer';
5
5
  import { CeilingRenderer } from './ceiling/ceiling-renderer';
6
+ import { ColumnRenderer } from './column/column-renderer';
6
7
  import { DoorRenderer } from './door/door-renderer';
7
8
  import { FenceRenderer } from './fence/fence-renderer';
8
9
  import { GuideRenderer } from './guide/guide-renderer';
@@ -13,6 +14,7 @@ import { RoofSegmentRenderer } from './roof-segment/roof-segment-renderer';
13
14
  import { ScanRenderer } from './scan/scan-renderer';
14
15
  import { SiteRenderer } from './site/site-renderer';
15
16
  import { SlabRenderer } from './slab/slab-renderer';
17
+ import { SpawnRenderer } from './spawn/spawn-renderer';
16
18
  import { StairRenderer } from './stair/stair-renderer';
17
19
  import { StairSegmentRenderer } from './stair-segment/stair-segment-renderer';
18
20
  import { WallRenderer } from './wall/wall-renderer';
@@ -22,5 +24,5 @@ export const NodeRenderer = ({ nodeId }) => {
22
24
  const node = useScene((state) => state.nodes[nodeId]);
23
25
  if (!node)
24
26
  return null;
25
- return (_jsxs(_Fragment, { children: [node.type === 'site' && _jsx(SiteRenderer, { node: node }), node.type === 'building' && _jsx(BuildingRenderer, { node: node }), node.type === 'ceiling' && _jsx(CeilingRenderer, { node: node }), node.type === 'level' && _jsx(LevelRenderer, { node: node }), node.type === 'item' && _jsx(ItemRenderer, { node: node }), node.type === 'slab' && _jsx(SlabRenderer, { node: node }), node.type === 'wall' && _jsx(WallRenderer, { node: node }), node.type === 'fence' && _jsx(FenceRenderer, { node: node }), node.type === 'door' && _jsx(DoorRenderer, { node: node }), node.type === 'window' && _jsx(WindowRenderer, { node: node }), node.type === 'zone' && _jsx(ZoneRenderer, { node: node }), node.type === 'roof' && _jsx(RoofRenderer, { node: node }), node.type === 'roof-segment' && _jsx(RoofSegmentRenderer, { node: node }), node.type === 'stair' && _jsx(StairRenderer, { node: node }), node.type === 'stair-segment' && _jsx(StairSegmentRenderer, { node: node }), node.type === 'scan' && _jsx(ScanRenderer, { node: node }), node.type === 'guide' && _jsx(GuideRenderer, { node: node })] }));
27
+ return (_jsxs(_Fragment, { children: [node.type === 'site' && _jsx(SiteRenderer, { node: node }), node.type === 'building' && _jsx(BuildingRenderer, { node: node }), node.type === 'ceiling' && _jsx(CeilingRenderer, { node: node }), node.type === 'column' && _jsx(ColumnRenderer, { node: node }), node.type === 'level' && _jsx(LevelRenderer, { node: node }), node.type === 'item' && _jsx(ItemRenderer, { node: node }), node.type === 'slab' && _jsx(SlabRenderer, { node: node }), node.type === 'spawn' && _jsx(SpawnRenderer, { node: node }), node.type === 'wall' && _jsx(WallRenderer, { node: node }), node.type === 'fence' && _jsx(FenceRenderer, { node: node }), node.type === 'door' && _jsx(DoorRenderer, { node: node }), node.type === 'window' && _jsx(WindowRenderer, { node: node }), node.type === 'zone' && _jsx(ZoneRenderer, { node: node }), node.type === 'roof' && _jsx(RoofRenderer, { node: node }), node.type === 'roof-segment' && _jsx(RoofSegmentRenderer, { node: node }), node.type === 'stair' && _jsx(StairRenderer, { node: node }), node.type === 'stair-segment' && _jsx(StairSegmentRenderer, { node: node }), node.type === 'scan' && _jsx(ScanRenderer, { node: node }), node.type === 'guide' && _jsx(GuideRenderer, { node: node })] }));
26
28
  };
@@ -1 +1 @@
1
- {"version":3,"file":"roof-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/roof/roof-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAe,MAAM,kBAAkB,CAAA;AAS7D,eAAO,MAAM,YAAY,GAAI,UAAU;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,4CAkCxD,CAAA"}
1
+ {"version":3,"file":"roof-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/roof/roof-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAe,MAAM,kBAAkB,CAAA;AAS7D,eAAO,MAAM,YAAY,GAAI,UAAU;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,4CAiDxD,CAAA"}
@@ -1,9 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useRegistry } from '@pascal-app/core';
3
- import { useMemo, useRef } from 'react';
3
+ import { useEffect, useMemo, useRef } from 'react';
4
+ import * as THREE from 'three';
4
5
  import { useNodeEvents } from '../../../hooks/use-node-events';
5
- import { createMaterial } from '../../../lib/materials';
6
6
  import useViewer from '../../../store/use-viewer';
7
+ import { getRoofMaterialArray } from '../../../systems/roof/roof-materials';
7
8
  import { NodeRenderer } from '../node-renderer';
8
9
  import { roofDebugMaterials, roofMaterials } from './roof-materials';
9
10
  export const RoofRenderer = ({ node }) => {
@@ -11,12 +12,21 @@ export const RoofRenderer = ({ node }) => {
11
12
  useRegistry(node.id, 'roof', ref);
12
13
  const handlers = useNodeEvents(node, 'roof');
13
14
  const debugColors = useViewer((s) => s.debugColors);
14
- const customMaterial = useMemo(() => {
15
- const mat = node.material;
16
- if (!mat)
17
- return null;
18
- return createMaterial(mat);
19
- }, [node.material, node.material?.preset, node.material?.properties, node.material?.texture]);
15
+ const placeholderGeometry = useMemo(() => {
16
+ const geometry = new THREE.BufferGeometry();
17
+ geometry.setAttribute('position', new THREE.Float32BufferAttribute([], 3));
18
+ geometry.addGroup(0, 0, 0);
19
+ geometry.addGroup(0, 0, 1);
20
+ geometry.addGroup(0, 0, 2);
21
+ geometry.addGroup(0, 0, 3);
22
+ return geometry;
23
+ }, []);
24
+ const customMaterial = useMemo(() => getRoofMaterialArray(node), [node]);
20
25
  const material = debugColors ? roofDebugMaterials : customMaterial || roofMaterials;
21
- return (_jsxs("group", { position: node.position, ref: ref, "rotation-y": node.rotation, visible: node.visible, ...handlers, children: [_jsx("mesh", { castShadow: true, material: material, name: "merged-roof", receiveShadow: true, children: _jsx("boxGeometry", { args: [0, 0, 0] }) }), _jsx("group", { name: "segments-wrapper", visible: false, children: (node.children ?? []).map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId))) })] }));
26
+ useEffect(() => {
27
+ return () => {
28
+ placeholderGeometry.dispose();
29
+ };
30
+ }, [placeholderGeometry]);
31
+ return (_jsxs("group", { position: node.position, ref: ref, "rotation-y": node.rotation, visible: node.visible, ...handlers, children: [_jsx("mesh", { castShadow: true, geometry: placeholderGeometry, material: material, name: "merged-roof", receiveShadow: true }), _jsx("group", { name: "segments-wrapper", visible: false, children: (node.children ?? []).map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId))) })] }));
22
32
  };
@@ -1 +1 @@
1
- {"version":3,"file":"roof-segment-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/roof-segment/roof-segment-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAe,MAAM,kBAAkB,CAAA;AAQpE,eAAO,MAAM,mBAAmB,GAAI,UAAU;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,4CA6BtE,CAAA"}
1
+ {"version":3,"file":"roof-segment-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/roof-segment/roof-segment-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,eAAe,EAGrB,MAAM,kBAAkB,CAAA;AAQzB,eAAO,MAAM,mBAAmB,GAAI,UAAU;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,4CAgDtE,CAAA"}
@@ -1,21 +1,40 @@
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 { useEffect, useMemo, useRef } from 'react';
4
+ import * as THREE from 'three';
4
5
  import { useNodeEvents } from '../../../hooks/use-node-events';
5
- import { createMaterial } from '../../../lib/materials';
6
6
  import useViewer from '../../../store/use-viewer';
7
+ import { getRoofMaterialArray } from '../../../systems/roof/roof-materials';
7
8
  import { roofDebugMaterials, roofMaterials } from '../roof/roof-materials';
8
9
  export const RoofSegmentRenderer = ({ node }) => {
9
10
  const ref = useRef(null);
11
+ const nodes = useScene((state) => state.nodes);
10
12
  useRegistry(node.id, 'roof-segment', ref);
11
13
  const handlers = useNodeEvents(node, 'roof-segment');
12
14
  const debugColors = useViewer((s) => s.debugColors);
15
+ const parentNode = node.parentId
16
+ ? nodes[node.parentId]
17
+ : undefined;
18
+ const placeholderGeometry = useMemo(() => {
19
+ const geometry = new THREE.BufferGeometry();
20
+ geometry.setAttribute('position', new THREE.Float32BufferAttribute([], 3));
21
+ geometry.addGroup(0, 0, 0);
22
+ geometry.addGroup(0, 0, 1);
23
+ geometry.addGroup(0, 0, 2);
24
+ geometry.addGroup(0, 0, 3);
25
+ return geometry;
26
+ }, []);
13
27
  const customMaterial = useMemo(() => {
14
- const mat = node.material;
15
- if (!mat)
28
+ if (node.material !== undefined || typeof node.materialPreset === 'string') {
16
29
  return null;
17
- return createMaterial(mat);
18
- }, [node.material, node.material?.preset, node.material?.properties, node.material?.texture]);
30
+ }
31
+ return parentNode ? getRoofMaterialArray(parentNode) : null;
32
+ }, [node, parentNode]);
19
33
  const material = debugColors ? roofDebugMaterials : customMaterial || roofMaterials;
20
- 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] }) }));
34
+ useEffect(() => {
35
+ return () => {
36
+ placeholderGeometry.dispose();
37
+ };
38
+ }, [placeholderGeometry]);
39
+ return (_jsx("mesh", { geometry: placeholderGeometry, material: material, position: node.position, ref: ref, "rotation-y": node.rotation, visible: node.visible, ...handlers }));
21
40
  };
@@ -1 +1 @@
1
- {"version":3,"file":"site-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/site/site-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAwC,MAAM,kBAAkB,CAAA;AAmCtF,eAAO,MAAM,YAAY,GAAI,UAAU;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,mDAgHxD,CAAA"}
1
+ {"version":3,"file":"site-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/site/site-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAwC,MAAM,kBAAkB,CAAA;AAmCtF,eAAO,MAAM,YAAY,GAAI,UAAU;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,mDAyHxD,CAAA"}
@@ -43,7 +43,13 @@ export const SiteRenderer = ({ node }) => {
43
43
  lowestLevelIndex = Math.min(lowestLevelIndex, n.level);
44
44
  });
45
45
  const next = nodeList
46
- .filter((n) => n.type === 'slab' && n.visible && n.polygon.length >= 3)
46
+ .filter((n) => n.type === 'slab' &&
47
+ n.visible &&
48
+ n.polygon.length >= 3 &&
49
+ // Only recessed slabs should punch through the site ground.
50
+ // Positive slabs are real floor geometry and should not create a
51
+ // ghost footprint in the background ground fill.
52
+ (n.elevation ?? 0.05) < 0)
47
53
  .filter((n) => {
48
54
  if (!Number.isFinite(lowestLevelIndex))
49
55
  return true;
@@ -1 +1 @@
1
- {"version":3,"file":"slab-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/slab/slab-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAe,MAAM,kBAAkB,CAAA;AAM7D,eAAO,MAAM,YAAY,GAAI,UAAU;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,4CAyBxD,CAAA"}
1
+ {"version":3,"file":"slab-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/slab/slab-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,QAAQ,EAAe,MAAM,kBAAkB,CAAA;AA+CrF,eAAO,MAAM,YAAY,GAAI,UAAU;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,4CAuCxD,CAAA"}
@@ -1,17 +1,58 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useRegistry } from '@pascal-app/core';
2
+ import { getMaterialPresetByRef, useRegistry } from '@pascal-app/core';
3
3
  import { useMemo, useRef } from 'react';
4
+ import * as THREE from 'three';
4
5
  import { useNodeEvents } from '../../../hooks/use-node-events';
5
- import { createMaterial, DEFAULT_SLAB_MATERIAL } from '../../../lib/materials';
6
+ import { applyMaterialPresetToMaterials, createMaterial, DEFAULT_SLAB_MATERIAL, } from '../../../lib/materials';
7
+ const slabMaterialCache = new Map();
8
+ function getSlabMaterial(cacheKey, params) {
9
+ const cached = slabMaterialCache.get(cacheKey);
10
+ if (cached)
11
+ return cached;
12
+ const preset = getMaterialPresetByRef(params.materialPreset);
13
+ const slabMaterial = preset
14
+ ? new THREE.MeshStandardMaterial()
15
+ : params.material
16
+ ? createMaterial(params.material).clone()
17
+ : DEFAULT_SLAB_MATERIAL.clone();
18
+ if (preset) {
19
+ // Apply the preset to the slab-owned material so async texture loads update
20
+ // the instance we actually render after refresh as well.
21
+ applyMaterialPresetToMaterials(slabMaterial, preset);
22
+ }
23
+ // Slabs participate in the WebGPU MRT scene pass. Keeping them opaque avoids
24
+ // pipeline variants that can fail when geometry is regenerated while a
25
+ // transparent/custom material is attached.
26
+ slabMaterial.transparent = false;
27
+ slabMaterial.opacity = 1;
28
+ slabMaterial.alphaMap = null;
29
+ slabMaterial.side = THREE.DoubleSide;
30
+ slabMaterial.depthWrite = true;
31
+ slabMaterial.needsUpdate = true;
32
+ slabMaterialCache.set(cacheKey, slabMaterial);
33
+ return slabMaterial;
34
+ }
6
35
  export const SlabRenderer = ({ node }) => {
7
36
  const ref = useRef(null);
8
37
  useRegistry(node.id, 'slab', ref);
9
38
  const handlers = useNodeEvents(node, 'slab');
10
39
  const material = useMemo(() => {
11
- const mat = node.material;
12
- if (!mat)
13
- return DEFAULT_SLAB_MATERIAL;
14
- return createMaterial(mat);
15
- }, [node.material, node.material?.preset, node.material?.properties, node.material?.texture]);
40
+ const resolvedMaterial = node.material;
41
+ const resolvedMaterialPreset = node.materialPreset;
42
+ const cacheKey = JSON.stringify({
43
+ material: resolvedMaterial ?? null,
44
+ materialPreset: resolvedMaterialPreset ?? null,
45
+ });
46
+ return getSlabMaterial(cacheKey, {
47
+ material: resolvedMaterial,
48
+ materialPreset: resolvedMaterialPreset,
49
+ });
50
+ }, [
51
+ node.material,
52
+ node.material?.preset,
53
+ node.material?.properties,
54
+ node.material?.texture,
55
+ node.materialPreset,
56
+ ]);
16
57
  return (_jsx("mesh", { castShadow: true, receiveShadow: true, ref: ref, ...handlers, material: material, visible: node.visible, children: _jsx("boxGeometry", { args: [0, 0, 0] }) }));
17
58
  };
@@ -0,0 +1,5 @@
1
+ import { type SpawnNode } from '@pascal-app/core';
2
+ export declare const SpawnRenderer: ({ node }: {
3
+ node: SpawnNode;
4
+ }) => import("react/jsx-runtime").JSX.Element;
5
+ //# sourceMappingURL=spawn-renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/spawn/spawn-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAkC,MAAM,kBAAkB,CAAA;AASjF,eAAO,MAAM,aAAa,GAAI,UAAU;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,4CA0D1D,CAAA"}
@@ -0,0 +1,32 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useLiveTransforms, useRegistry } from '@pascal-app/core';
3
+ import { useMemo, useRef } from 'react';
4
+ import { Color, Shape } from 'three';
5
+ import { useNodeEvents } from '../../../hooks/use-node-events';
6
+ import useViewer from '../../../store/use-viewer';
7
+ const SPAWN_COLOR = new Color('#22c55e');
8
+ export const SpawnRenderer = ({ node }) => {
9
+ const ref = useRef(null);
10
+ const handlers = useNodeEvents(node, 'spawn');
11
+ const liveTransform = useLiveTransforms((state) => state.get(node.id));
12
+ const walkthroughMode = useViewer((state) => state.walkthroughMode);
13
+ useRegistry(node.id, 'spawn', ref);
14
+ const materialProps = useMemo(() => ({
15
+ color: SPAWN_COLOR,
16
+ emissive: SPAWN_COLOR,
17
+ emissiveIntensity: 0.08,
18
+ metalness: 0.03,
19
+ roughness: 0.42,
20
+ }), []);
21
+ const arrowShape = useMemo(() => {
22
+ const shape = new Shape();
23
+ // Positive local Y becomes negative world Z after the -90deg X rotation below,
24
+ // so this tip points "forward" for the player/spawn direction.
25
+ shape.moveTo(0, 0.24);
26
+ shape.lineTo(-0.18, -0.14);
27
+ shape.lineTo(0.18, -0.14);
28
+ shape.closePath();
29
+ return shape;
30
+ }, []);
31
+ return (_jsxs("group", { position: liveTransform?.position ?? node.position, ref: ref, rotation: [0, liveTransform?.rotation ?? node.rotation, 0], visible: !walkthroughMode, children: [_jsxs("mesh", { position: [0, 0.09, 0], rotation: [-Math.PI / 2, 0, 0], ...handlers, children: [_jsx("ringGeometry", { args: [0.34, 0.48, 48] }), _jsx("meshStandardMaterial", { ...materialProps })] }), _jsxs("mesh", { position: [0, 0.1, -0.52], rotation: [-Math.PI / 2, 0, 0], ...handlers, children: [_jsx("shapeGeometry", { args: [arrowShape] }), _jsx("meshStandardMaterial", { ...materialProps })] }), _jsxs("mesh", { position: [0, 0.41, 0], ...handlers, children: [_jsx("boxGeometry", { args: [0.3, 0.54, 0.16] }), _jsx("meshStandardMaterial", { ...materialProps })] }), _jsxs("mesh", { position: [0, 0.83, 0], ...handlers, children: [_jsx("boxGeometry", { args: [0.18, 0.18, 0.18] }), _jsx("meshStandardMaterial", { ...materialProps })] })] }));
32
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"stair-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/stair/stair-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,SAAS,EAAgD,MAAM,kBAAkB,CAAA;AAqC/G,eAAO,MAAM,aAAa,GAAI,UAAU;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,4CAwC1D,CAAA"}
1
+ {"version":3,"file":"stair-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/stair/stair-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,SAAS,EAIf,MAAM,kBAAkB,CAAA;AA8CzB,eAAO,MAAM,aAAa,GAAI,UAAU;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,4CA2E1D,CAAA"}