@pascal-app/viewer 0.7.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/renderers/node-renderer.d.ts +1 -1
- package/dist/components/renderers/node-renderer.d.ts.map +1 -1
- package/dist/components/renderers/node-renderer.js +38 -22
- package/dist/components/renderers/parametric-node-renderer.d.ts +5 -0
- package/dist/components/renderers/parametric-node-renderer.d.ts.map +1 -0
- package/dist/components/renderers/parametric-node-renderer.js +34 -0
- package/dist/components/renderers/scene-renderer.d.ts +1 -1
- package/dist/components/renderers/scene-renderer.d.ts.map +1 -1
- package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
- package/dist/components/viewer/frame-limiter.js +5 -1
- package/dist/components/viewer/ground-occluder.d.ts +1 -1
- package/dist/components/viewer/ground-occluder.d.ts.map +1 -1
- package/dist/components/viewer/ground-occluder.js +12 -24
- package/dist/components/viewer/index.d.ts +38 -1
- package/dist/components/viewer/index.d.ts.map +1 -1
- package/dist/components/viewer/index.js +183 -60
- package/dist/components/viewer/lights.d.ts +1 -1
- package/dist/components/viewer/lights.d.ts.map +1 -1
- package/dist/components/viewer/lights.js +170 -43
- package/dist/components/viewer/perf-monitor.d.ts +1 -1
- package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
- package/dist/components/viewer/perf-monitor.js +99 -11
- package/dist/components/viewer/post-processing.d.ts.map +1 -1
- package/dist/components/viewer/post-processing.js +217 -61
- package/dist/components/viewer/registered-systems.d.ts +12 -0
- package/dist/components/viewer/registered-systems.d.ts.map +1 -0
- package/dist/components/viewer/registered-systems.js +47 -0
- package/dist/components/viewer/scene-bvh.d.ts +17 -0
- package/dist/components/viewer/scene-bvh.d.ts.map +1 -0
- package/dist/components/viewer/scene-bvh.js +94 -0
- package/dist/components/viewer/selection-manager.d.ts +1 -1
- package/dist/components/viewer/selection-manager.d.ts.map +1 -1
- package/dist/components/viewer/selection-manager.js +20 -5
- package/dist/components/viewer/viewer-camera.d.ts +1 -1
- package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
- package/dist/components/viewer/walkthrough-controls.d.ts +1 -1
- package/dist/components/viewer/walkthrough-controls.d.ts.map +1 -1
- package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
- package/dist/hooks/use-node-events.d.ts +5 -73
- package/dist/hooks/use-node-events.d.ts.map +1 -1
- package/dist/hooks/use-node-events.js +19 -7
- package/dist/index.d.ts +36 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +67 -3
- package/dist/lib/csg-utils.d.ts +16 -0
- package/dist/lib/csg-utils.d.ts.map +1 -0
- package/dist/lib/csg-utils.js +33 -0
- package/dist/lib/drawable-geometry.d.ts +21 -0
- package/dist/lib/drawable-geometry.d.ts.map +1 -0
- package/dist/lib/drawable-geometry.js +22 -0
- package/dist/lib/edge-style.d.ts +3 -0
- package/dist/lib/edge-style.d.ts.map +1 -0
- package/dist/lib/edge-style.js +11 -0
- package/dist/lib/gpu-perf.d.ts +4 -0
- package/dist/lib/gpu-perf.d.ts.map +1 -0
- package/dist/lib/gpu-perf.js +23 -0
- package/dist/lib/ink-edges.d.ts +9 -0
- package/dist/lib/ink-edges.d.ts.map +1 -0
- package/dist/lib/ink-edges.js +43 -0
- package/dist/lib/isolation.d.ts +39 -0
- package/dist/lib/isolation.d.ts.map +1 -0
- package/dist/lib/isolation.js +103 -0
- package/dist/lib/layers.d.ts +18 -0
- package/dist/lib/layers.d.ts.map +1 -1
- package/dist/lib/layers.js +18 -0
- package/dist/lib/materials.d.ts +25 -15
- package/dist/lib/materials.d.ts.map +1 -1
- package/dist/lib/materials.js +277 -63
- package/dist/lib/merged-outline-node.d.ts.map +1 -1
- package/dist/lib/merged-outline-node.js +11 -11
- package/dist/lib/polygon-union.d.ts +4 -0
- package/dist/lib/polygon-union.d.ts.map +1 -0
- package/dist/lib/polygon-union.js +289 -0
- package/dist/lib/polygon-union.test.d.ts +2 -0
- package/dist/lib/polygon-union.test.d.ts.map +1 -0
- package/dist/lib/polygon-union.test.js +105 -0
- package/dist/lib/scene-themes.d.ts +29 -0
- package/dist/lib/scene-themes.d.ts.map +1 -0
- package/dist/lib/scene-themes.js +193 -0
- package/dist/store/use-viewer.d.ts +42 -4
- package/dist/store/use-viewer.d.ts.map +1 -1
- package/dist/store/use-viewer.js +26 -3
- package/dist/systems/ceiling/ceiling-system.d.ts +6 -2
- package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
- package/dist/systems/ceiling/ceiling-system.js +62 -12
- package/dist/systems/door/door-system.d.ts.map +1 -1
- package/dist/systems/door/door-system.js +373 -22
- package/dist/systems/elevator/elevator-interaction-system.d.ts +2 -0
- package/dist/systems/elevator/elevator-interaction-system.d.ts.map +1 -0
- package/dist/systems/elevator/elevator-interaction-system.js +62 -0
- package/dist/systems/fence/fence-system.d.ts +3 -0
- package/dist/systems/fence/fence-system.d.ts.map +1 -1
- package/dist/systems/fence/fence-system.js +10 -6
- package/dist/systems/floor-elevation/floor-elevation-system.d.ts +20 -0
- package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -0
- package/dist/systems/floor-elevation/floor-elevation-system.js +78 -0
- package/dist/systems/geometry/geometry-system.d.ts +34 -0
- package/dist/systems/geometry/geometry-system.d.ts.map +1 -0
- package/dist/systems/geometry/geometry-system.js +279 -0
- package/dist/systems/guide/guide-system.d.ts.map +1 -1
- package/dist/systems/guide/guide-system.js +1 -26
- package/dist/systems/interactive/interactive-system.d.ts +1 -1
- package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
- package/dist/systems/item/item-system.d.ts +7 -0
- package/dist/systems/item/item-system.d.ts.map +1 -1
- package/dist/systems/item/item-system.js +8 -11
- package/dist/systems/item-light/item-light-system.d.ts +1 -1
- package/dist/systems/item-light/item-light-system.d.ts.map +1 -1
- package/dist/systems/item-light/item-light-system.js +18 -5
- package/dist/systems/roof/roof-materials.d.ts +3 -2
- package/dist/systems/roof/roof-materials.d.ts.map +1 -1
- package/dist/systems/roof/roof-materials.js +38 -14
- package/dist/systems/roof/roof-system.d.ts +27 -0
- package/dist/systems/roof/roof-system.d.ts.map +1 -1
- package/dist/systems/roof/roof-system.js +411 -71
- package/dist/systems/scan/scan-system.d.ts.map +1 -1
- package/dist/systems/scan/scan-system.js +1 -26
- package/dist/systems/slab/slab-system.d.ts.map +1 -1
- package/dist/systems/slab/slab-system.js +167 -49
- package/dist/systems/slab/slab-system.test.d.ts +2 -0
- package/dist/systems/slab/slab-system.test.d.ts.map +1 -0
- package/dist/systems/slab/slab-system.test.js +62 -0
- package/dist/systems/stair/stair-materials.d.ts +4 -3
- package/dist/systems/stair/stair-materials.d.ts.map +1 -1
- package/dist/systems/stair/stair-materials.js +30 -15
- package/dist/systems/stair/stair-system.d.ts.map +1 -1
- package/dist/systems/stair/stair-system.js +58 -71
- package/dist/systems/surface-hole-geometry.d.ts +3 -0
- package/dist/systems/surface-hole-geometry.d.ts.map +1 -0
- package/dist/systems/surface-hole-geometry.js +4 -0
- package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout.js +18 -2
- package/dist/systems/wall/wall-materials.d.ts +4 -3
- package/dist/systems/wall/wall-materials.d.ts.map +1 -1
- package/dist/systems/wall/wall-materials.js +52 -28
- package/dist/systems/wall/wall-system.d.ts +1 -1
- package/dist/systems/wall/wall-system.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.js +179 -31
- package/dist/systems/window/window-system.d.ts.map +1 -1
- package/dist/systems/window/window-system.js +71 -7
- package/package.json +4 -5
- package/dist/components/renderers/building/building-renderer.d.ts +0 -5
- package/dist/components/renderers/building/building-renderer.d.ts.map +0 -1
- package/dist/components/renderers/building/building-renderer.js +0 -11
- package/dist/components/renderers/ceiling/ceiling-renderer.d.ts +0 -5
- package/dist/components/renderers/ceiling/ceiling-renderer.d.ts.map +0 -1
- package/dist/components/renderers/ceiling/ceiling-renderer.js +0 -58
- package/dist/components/renderers/column/column-renderer.d.ts +0 -5
- package/dist/components/renderers/column/column-renderer.d.ts.map +0 -1
- package/dist/components/renderers/column/column-renderer.js +0 -594
- package/dist/components/renderers/door/door-renderer.d.ts +0 -5
- package/dist/components/renderers/door/door-renderer.d.ts.map +0 -1
- package/dist/components/renderers/door/door-renderer.js +0 -16
- package/dist/components/renderers/fence/fence-renderer.d.ts +0 -5
- package/dist/components/renderers/fence/fence-renderer.d.ts.map +0 -1
- package/dist/components/renderers/fence/fence-renderer.js +0 -29
- package/dist/components/renderers/guide/guide-renderer.d.ts +0 -5
- package/dist/components/renderers/guide/guide-renderer.d.ts.map +0 -1
- package/dist/components/renderers/guide/guide-renderer.js +0 -38
- package/dist/components/renderers/item/item-renderer.d.ts +0 -5
- package/dist/components/renderers/item/item-renderer.d.ts.map +0 -1
- package/dist/components/renderers/item/item-renderer.js +0 -172
- package/dist/components/renderers/level/level-renderer.d.ts +0 -5
- package/dist/components/renderers/level/level-renderer.d.ts.map +0 -1
- package/dist/components/renderers/level/level-renderer.js +0 -11
- package/dist/components/renderers/roof/roof-materials.d.ts +0 -4
- package/dist/components/renderers/roof/roof-materials.d.ts.map +0 -1
- package/dist/components/renderers/roof/roof-materials.js +0 -16
- package/dist/components/renderers/roof/roof-renderer.d.ts +0 -5
- package/dist/components/renderers/roof/roof-renderer.d.ts.map +0 -1
- package/dist/components/renderers/roof/roof-renderer.js +0 -32
- package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts +0 -5
- package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts.map +0 -1
- package/dist/components/renderers/roof-segment/roof-segment-renderer.js +0 -40
- package/dist/components/renderers/scan/scan-renderer.d.ts +0 -5
- package/dist/components/renderers/scan/scan-renderer.d.ts.map +0 -1
- package/dist/components/renderers/scan/scan-renderer.js +0 -54
- package/dist/components/renderers/site/site-renderer.d.ts +0 -5
- package/dist/components/renderers/site/site-renderer.d.ts.map +0 -1
- package/dist/components/renderers/site/site-renderer.js +0 -106
- package/dist/components/renderers/slab/slab-renderer.d.ts +0 -5
- package/dist/components/renderers/slab/slab-renderer.d.ts.map +0 -1
- package/dist/components/renderers/slab/slab-renderer.js +0 -58
- package/dist/components/renderers/spawn/spawn-renderer.d.ts +0 -5
- package/dist/components/renderers/spawn/spawn-renderer.d.ts.map +0 -1
- package/dist/components/renderers/spawn/spawn-renderer.js +0 -32
- package/dist/components/renderers/stair/stair-renderer.d.ts +0 -5
- package/dist/components/renderers/stair/stair-renderer.d.ts.map +0 -1
- package/dist/components/renderers/stair/stair-renderer.js +0 -603
- package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts +0 -5
- package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts.map +0 -1
- package/dist/components/renderers/stair-segment/stair-segment-renderer.js +0 -32
- package/dist/components/renderers/wall/wall-renderer.d.ts +0 -5
- package/dist/components/renderers/wall/wall-renderer.d.ts.map +0 -1
- package/dist/components/renderers/wall/wall-renderer.js +0 -16
- package/dist/components/renderers/window/window-renderer.d.ts +0 -5
- package/dist/components/renderers/window/window-renderer.d.ts.map +0 -1
- package/dist/components/renderers/window/window-renderer.js +0 -21
- package/dist/components/renderers/zone/zone-renderer.d.ts +0 -5
- package/dist/components/renderers/zone/zone-renderer.d.ts.map +0 -1
- package/dist/components/renderers/zone/zone-renderer.js +0 -181
- package/dist/lib/suppress-three-clock-warning.d.ts +0 -25
- package/dist/lib/suppress-three-clock-warning.d.ts.map +0 -1
- package/dist/lib/suppress-three-clock-warning.js +0 -51
- package/dist/systems/export/export-system.d.ts +0 -2
- package/dist/systems/export/export-system.d.ts.map +0 -1
- package/dist/systems/export/export-system.js +0 -60
- package/dist/systems/item-mesh-metadata/compute-item-mesh-metadata.d.ts +0 -14
- package/dist/systems/item-mesh-metadata/compute-item-mesh-metadata.d.ts.map +0 -1
- package/dist/systems/item-mesh-metadata/compute-item-mesh-metadata.js +0 -155
- package/dist/systems/item-mesh-metadata/item-mesh-metadata-system.d.ts +0 -6
- package/dist/systems/item-mesh-metadata/item-mesh-metadata-system.d.ts.map +0 -1
- package/dist/systems/item-mesh-metadata/item-mesh-metadata-system.js +0 -75
- package/dist/systems/item-mesh-metadata/sync-request.d.ts +0 -7
- package/dist/systems/item-mesh-metadata/sync-request.d.ts.map +0 -1
- package/dist/systems/item-mesh-metadata/sync-request.js +0 -25
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useRegistry, useScene } from '@pascal-app/core';
|
|
3
|
-
import { useLayoutEffect, useMemo, useRef } from 'react';
|
|
4
|
-
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
5
|
-
import { createMaterial, createMaterialFromPresetRef, DEFAULT_STAIR_MATERIAL, } from '../../../lib/materials';
|
|
6
|
-
export const FenceRenderer = ({ node }) => {
|
|
7
|
-
const ref = useRef(null);
|
|
8
|
-
const handlers = useNodeEvents(node, 'fence');
|
|
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
|
-
]);
|
|
24
|
-
useRegistry(node.id, 'fence', ref);
|
|
25
|
-
useLayoutEffect(() => {
|
|
26
|
-
useScene.getState().markDirty(node.id);
|
|
27
|
-
}, [node.id]);
|
|
28
|
-
return (_jsx("mesh", { castShadow: true, material: material, receiveShadow: true, ref: ref, visible: node.visible, ...handlers, children: _jsx("boxGeometry", { args: [0, 0, 0] }) }));
|
|
29
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"guide-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/guide/guide-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAe,MAAM,kBAAkB,CAAA;AAS9D,eAAO,MAAM,aAAa,GAAI,UAAU;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,4CAqB1D,CAAA"}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useRegistry } from '@pascal-app/core';
|
|
3
|
-
import { useLoader } from '@react-three/fiber';
|
|
4
|
-
import { Suspense, useMemo, useRef } from 'react';
|
|
5
|
-
import { DoubleSide, TextureLoader } from 'three';
|
|
6
|
-
import { float, texture } from 'three/tsl';
|
|
7
|
-
import { MeshBasicNodeMaterial } from 'three/webgpu';
|
|
8
|
-
import { useAssetUrl } from '../../../hooks/use-asset-url';
|
|
9
|
-
import useViewer from '../../../store/use-viewer';
|
|
10
|
-
export const GuideRenderer = ({ node }) => {
|
|
11
|
-
const showGuides = useViewer((s) => s.showGuides);
|
|
12
|
-
const ref = useRef(null);
|
|
13
|
-
useRegistry(node.id, 'guide', ref);
|
|
14
|
-
const resolvedUrl = useAssetUrl(node.url);
|
|
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
|
-
};
|
|
17
|
-
const GuidePlane = ({ url, scale, opacity }) => {
|
|
18
|
-
const tex = useLoader(TextureLoader, url);
|
|
19
|
-
const { width, height, material } = useMemo(() => {
|
|
20
|
-
const img = tex.image;
|
|
21
|
-
const w = img.width || 1;
|
|
22
|
-
const h = img.height || 1;
|
|
23
|
-
const aspect = w / h;
|
|
24
|
-
// Default: 10 meters wide, height from aspect ratio
|
|
25
|
-
const planeWidth = 10 * scale;
|
|
26
|
-
const planeHeight = (10 / aspect) * scale;
|
|
27
|
-
const normalizedOpacity = opacity / 100;
|
|
28
|
-
const mat = new MeshBasicNodeMaterial({
|
|
29
|
-
transparent: true,
|
|
30
|
-
colorNode: texture(tex),
|
|
31
|
-
opacityNode: float(normalizedOpacity),
|
|
32
|
-
side: DoubleSide,
|
|
33
|
-
depthWrite: false,
|
|
34
|
-
});
|
|
35
|
-
return { width: planeWidth, height: planeHeight, material: mat };
|
|
36
|
-
}, [tex, scale, opacity]);
|
|
37
|
-
return (_jsx("mesh", { frustumCulled: false, material: material, raycast: () => { }, rotation: [-Math.PI / 2, 0, 0], children: _jsx("planeGeometry", { args: [width, height], boundingBox: null, boundingSphere: null }) }));
|
|
38
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
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,172 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useInteractive, useRegistry, useScene, } from '@pascal-app/core';
|
|
3
|
-
import { useAnimations } from '@react-three/drei';
|
|
4
|
-
import { Clone } from '@react-three/drei/core/Clone';
|
|
5
|
-
import { useGLTF } from '@react-three/drei/core/Gltf';
|
|
6
|
-
import { useFrame } from '@react-three/fiber';
|
|
7
|
-
import { Suspense, useEffect, useMemo, useRef } from 'react';
|
|
8
|
-
import { MathUtils } from 'three';
|
|
9
|
-
import { positionLocal, smoothstep, time } from 'three/tsl';
|
|
10
|
-
import { MeshStandardNodeMaterial } from 'three/webgpu';
|
|
11
|
-
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
12
|
-
import { resolveCdnUrl } from '../../../lib/asset-url';
|
|
13
|
-
import { baseMaterial, glassMaterial } from '../../../lib/materials';
|
|
14
|
-
import { useItemLightPool } from '../../../store/use-item-light-pool';
|
|
15
|
-
import { ErrorBoundary } from '../../error-boundary';
|
|
16
|
-
import { NodeRenderer } from '../node-renderer';
|
|
17
|
-
const getMaterialForOriginal = (original) => {
|
|
18
|
-
if (original.name.toLowerCase() === 'glass') {
|
|
19
|
-
return glassMaterial;
|
|
20
|
-
}
|
|
21
|
-
return baseMaterial;
|
|
22
|
-
};
|
|
23
|
-
const BrokenItemFallback = ({ node }) => {
|
|
24
|
-
const handlers = useNodeEvents(node, 'item');
|
|
25
|
-
const [w, h, d] = node.asset.dimensions;
|
|
26
|
-
return (_jsxs("mesh", { "position-y": h / 2, ...handlers, children: [_jsx("boxGeometry", { args: [w, h, d] }), _jsx("meshStandardMaterial", { color: "#ef4444", opacity: 0.6, transparent: true, wireframe: true })] }));
|
|
27
|
-
};
|
|
28
|
-
export const ItemRenderer = ({ node }) => {
|
|
29
|
-
const ref = useRef(null);
|
|
30
|
-
useRegistry(node.id, node.type, ref);
|
|
31
|
-
return (_jsxs("group", { position: node.position, ref: ref, rotation: node.rotation, visible: node.visible, children: [_jsx(ErrorBoundary, { fallback: _jsx(BrokenItemFallback, { node: node }), children: _jsx(Suspense, { fallback: _jsx(PreviewModel, { node: node }), children: _jsx(ModelRenderer, { node: node }) }) }), node.children?.map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId)))] }));
|
|
32
|
-
};
|
|
33
|
-
const previewMaterial = new MeshStandardNodeMaterial({
|
|
34
|
-
color: '#cccccc',
|
|
35
|
-
roughness: 1,
|
|
36
|
-
metalness: 0,
|
|
37
|
-
depthTest: false,
|
|
38
|
-
});
|
|
39
|
-
const previewOpacity = smoothstep(0.42, 0.55, positionLocal.y.add(time.mul(-0.2)).mul(10).fract());
|
|
40
|
-
previewMaterial.opacityNode = previewOpacity;
|
|
41
|
-
previewMaterial.transparent = true;
|
|
42
|
-
const PreviewModel = ({ node }) => {
|
|
43
|
-
return (_jsx("mesh", { material: previewMaterial, "position-y": node.asset.dimensions[1] / 2, children: _jsx("boxGeometry", { args: [node.asset.dimensions[0], node.asset.dimensions[1], node.asset.dimensions[2]] }) }));
|
|
44
|
-
};
|
|
45
|
-
const multiplyScales = (a, b) => [a[0] * b[0], a[1] * b[1], a[2] * b[2]];
|
|
46
|
-
const ModelRenderer = ({ node }) => {
|
|
47
|
-
const { scene, nodes, animations } = useGLTF(resolveCdnUrl(node.asset.src) || '');
|
|
48
|
-
const ref = useRef(null);
|
|
49
|
-
const { actions } = useAnimations(animations, ref);
|
|
50
|
-
// Freeze the interactive definition at mount — asset schemas don't change at runtime
|
|
51
|
-
const interactiveRef = useRef(node.asset.interactive);
|
|
52
|
-
if (nodes.cutout) {
|
|
53
|
-
nodes.cutout.visible = false;
|
|
54
|
-
}
|
|
55
|
-
const handlers = useNodeEvents(node, 'item');
|
|
56
|
-
useEffect(() => {
|
|
57
|
-
if (!node.parentId)
|
|
58
|
-
return;
|
|
59
|
-
useScene.getState().dirtyNodes.add(node.parentId);
|
|
60
|
-
}, [node.parentId]);
|
|
61
|
-
useEffect(() => {
|
|
62
|
-
const interactive = interactiveRef.current;
|
|
63
|
-
if (!interactive)
|
|
64
|
-
return;
|
|
65
|
-
useInteractive.getState().initItem(node.id, interactive);
|
|
66
|
-
return () => useInteractive.getState().removeItem(node.id);
|
|
67
|
-
}, [node.id]);
|
|
68
|
-
useMemo(() => {
|
|
69
|
-
scene.traverse((child) => {
|
|
70
|
-
if (child.isMesh) {
|
|
71
|
-
const mesh = child;
|
|
72
|
-
if (mesh.name === 'cutout') {
|
|
73
|
-
child.visible = false;
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
let hasGlass = false;
|
|
77
|
-
// Handle both single material and material array cases
|
|
78
|
-
if (Array.isArray(mesh.material)) {
|
|
79
|
-
mesh.material = mesh.material.map((mat) => getMaterialForOriginal(mat));
|
|
80
|
-
hasGlass = mesh.material.some((mat) => mat.name === 'glass');
|
|
81
|
-
// Fix geometry groups that reference materialIndex beyond the material
|
|
82
|
-
// array length — this causes three-mesh-bvh to crash with
|
|
83
|
-
// "Cannot read properties of undefined (reading 'side')"
|
|
84
|
-
const matCount = mesh.material.length;
|
|
85
|
-
if (mesh.geometry.groups.length > 0) {
|
|
86
|
-
for (const group of mesh.geometry.groups) {
|
|
87
|
-
if (group.materialIndex !== undefined && group.materialIndex >= matCount) {
|
|
88
|
-
group.materialIndex = 0;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
mesh.material = getMaterialForOriginal(mesh.material);
|
|
95
|
-
hasGlass = mesh.material.name === 'glass';
|
|
96
|
-
}
|
|
97
|
-
mesh.castShadow = !hasGlass;
|
|
98
|
-
mesh.receiveShadow = !hasGlass;
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
}, [scene]);
|
|
102
|
-
const interactive = interactiveRef.current;
|
|
103
|
-
const animEffect = interactive?.effects.find((e) => e.kind === 'animation') ?? null;
|
|
104
|
-
const lightEffects = interactive?.effects.filter((e) => e.kind === 'light') ?? [];
|
|
105
|
-
return (_jsxs(_Fragment, { children: [_jsx(Clone, { object: scene, position: node.asset.offset, ref: ref, rotation: node.asset.rotation, scale: multiplyScales(node.asset.scale || [1, 1, 1], node.scale || [1, 1, 1]), ...handlers }), animations.length > 0 && (_jsx(ItemAnimation, { actions: actions, animations: animations, animEffect: animEffect, interactive: interactive ?? null, nodeId: node.id })), lightEffects.map((effect, i) => (_jsx(ItemLightRegistrar, { effect: effect, index: i, interactive: interactive, nodeId: node.id }, i)))] }));
|
|
106
|
-
};
|
|
107
|
-
const ItemAnimation = ({ nodeId, animEffect, interactive, actions, animations, }) => {
|
|
108
|
-
const activeClipRef = useRef(null);
|
|
109
|
-
const fadingOutRef = useRef(null);
|
|
110
|
-
// Reactive: derive target clip name — only re-renders when the clip name itself changes
|
|
111
|
-
const targetClip = useInteractive((s) => {
|
|
112
|
-
const values = s.items[nodeId]?.controlValues;
|
|
113
|
-
if (!animEffect)
|
|
114
|
-
return animations[0]?.name ?? null;
|
|
115
|
-
const toggleIndex = interactive.controls.findIndex((c) => c.kind === 'toggle');
|
|
116
|
-
const isOn = toggleIndex >= 0 ? Boolean(values?.[toggleIndex]) : false;
|
|
117
|
-
return isOn
|
|
118
|
-
? (animEffect.clips.on ?? null)
|
|
119
|
-
: (animEffect.clips.off ?? animEffect.clips.loop ?? null);
|
|
120
|
-
});
|
|
121
|
-
// When target clip changes: kick off the transition
|
|
122
|
-
useEffect(() => {
|
|
123
|
-
// Cancel any ongoing fade-out immediately
|
|
124
|
-
if (fadingOutRef.current) {
|
|
125
|
-
fadingOutRef.current.timeScale = 0;
|
|
126
|
-
fadingOutRef.current = null;
|
|
127
|
-
}
|
|
128
|
-
// Move current clip to fade-out
|
|
129
|
-
if (activeClipRef.current && activeClipRef.current !== targetClip) {
|
|
130
|
-
const old = actions[activeClipRef.current];
|
|
131
|
-
if (old?.isRunning())
|
|
132
|
-
fadingOutRef.current = old;
|
|
133
|
-
}
|
|
134
|
-
// Start new clip at timeScale 0.01 (as 0 would cause isRunning to be false and thus not play at all), then fade in to 1
|
|
135
|
-
activeClipRef.current = targetClip;
|
|
136
|
-
if (targetClip) {
|
|
137
|
-
const next = actions[targetClip];
|
|
138
|
-
if (next) {
|
|
139
|
-
next.timeScale = 0.01;
|
|
140
|
-
next.play();
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}, [targetClip, actions]);
|
|
144
|
-
// useFrame: only lerping — no logic
|
|
145
|
-
useFrame((_, delta) => {
|
|
146
|
-
if (fadingOutRef.current) {
|
|
147
|
-
const action = fadingOutRef.current;
|
|
148
|
-
action.timeScale = MathUtils.lerp(action.timeScale, 0, Math.min(delta * 5, 1));
|
|
149
|
-
if (action.timeScale < 0.01) {
|
|
150
|
-
action.timeScale = 0;
|
|
151
|
-
fadingOutRef.current = null;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
if (activeClipRef.current) {
|
|
155
|
-
const action = actions[activeClipRef.current];
|
|
156
|
-
if (action?.isRunning() && action.timeScale < 1) {
|
|
157
|
-
action.timeScale = MathUtils.lerp(action.timeScale, 1, Math.min(delta * 5, 1));
|
|
158
|
-
if (1 - action.timeScale < 0.01)
|
|
159
|
-
action.timeScale = 1;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
return null;
|
|
164
|
-
};
|
|
165
|
-
const ItemLightRegistrar = ({ nodeId, effect, interactive, index, }) => {
|
|
166
|
-
useEffect(() => {
|
|
167
|
-
const key = `${nodeId}:${index}`;
|
|
168
|
-
useItemLightPool.getState().register(key, nodeId, effect, interactive);
|
|
169
|
-
return () => useItemLightPool.getState().unregister(key);
|
|
170
|
-
}, [nodeId, index, effect, interactive]);
|
|
171
|
-
return null;
|
|
172
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"level-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/level/level-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAe,MAAM,kBAAkB,CAAA;AAM9D,eAAO,MAAM,aAAa,GAAI,UAAU;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,4CAa1D,CAAA"}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useRegistry } from '@pascal-app/core';
|
|
3
|
-
import { useRef } from 'react';
|
|
4
|
-
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
5
|
-
import { NodeRenderer } from '../node-renderer';
|
|
6
|
-
export const LevelRenderer = ({ node }) => {
|
|
7
|
-
const ref = useRef(null);
|
|
8
|
-
useRegistry(node.id, node.type, ref);
|
|
9
|
-
const handlers = useNodeEvents(node, 'level');
|
|
10
|
-
return (_jsx("group", { ref: ref, ...handlers, children: node.children.map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId))) }));
|
|
11
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"roof-materials.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/roof/roof-materials.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,QAAQ,EAKzC,CAAA;AAGD,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,QAAQ,EAK9C,CAAA"}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import * as THREE from 'three';
|
|
2
|
-
// Production materials — match the rest of the scene (white walls, light-gray slabs).
|
|
3
|
-
// Indices: 0 = Wall/Trim, 1 = Deck, 2 = Interior, 3 = Shingle
|
|
4
|
-
export const roofMaterials = [
|
|
5
|
-
new THREE.MeshStandardMaterial({ color: 'white', roughness: 1, side: THREE.DoubleSide }), // 0: Wall/Trim
|
|
6
|
-
new THREE.MeshStandardMaterial({ color: '#e5e5e5', roughness: 1, side: THREE.FrontSide }), // 1: Deck
|
|
7
|
-
new THREE.MeshStandardMaterial({ color: 'white', roughness: 1, side: THREE.DoubleSide }), // 2: Interior
|
|
8
|
-
new THREE.MeshStandardMaterial({ color: '#e5e5e5', roughness: 0.9, side: THREE.FrontSide }), // 3: Shingle
|
|
9
|
-
];
|
|
10
|
-
// Debug materials — vivid, distinct colours to identify each surface group.
|
|
11
|
-
export const roofDebugMaterials = [
|
|
12
|
-
new THREE.MeshStandardMaterial({ color: '#eaeaea', roughness: 0.8, side: THREE.DoubleSide }), // 0: Wall
|
|
13
|
-
new THREE.MeshStandardMaterial({ color: '#000000', roughness: 0.9, side: THREE.FrontSide }), // 1: Deck
|
|
14
|
-
new THREE.MeshStandardMaterial({ color: '#dddddd', roughness: 0.9, side: THREE.DoubleSide }), // 2: Interior
|
|
15
|
-
new THREE.MeshStandardMaterial({ color: '#4ade80', roughness: 0.9, side: THREE.FrontSide }), // 3: Shingle
|
|
16
|
-
];
|
|
@@ -1 +0,0 @@
|
|
|
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,32 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useRegistry } from '@pascal-app/core';
|
|
3
|
-
import { useEffect, useMemo, useRef } from 'react';
|
|
4
|
-
import * as THREE from 'three';
|
|
5
|
-
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
6
|
-
import useViewer from '../../../store/use-viewer';
|
|
7
|
-
import { getRoofMaterialArray } from '../../../systems/roof/roof-materials';
|
|
8
|
-
import { NodeRenderer } from '../node-renderer';
|
|
9
|
-
import { roofDebugMaterials, roofMaterials } from './roof-materials';
|
|
10
|
-
export const RoofRenderer = ({ node }) => {
|
|
11
|
-
const ref = useRef(null);
|
|
12
|
-
useRegistry(node.id, 'roof', ref);
|
|
13
|
-
const handlers = useNodeEvents(node, 'roof');
|
|
14
|
-
const debugColors = useViewer((s) => s.debugColors);
|
|
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]);
|
|
25
|
-
const material = debugColors ? roofDebugMaterials : customMaterial || roofMaterials;
|
|
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))) })] }));
|
|
32
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
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,40 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useRegistry, useScene, } from '@pascal-app/core';
|
|
3
|
-
import { useEffect, useMemo, useRef } from 'react';
|
|
4
|
-
import * as THREE from 'three';
|
|
5
|
-
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
6
|
-
import useViewer from '../../../store/use-viewer';
|
|
7
|
-
import { getRoofMaterialArray } from '../../../systems/roof/roof-materials';
|
|
8
|
-
import { roofDebugMaterials, roofMaterials } from '../roof/roof-materials';
|
|
9
|
-
export const RoofSegmentRenderer = ({ node }) => {
|
|
10
|
-
const ref = useRef(null);
|
|
11
|
-
const nodes = useScene((state) => state.nodes);
|
|
12
|
-
useRegistry(node.id, 'roof-segment', ref);
|
|
13
|
-
const handlers = useNodeEvents(node, 'roof-segment');
|
|
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
|
-
}, []);
|
|
27
|
-
const customMaterial = useMemo(() => {
|
|
28
|
-
if (node.material !== undefined || typeof node.materialPreset === 'string') {
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
return parentNode ? getRoofMaterialArray(parentNode) : null;
|
|
32
|
-
}, [node, parentNode]);
|
|
33
|
-
const material = debugColors ? roofDebugMaterials : customMaterial || roofMaterials;
|
|
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 }));
|
|
40
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scan-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/scan/scan-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAe,MAAM,kBAAkB,CAAA;AAO7D,eAAO,MAAM,YAAY,GAAI,UAAU;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,4CAsBxD,CAAA"}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useRegistry } from '@pascal-app/core';
|
|
3
|
-
import { Suspense, useMemo, useRef } from 'react';
|
|
4
|
-
import { useAssetUrl } from '../../../hooks/use-asset-url';
|
|
5
|
-
import { useGLTFKTX2 } from '../../../hooks/use-gltf-ktx2';
|
|
6
|
-
import useViewer from '../../../store/use-viewer';
|
|
7
|
-
export const ScanRenderer = ({ node }) => {
|
|
8
|
-
const showScans = useViewer((s) => s.showScans);
|
|
9
|
-
const ref = useRef(null);
|
|
10
|
-
useRegistry(node.id, 'scan', ref);
|
|
11
|
-
const resolvedUrl = useAssetUrl(node.url);
|
|
12
|
-
return (_jsx("group", { position: node.position, ref: ref, rotation: node.rotation, scale: [node.scale, node.scale, node.scale], visible: showScans, children: resolvedUrl && (_jsx(Suspense, { children: _jsx(ScanModel, { opacity: node.opacity, url: resolvedUrl }) })) }));
|
|
13
|
-
};
|
|
14
|
-
const ScanModel = ({ url, opacity }) => {
|
|
15
|
-
const gltf = useGLTFKTX2(url);
|
|
16
|
-
const scene = gltf.scene;
|
|
17
|
-
useMemo(() => {
|
|
18
|
-
const normalizedOpacity = opacity / 100;
|
|
19
|
-
const isTransparent = normalizedOpacity < 1;
|
|
20
|
-
const updateMaterial = (material) => {
|
|
21
|
-
if (isTransparent) {
|
|
22
|
-
material.transparent = true;
|
|
23
|
-
material.opacity = normalizedOpacity;
|
|
24
|
-
material.depthWrite = false;
|
|
25
|
-
}
|
|
26
|
-
else {
|
|
27
|
-
material.transparent = false;
|
|
28
|
-
material.opacity = 1;
|
|
29
|
-
material.depthWrite = true;
|
|
30
|
-
}
|
|
31
|
-
material.needsUpdate = true;
|
|
32
|
-
};
|
|
33
|
-
scene.traverse((child) => {
|
|
34
|
-
if (child.isMesh) {
|
|
35
|
-
const mesh = child;
|
|
36
|
-
// Disable raycasting
|
|
37
|
-
mesh.raycast = () => { };
|
|
38
|
-
// Exclude from bounding box calculations
|
|
39
|
-
mesh.geometry.boundingBox = null;
|
|
40
|
-
mesh.geometry.boundingSphere = null;
|
|
41
|
-
mesh.frustumCulled = false;
|
|
42
|
-
if (Array.isArray(mesh.material)) {
|
|
43
|
-
mesh.material.forEach((material) => {
|
|
44
|
-
updateMaterial(material);
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
else {
|
|
48
|
-
updateMaterial(mesh.material);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
}, [scene, opacity]);
|
|
53
|
-
return _jsx("primitive", { object: scene });
|
|
54
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useRegistry, useScene } from '@pascal-app/core';
|
|
3
|
-
import polygonClipping from 'polygon-clipping';
|
|
4
|
-
import { useMemo, useRef } from 'react';
|
|
5
|
-
import { BufferGeometry, Float32BufferAttribute, Path, Shape } from 'three';
|
|
6
|
-
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
7
|
-
import useViewer from '../../../store/use-viewer';
|
|
8
|
-
import { NodeRenderer } from '../node-renderer';
|
|
9
|
-
const Y_OFFSET = 0.01;
|
|
10
|
-
/**
|
|
11
|
-
* Creates simple line geometry for site boundary
|
|
12
|
-
* Single horizontal line at ground level
|
|
13
|
-
*/
|
|
14
|
-
const createBoundaryLineGeometry = (points) => {
|
|
15
|
-
const geometry = new BufferGeometry();
|
|
16
|
-
if (points.length < 2)
|
|
17
|
-
return geometry;
|
|
18
|
-
const positions = [];
|
|
19
|
-
// Create a simple line loop at ground level
|
|
20
|
-
for (const [x, z] of points) {
|
|
21
|
-
positions.push(x ?? 0, Y_OFFSET, z ?? 0);
|
|
22
|
-
}
|
|
23
|
-
// Close the loop
|
|
24
|
-
positions.push(points[0]?.[0] ?? 0, Y_OFFSET, points[0]?.[1] ?? 0);
|
|
25
|
-
geometry.setAttribute('position', new Float32BufferAttribute(positions, 3));
|
|
26
|
-
return geometry;
|
|
27
|
-
};
|
|
28
|
-
export const SiteRenderer = ({ node }) => {
|
|
29
|
-
const ref = useRef(null);
|
|
30
|
-
useRegistry(node.id, 'site', ref);
|
|
31
|
-
const theme = useViewer((state) => state.theme);
|
|
32
|
-
const bgColor = theme === 'dark' ? '#1f2433' : '#fafafa';
|
|
33
|
-
// Cache slab polygon references to keep the selector stable across unrelated store updates
|
|
34
|
-
const slabPolygonsCache = useRef([]);
|
|
35
|
-
const slabPolygons = useScene((state) => {
|
|
36
|
-
const nodeList = Object.values(state.nodes);
|
|
37
|
-
const levelIndexById = new Map();
|
|
38
|
-
let lowestLevelIndex = Number.POSITIVE_INFINITY;
|
|
39
|
-
nodeList.forEach((n) => {
|
|
40
|
-
if (n.type !== 'level')
|
|
41
|
-
return;
|
|
42
|
-
levelIndexById.set(n.id, n.level);
|
|
43
|
-
lowestLevelIndex = Math.min(lowestLevelIndex, n.level);
|
|
44
|
-
});
|
|
45
|
-
const next = nodeList
|
|
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)
|
|
53
|
-
.filter((n) => {
|
|
54
|
-
if (!Number.isFinite(lowestLevelIndex))
|
|
55
|
-
return true;
|
|
56
|
-
const parentLevel = n.parentId ? levelIndexById.get(n.parentId) : undefined;
|
|
57
|
-
return parentLevel === lowestLevelIndex;
|
|
58
|
-
})
|
|
59
|
-
.map((n) => n.polygon);
|
|
60
|
-
const prev = slabPolygonsCache.current;
|
|
61
|
-
if (next.length === prev.length && next.every((p, i) => p === prev[i]))
|
|
62
|
-
return prev;
|
|
63
|
-
slabPolygonsCache.current = next;
|
|
64
|
-
return next;
|
|
65
|
-
});
|
|
66
|
-
// Ground shape: site polygon with slab footprints punched as holes
|
|
67
|
-
const groundShape = useMemo(() => {
|
|
68
|
-
if (!node?.polygon?.points || node.polygon.points.length < 3)
|
|
69
|
-
return null;
|
|
70
|
-
const pts = node.polygon.points;
|
|
71
|
-
const shape = new Shape();
|
|
72
|
-
shape.moveTo(pts[0][0], -pts[0][1]);
|
|
73
|
-
for (let i = 1; i < pts.length; i++)
|
|
74
|
-
shape.lineTo(pts[i][0], -pts[i][1]);
|
|
75
|
-
shape.closePath();
|
|
76
|
-
if (slabPolygons.length > 0) {
|
|
77
|
-
const multiPolygons = slabPolygons.map((p) => [
|
|
78
|
-
p.map((pt) => [pt[0], -pt[1]]),
|
|
79
|
-
]);
|
|
80
|
-
const unioned = polygonClipping.union(multiPolygons[0], ...multiPolygons.slice(1));
|
|
81
|
-
for (const geom of unioned) {
|
|
82
|
-
const ring = geom[0];
|
|
83
|
-
if (ring && ring.length > 0) {
|
|
84
|
-
const hole = new Path();
|
|
85
|
-
hole.moveTo(ring[0][0], ring[0][1]);
|
|
86
|
-
for (let i = 1; i < ring.length; i++)
|
|
87
|
-
hole.lineTo(ring[i][0], ring[i][1]);
|
|
88
|
-
hole.closePath();
|
|
89
|
-
shape.holes.push(hole);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return shape;
|
|
94
|
-
}, [node?.polygon?.points, slabPolygons]);
|
|
95
|
-
// Create boundary line geometry
|
|
96
|
-
const lineGeometry = useMemo(() => {
|
|
97
|
-
if (!node?.polygon?.points || node.polygon.points.length < 2)
|
|
98
|
-
return null;
|
|
99
|
-
return createBoundaryLineGeometry(node.polygon.points);
|
|
100
|
-
}, [node?.polygon?.points]);
|
|
101
|
-
const handlers = useNodeEvents(node, 'site');
|
|
102
|
-
if (!(node && lineGeometry)) {
|
|
103
|
-
return null;
|
|
104
|
-
}
|
|
105
|
-
return (_jsxs("group", { ref: ref, ...handlers, children: [node.children.map((child) => (_jsx(NodeRenderer, { nodeId: typeof child === 'string' ? child : child.id }, typeof child === 'string' ? child : child.id))), groundShape && (_jsxs("mesh", { position: [0, -0.05, 0], receiveShadow: true, rotation: [-Math.PI / 2, 0, 0], children: [_jsx("shapeGeometry", { args: [groundShape] }), _jsx("meshStandardMaterial", { color: bgColor, depthWrite: true, polygonOffset: true, polygonOffsetFactor: 1, polygonOffsetUnits: 1 })] })), _jsx("line", { frustumCulled: false, geometry: lineGeometry, renderOrder: 9, children: _jsx("lineBasicMaterial", { color: "#f59e0b", linewidth: 2, opacity: 0.6, transparent: true }) })] }));
|
|
106
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|