@pascal-app/viewer 0.1.13 → 0.2.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.
- package/dist/components/renderers/ceiling/ceiling-renderer.d.ts.map +1 -1
- package/dist/components/renderers/ceiling/ceiling-renderer.js +16 -9
- package/dist/components/renderers/door/door-renderer.d.ts +5 -0
- package/dist/components/renderers/door/door-renderer.d.ts.map +1 -0
- package/dist/components/renderers/door/door-renderer.js +11 -0
- package/dist/components/renderers/guide/guide-renderer.d.ts.map +1 -1
- package/dist/components/renderers/guide/guide-renderer.js +4 -2
- package/dist/components/renderers/item/item-renderer.d.ts.map +1 -1
- package/dist/components/renderers/item/item-renderer.js +92 -7
- package/dist/components/renderers/node-renderer.d.ts.map +1 -1
- package/dist/components/renderers/node-renderer.js +3 -1
- package/dist/components/renderers/roof/roof-materials.d.ts +4 -0
- package/dist/components/renderers/roof/roof-materials.d.ts.map +1 -0
- package/dist/components/renderers/roof/roof-materials.js +16 -0
- package/dist/components/renderers/roof/roof-renderer.d.ts.map +1 -1
- package/dist/components/renderers/roof/roof-renderer.js +5 -1
- package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts +5 -0
- package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts.map +1 -0
- package/dist/components/renderers/roof-segment/roof-segment-renderer.js +13 -0
- package/dist/components/renderers/scan/scan-renderer.d.ts.map +1 -1
- package/dist/components/renderers/scan/scan-renderer.js +3 -1
- package/dist/components/renderers/scene-renderer.d.ts.map +1 -1
- package/dist/components/renderers/scene-renderer.js +3 -3
- package/dist/components/renderers/site/site-renderer.d.ts.map +1 -1
- package/dist/components/renderers/site/site-renderer.js +4 -19
- package/dist/components/renderers/slab/slab-renderer.js +1 -1
- package/dist/components/renderers/wall/wall-renderer.d.ts.map +1 -1
- package/dist/components/renderers/wall/wall-renderer.js +1 -1
- package/dist/components/renderers/window/window-renderer.d.ts.map +1 -1
- package/dist/components/renderers/window/window-renderer.js +2 -1
- package/dist/components/renderers/zone/zone-renderer.d.ts.map +1 -1
- package/dist/components/renderers/zone/zone-renderer.js +33 -13
- package/dist/components/viewer/ground-occluder.d.ts +2 -0
- package/dist/components/viewer/ground-occluder.d.ts.map +1 -0
- package/dist/components/viewer/ground-occluder.js +55 -0
- package/dist/components/viewer/index.d.ts +1 -0
- package/dist/components/viewer/index.d.ts.map +1 -1
- package/dist/components/viewer/index.js +59 -6
- package/dist/components/viewer/lights.d.ts.map +1 -1
- package/dist/components/viewer/lights.js +69 -5
- package/dist/components/viewer/perf-monitor.d.ts +2 -0
- package/dist/components/viewer/perf-monitor.d.ts.map +1 -0
- package/dist/components/viewer/perf-monitor.js +42 -0
- package/dist/components/viewer/post-processing.d.ts.map +1 -1
- package/dist/components/viewer/post-processing.js +230 -107
- package/dist/components/viewer/selection-manager.d.ts.map +1 -1
- package/dist/components/viewer/selection-manager.js +47 -17
- package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
- package/dist/components/viewer/viewer-camera.js +2 -2
- package/dist/hooks/use-gltf-ktx2.d.ts +1 -1
- package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
- package/dist/hooks/use-gltf-ktx2.js +25 -7
- package/dist/hooks/use-node-events.d.ts +9 -1
- package/dist/hooks/use-node-events.d.ts.map +1 -1
- package/dist/hooks/use-node-events.js +5 -5
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/lib/asset-url.d.ts +1 -1
- package/dist/lib/asset-url.d.ts.map +1 -1
- package/dist/lib/asset-url.js +2 -1
- package/dist/lib/layers.d.ts +5 -0
- package/dist/lib/layers.d.ts.map +1 -0
- package/dist/lib/layers.js +4 -0
- package/dist/store/use-item-light-pool.d.ts +18 -0
- package/dist/store/use-item-light-pool.d.ts.map +1 -0
- package/dist/store/use-item-light-pool.js +30 -0
- package/dist/store/use-viewer.d.ts +52 -7
- package/dist/store/use-viewer.d.ts.map +1 -1
- package/dist/store/use-viewer.js +79 -17
- package/dist/systems/interactive/interactive-system.d.ts +2 -0
- package/dist/systems/interactive/interactive-system.d.ts.map +1 -0
- package/dist/systems/interactive/interactive-system.js +95 -0
- package/dist/systems/item-light/item-light-system.d.ts +2 -0
- package/dist/systems/item-light/item-light-system.d.ts.map +1 -0
- package/dist/systems/item-light/item-light-system.js +235 -0
- package/dist/systems/level/level-system.d.ts.map +1 -1
- package/dist/systems/level/level-system.js +19 -8
- package/dist/systems/level/level-utils.d.ts +17 -0
- package/dist/systems/level/level-utils.d.ts.map +1 -0
- package/dist/systems/level/level-utils.js +82 -0
- package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout.js +2 -4
- package/dist/systems/zone/zone-system.d.ts.map +1 -1
- package/dist/systems/zone/zone-system.js +29 -3
- package/package.json +6 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ceiling-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/ceiling/ceiling-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAe,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"ceiling-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/ceiling/ceiling-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAe,MAAM,kBAAkB,CAAA;AA+ChE,eAAO,MAAM,eAAe,GAAI,UAAU;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,4CAwB9D,CAAA"}
|
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useRegistry } from '@pascal-app/core';
|
|
3
3
|
import { useRef } from 'react';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { float, mix, positionWorld, smoothstep } from 'three/tsl';
|
|
5
|
+
import { BackSide, FrontSide, MeshBasicNodeMaterial } from 'three/webgpu';
|
|
6
6
|
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
7
7
|
import { NodeRenderer } from '../node-renderer';
|
|
8
8
|
// TSL material that renders differently based on face direction:
|
|
9
9
|
// - Back face (looking up at ceiling from below): solid
|
|
10
10
|
// - Front face (looking down at ceiling from above): 30% opacity
|
|
11
|
-
const
|
|
12
|
-
color:
|
|
13
|
-
side: DoubleSide,
|
|
11
|
+
const ceilingTopMaterial = new MeshBasicNodeMaterial({
|
|
12
|
+
color: 0xb5_a7_8d,
|
|
14
13
|
transparent: true,
|
|
15
14
|
depthWrite: false,
|
|
15
|
+
side: FrontSide,
|
|
16
|
+
// Disabled as we only show ceiling grid when needed
|
|
17
|
+
// alphaTestNode: float(0.4), // Discard pixels with alpha below 0.4 to create grid lines and not affect depth buffer
|
|
18
|
+
});
|
|
19
|
+
const ceilingBottomMaterial = new MeshBasicNodeMaterial({
|
|
20
|
+
color: 0x99_99_99,
|
|
21
|
+
transparent: true,
|
|
22
|
+
side: BackSide,
|
|
16
23
|
});
|
|
17
24
|
// Create grid pattern based on local position
|
|
18
25
|
const gridScale = 5; // Grid cells per meter (1 = 1m grid)
|
|
@@ -25,14 +32,14 @@ const lineX = smoothstep(lineWidth, 0, gridX).add(smoothstep(1.0 - lineWidth, 1.
|
|
|
25
32
|
const lineY = smoothstep(lineWidth, 0, gridY).add(smoothstep(1.0 - lineWidth, 1.0, gridY));
|
|
26
33
|
// Combine: if either X or Y is a line, show the line
|
|
27
34
|
const gridPattern = lineX.max(lineY);
|
|
28
|
-
// Grid lines at 0.
|
|
29
|
-
const gridOpacity = mix(float(0.
|
|
35
|
+
// Grid lines at 0.6 opacity, spaces at 0.2 opacity
|
|
36
|
+
const gridOpacity = mix(float(0.2), float(0.6), gridPattern);
|
|
30
37
|
// faceDirection is 1.0 for front face, -1.0 for back face
|
|
31
38
|
// Front face (top, looking down): grid pattern, Back face (bottom, looking up): solid
|
|
32
|
-
|
|
39
|
+
ceilingTopMaterial.opacityNode = gridOpacity;
|
|
33
40
|
export const CeilingRenderer = ({ node }) => {
|
|
34
41
|
const ref = useRef(null);
|
|
35
42
|
useRegistry(node.id, 'ceiling', ref);
|
|
36
43
|
const handlers = useNodeEvents(node, 'ceiling');
|
|
37
|
-
return (_jsxs("mesh", { ref: ref, material:
|
|
44
|
+
return (_jsxs("mesh", { material: ceilingBottomMaterial, ref: ref, children: [_jsx("boxGeometry", { args: [0, 0, 0] }), _jsx("mesh", { material: ceilingTopMaterial, name: "ceiling-grid", ...handlers, scale: 0, visible: false, children: _jsx("boxGeometry", { args: [0, 0, 0] }) }), node.children.map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId)))] }));
|
|
38
45
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"door-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/door/door-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAe,MAAM,kBAAkB,CAAA;AAK7D,eAAO,MAAM,YAAY,GAAI,UAAU;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,4CAsBxD,CAAA"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } 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
|
+
export const DoorRenderer = ({ node }) => {
|
|
6
|
+
const ref = useRef(null);
|
|
7
|
+
useRegistry(node.id, 'door', ref);
|
|
8
|
+
const handlers = useNodeEvents(node, 'door');
|
|
9
|
+
const isTransient = !!node.metadata?.isTransient;
|
|
10
|
+
return (_jsxs("mesh", { castShadow: true, position: node.position, receiveShadow: true, ref: ref, rotation: node.rotation, visible: node.visible, ...(isTransient ? {} : handlers), children: [_jsx("boxGeometry", { args: [0, 0, 0] }), _jsx("meshStandardMaterial", { color: "#d1d5db" })] }));
|
|
11
|
+
};
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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"}
|
|
@@ -6,11 +6,13 @@ import { DoubleSide, TextureLoader } from 'three';
|
|
|
6
6
|
import { float, texture } from 'three/tsl';
|
|
7
7
|
import { MeshBasicNodeMaterial } from 'three/webgpu';
|
|
8
8
|
import { useAssetUrl } from '../../../hooks/use-asset-url';
|
|
9
|
+
import useViewer from '../../../store/use-viewer';
|
|
9
10
|
export const GuideRenderer = ({ node }) => {
|
|
11
|
+
const showGuides = useViewer((s) => s.showGuides);
|
|
10
12
|
const ref = useRef(null);
|
|
11
13
|
useRegistry(node.id, 'guide', ref);
|
|
12
14
|
const resolvedUrl = useAssetUrl(node.url);
|
|
13
|
-
return (_jsx("group", {
|
|
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 }) })) }));
|
|
14
16
|
};
|
|
15
17
|
const GuidePlane = ({ url, scale, opacity }) => {
|
|
16
18
|
const tex = useLoader(TextureLoader, url);
|
|
@@ -32,5 +34,5 @@ const GuidePlane = ({ url, scale, opacity }) => {
|
|
|
32
34
|
});
|
|
33
35
|
return { width: planeWidth, height: planeHeight, material: mat };
|
|
34
36
|
}, [tex, scale, opacity]);
|
|
35
|
-
return (_jsx("mesh", {
|
|
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 }) }));
|
|
36
38
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"item-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/item/item-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
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;AAwCzB,eAAO,MAAM,YAAY,GAAI,UAAU;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,4CAexD,CAAA"}
|
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useRegistry, useScene } from '@pascal-app/core';
|
|
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';
|
|
3
4
|
import { Clone } from '@react-three/drei/core/Clone';
|
|
4
5
|
import { useGLTF } from '@react-three/drei/core/Gltf';
|
|
6
|
+
import { useFrame } from '@react-three/fiber';
|
|
5
7
|
import { Suspense, useEffect, useMemo, useRef } from 'react';
|
|
8
|
+
import { MathUtils } from 'three';
|
|
6
9
|
import { positionLocal, smoothstep, time } from 'three/tsl';
|
|
7
10
|
import { DoubleSide, MeshStandardNodeMaterial } from 'three/webgpu';
|
|
8
11
|
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
9
12
|
import { resolveCdnUrl } from '../../../lib/asset-url';
|
|
13
|
+
import { useItemLightPool } from '../../../store/use-item-light-pool';
|
|
10
14
|
import { NodeRenderer } from '../node-renderer';
|
|
11
15
|
// Shared materials to avoid creating new instances for every mesh
|
|
12
16
|
const defaultMaterial = new MeshStandardNodeMaterial({
|
|
13
|
-
color:
|
|
17
|
+
color: 0xff_ff_ff,
|
|
14
18
|
roughness: 1,
|
|
15
19
|
metalness: 0,
|
|
16
20
|
});
|
|
@@ -33,7 +37,7 @@ const getMaterialForOriginal = (original) => {
|
|
|
33
37
|
export const ItemRenderer = ({ node }) => {
|
|
34
38
|
const ref = useRef(null);
|
|
35
39
|
useRegistry(node.id, node.type, ref);
|
|
36
|
-
return (_jsxs("group", { position: node.position, rotation: node.rotation,
|
|
40
|
+
return (_jsxs("group", { position: node.position, ref: ref, rotation: node.rotation, visible: node.visible, children: [_jsx(Suspense, { fallback: _jsx(PreviewModel, { node: node }), children: _jsx(ModelRenderer, { node: node }) }), node.children?.map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId)))] }));
|
|
37
41
|
};
|
|
38
42
|
const previewMaterial = new MeshStandardNodeMaterial({
|
|
39
43
|
color: '#cccccc',
|
|
@@ -45,10 +49,15 @@ const previewOpacity = smoothstep(0.42, 0.55, positionLocal.y.add(time.mul(-0.2)
|
|
|
45
49
|
previewMaterial.opacityNode = previewOpacity;
|
|
46
50
|
previewMaterial.transparent = true;
|
|
47
51
|
const PreviewModel = ({ node }) => {
|
|
48
|
-
return (_jsx("mesh", { "position-y": node.asset.dimensions[1] / 2,
|
|
52
|
+
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]] }) }));
|
|
49
53
|
};
|
|
54
|
+
const multiplyScales = (a, b) => [a[0] * b[0], a[1] * b[1], a[2] * b[2]];
|
|
50
55
|
const ModelRenderer = ({ node }) => {
|
|
51
|
-
const { scene, nodes } = useGLTF(resolveCdnUrl(node.asset.src) || '');
|
|
56
|
+
const { scene, nodes, animations } = useGLTF(resolveCdnUrl(node.asset.src) || '');
|
|
57
|
+
const ref = useRef(null);
|
|
58
|
+
const { actions } = useAnimations(animations, ref);
|
|
59
|
+
// Freeze the interactive definition at mount — asset schemas don't change at runtime
|
|
60
|
+
const interactiveRef = useRef(node.asset.interactive);
|
|
52
61
|
if (nodes.cutout) {
|
|
53
62
|
nodes.cutout.visible = false;
|
|
54
63
|
}
|
|
@@ -58,6 +67,13 @@ const ModelRenderer = ({ node }) => {
|
|
|
58
67
|
return;
|
|
59
68
|
useScene.getState().dirtyNodes.add(node.parentId);
|
|
60
69
|
}, [node.parentId]);
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
const interactive = interactiveRef.current;
|
|
72
|
+
if (!interactive)
|
|
73
|
+
return;
|
|
74
|
+
useInteractive.getState().initItem(node.id, interactive);
|
|
75
|
+
return () => useInteractive.getState().removeItem(node.id);
|
|
76
|
+
}, [node.id]);
|
|
61
77
|
useMemo(() => {
|
|
62
78
|
scene.traverse((child) => {
|
|
63
79
|
if (child.isMesh) {
|
|
@@ -81,5 +97,74 @@ const ModelRenderer = ({ node }) => {
|
|
|
81
97
|
}
|
|
82
98
|
});
|
|
83
99
|
}, [scene]);
|
|
84
|
-
|
|
100
|
+
const interactive = interactiveRef.current;
|
|
101
|
+
const animEffect = interactive?.effects.find((e) => e.kind === 'animation') ?? null;
|
|
102
|
+
const lightEffects = interactive?.effects.filter((e) => e.kind === 'light') ?? [];
|
|
103
|
+
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)))] }));
|
|
104
|
+
};
|
|
105
|
+
const ItemAnimation = ({ nodeId, animEffect, interactive, actions, animations, }) => {
|
|
106
|
+
const activeClipRef = useRef(null);
|
|
107
|
+
const fadingOutRef = useRef(null);
|
|
108
|
+
// Reactive: derive target clip name — only re-renders when the clip name itself changes
|
|
109
|
+
const targetClip = useInteractive((s) => {
|
|
110
|
+
const values = s.items[nodeId]?.controlValues;
|
|
111
|
+
if (!animEffect)
|
|
112
|
+
return animations[0]?.name ?? null;
|
|
113
|
+
const toggleIndex = interactive.controls.findIndex((c) => c.kind === 'toggle');
|
|
114
|
+
const isOn = toggleIndex >= 0 ? Boolean(values?.[toggleIndex]) : false;
|
|
115
|
+
return isOn
|
|
116
|
+
? (animEffect.clips.on ?? null)
|
|
117
|
+
: (animEffect.clips.off ?? animEffect.clips.loop ?? null);
|
|
118
|
+
});
|
|
119
|
+
// When target clip changes: kick off the transition
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
// Cancel any ongoing fade-out immediately
|
|
122
|
+
if (fadingOutRef.current) {
|
|
123
|
+
fadingOutRef.current.timeScale = 0;
|
|
124
|
+
fadingOutRef.current = null;
|
|
125
|
+
}
|
|
126
|
+
// Move current clip to fade-out
|
|
127
|
+
if (activeClipRef.current && activeClipRef.current !== targetClip) {
|
|
128
|
+
const old = actions[activeClipRef.current];
|
|
129
|
+
if (old?.isRunning())
|
|
130
|
+
fadingOutRef.current = old;
|
|
131
|
+
}
|
|
132
|
+
// 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
|
|
133
|
+
activeClipRef.current = targetClip;
|
|
134
|
+
if (targetClip) {
|
|
135
|
+
const next = actions[targetClip];
|
|
136
|
+
if (next) {
|
|
137
|
+
next.timeScale = 0.01;
|
|
138
|
+
next.play();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}, [targetClip, actions]);
|
|
142
|
+
// useFrame: only lerping — no logic
|
|
143
|
+
useFrame((_, delta) => {
|
|
144
|
+
if (fadingOutRef.current) {
|
|
145
|
+
const action = fadingOutRef.current;
|
|
146
|
+
action.timeScale = MathUtils.lerp(action.timeScale, 0, Math.min(delta * 5, 1));
|
|
147
|
+
if (action.timeScale < 0.01) {
|
|
148
|
+
action.timeScale = 0;
|
|
149
|
+
fadingOutRef.current = null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (activeClipRef.current) {
|
|
153
|
+
const action = actions[activeClipRef.current];
|
|
154
|
+
if (action?.isRunning() && action.timeScale < 1) {
|
|
155
|
+
action.timeScale = MathUtils.lerp(action.timeScale, 1, Math.min(delta * 5, 1));
|
|
156
|
+
if (1 - action.timeScale < 0.01)
|
|
157
|
+
action.timeScale = 1;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
return null;
|
|
162
|
+
};
|
|
163
|
+
const ItemLightRegistrar = ({ nodeId, effect, interactive, index, }) => {
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
const key = `${nodeId}:${index}`;
|
|
166
|
+
useItemLightPool.getState().register(key, nodeId, effect, interactive);
|
|
167
|
+
return () => useItemLightPool.getState().unregister(key);
|
|
168
|
+
}, [nodeId, index, effect, interactive]);
|
|
169
|
+
return null;
|
|
85
170
|
};
|
|
@@ -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;
|
|
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;AAgBzD,eAAO,MAAM,YAAY,GAAI,YAAY;IAAE,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,mDAuBjE,CAAA"}
|
|
@@ -3,10 +3,12 @@ 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 { DoorRenderer } from './door/door-renderer';
|
|
6
7
|
import { GuideRenderer } from './guide/guide-renderer';
|
|
7
8
|
import { ItemRenderer } from './item/item-renderer';
|
|
8
9
|
import { LevelRenderer } from './level/level-renderer';
|
|
9
10
|
import { RoofRenderer } from './roof/roof-renderer';
|
|
11
|
+
import { RoofSegmentRenderer } from './roof-segment/roof-segment-renderer';
|
|
10
12
|
import { ScanRenderer } from './scan/scan-renderer';
|
|
11
13
|
import { SiteRenderer } from './site/site-renderer';
|
|
12
14
|
import { SlabRenderer } from './slab/slab-renderer';
|
|
@@ -17,5 +19,5 @@ export const NodeRenderer = ({ nodeId }) => {
|
|
|
17
19
|
const node = useScene((state) => state.nodes[nodeId]);
|
|
18
20
|
if (!node)
|
|
19
21
|
return null;
|
|
20
|
-
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 === 'window' && _jsx(WindowRenderer, { node: node }), node.type === 'zone' && _jsx(ZoneRenderer, { node: node }), node.type === 'roof' && _jsx(RoofRenderer, { node: node }), node.type === 'scan' && _jsx(ScanRenderer, { node: node }), node.type === 'guide' && _jsx(GuideRenderer, { node: node })] }));
|
|
22
|
+
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 === '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 === 'scan' && _jsx(ScanRenderer, { node: node }), node.type === 'guide' && _jsx(GuideRenderer, { node: node })] }));
|
|
21
23
|
};
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
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 +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;
|
|
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;AAQ7D,eAAO,MAAM,YAAY,GAAI,UAAU;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,4CA+BxD,CAAA"}
|
|
@@ -2,9 +2,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useRegistry } from '@pascal-app/core';
|
|
3
3
|
import { useRef } from 'react';
|
|
4
4
|
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
5
|
+
import useViewer from '../../../store/use-viewer';
|
|
6
|
+
import { NodeRenderer } from '../node-renderer';
|
|
7
|
+
import { roofDebugMaterials, roofMaterials } from './roof-materials';
|
|
5
8
|
export const RoofRenderer = ({ node }) => {
|
|
6
9
|
const ref = useRef(null);
|
|
7
10
|
useRegistry(node.id, 'roof', ref);
|
|
8
11
|
const handlers = useNodeEvents(node, 'roof');
|
|
9
|
-
|
|
12
|
+
const debugColors = useViewer((s) => s.debugColors);
|
|
13
|
+
return (_jsxs("group", { position: node.position, ref: ref, "rotation-y": node.rotation, visible: node.visible, ...handlers, children: [_jsx("mesh", { castShadow: true, material: debugColors ? roofDebugMaterials : roofMaterials, 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))) })] }));
|
|
10
14
|
};
|
|
@@ -0,0 +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;AAOpE,eAAO,MAAM,mBAAmB,GAAI,UAAU;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,4CAqBtE,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
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 useViewer from '../../../store/use-viewer';
|
|
6
|
+
import { roofDebugMaterials, roofMaterials } from '../roof/roof-materials';
|
|
7
|
+
export const RoofSegmentRenderer = ({ node }) => {
|
|
8
|
+
const ref = useRef(null);
|
|
9
|
+
useRegistry(node.id, 'roof-segment', ref);
|
|
10
|
+
const handlers = useNodeEvents(node, 'roof-segment');
|
|
11
|
+
const debugColors = useViewer((s) => s.debugColors);
|
|
12
|
+
return (_jsx("mesh", { material: debugColors ? roofDebugMaterials : roofMaterials, position: node.position, ref: ref, "rotation-y": node.rotation, visible: node.visible, ...handlers, children: _jsx("boxGeometry", { args: [0, 0, 0] }) }));
|
|
13
|
+
};
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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"}
|
|
@@ -3,11 +3,13 @@ import { useRegistry } from '@pascal-app/core';
|
|
|
3
3
|
import { Suspense, useMemo, useRef } from 'react';
|
|
4
4
|
import { useAssetUrl } from '../../../hooks/use-asset-url';
|
|
5
5
|
import { useGLTFKTX2 } from '../../../hooks/use-gltf-ktx2';
|
|
6
|
+
import useViewer from '../../../store/use-viewer';
|
|
6
7
|
export const ScanRenderer = ({ node }) => {
|
|
8
|
+
const showScans = useViewer((s) => s.showScans);
|
|
7
9
|
const ref = useRef(null);
|
|
8
10
|
useRegistry(node.id, 'scan', ref);
|
|
9
11
|
const resolvedUrl = useAssetUrl(node.url);
|
|
10
|
-
return (_jsx("group", {
|
|
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 }) })) }));
|
|
11
13
|
};
|
|
12
14
|
const ScanModel = ({ url, opacity }) => {
|
|
13
15
|
const gltf = useGLTFKTX2(url);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scene-renderer.d.ts","sourceRoot":"","sources":["../../../src/components/renderers/scene-renderer.tsx"],"names":[],"mappings":"AAKA,eAAO,MAAM,aAAa,+CAUzB,
|
|
1
|
+
{"version":3,"file":"scene-renderer.d.ts","sourceRoot":"","sources":["../../../src/components/renderers/scene-renderer.tsx"],"names":[],"mappings":"AAKA,eAAO,MAAM,aAAa,+CAUzB,CAAA"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
'use client';
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { useScene } from
|
|
4
|
-
import { NodeRenderer } from
|
|
3
|
+
import { useScene } from '@pascal-app/core';
|
|
4
|
+
import { NodeRenderer } from './node-renderer';
|
|
5
5
|
export const SceneRenderer = () => {
|
|
6
6
|
const rootNodes = useScene((state) => state.rootNodeIds);
|
|
7
7
|
return (_jsx("group", { name: "scene-renderer", children: rootNodes.map((nodeId) => (_jsx(NodeRenderer, { nodeId: nodeId }, nodeId))) }));
|
|
@@ -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,EAAe,MAAM,kBAAkB,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,EAAe,MAAM,kBAAkB,CAAA;AA+B7D,eAAO,MAAM,YAAY,GAAI,UAAU;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,mDA2DxD,CAAA"}
|
|
@@ -1,12 +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 { Html } from '@react-three/drei';
|
|
4
3
|
import { useMemo, useRef } from 'react';
|
|
5
4
|
import { BufferGeometry, Float32BufferAttribute, Shape } from 'three';
|
|
6
5
|
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
7
6
|
import { NodeRenderer } from '../node-renderer';
|
|
8
7
|
const Y_OFFSET = 0.01;
|
|
9
|
-
const LINE_HEIGHT = 0.5;
|
|
10
8
|
/**
|
|
11
9
|
* Creates simple line geometry for site boundary
|
|
12
10
|
* Single horizontal line at ground level
|
|
@@ -18,10 +16,10 @@ const createBoundaryLineGeometry = (points) => {
|
|
|
18
16
|
const positions = [];
|
|
19
17
|
// Create a simple line loop at ground level
|
|
20
18
|
for (const [x, z] of points) {
|
|
21
|
-
positions.push(x, Y_OFFSET, z);
|
|
19
|
+
positions.push(x ?? 0, Y_OFFSET, z ?? 0);
|
|
22
20
|
}
|
|
23
21
|
// Close the loop
|
|
24
|
-
positions.push(points[0][0], Y_OFFSET, points[0][1]);
|
|
22
|
+
positions.push(points[0]?.[0] ?? 0, Y_OFFSET, points[0]?.[1] ?? 0);
|
|
25
23
|
geometry.setAttribute('position', new Float32BufferAttribute(positions, 3));
|
|
26
24
|
return geometry;
|
|
27
25
|
};
|
|
@@ -50,22 +48,9 @@ export const SiteRenderer = ({ node }) => {
|
|
|
50
48
|
return null;
|
|
51
49
|
return createBoundaryLineGeometry(node.polygon.points);
|
|
52
50
|
}, [node?.polygon?.points]);
|
|
53
|
-
// Edge distances for labels
|
|
54
|
-
const edges = useMemo(() => {
|
|
55
|
-
const polygon = node?.polygon?.points ?? [];
|
|
56
|
-
if (polygon.length < 2)
|
|
57
|
-
return [];
|
|
58
|
-
return polygon.map(([x1, z1], i) => {
|
|
59
|
-
const [x2, z2] = polygon[(i + 1) % polygon.length];
|
|
60
|
-
const midX = (x1 + x2) / 2;
|
|
61
|
-
const midZ = (z1 + z2) / 2;
|
|
62
|
-
const dist = Math.sqrt((x2 - x1) ** 2 + (z2 - z1) ** 2);
|
|
63
|
-
return { midX, midZ, dist };
|
|
64
|
-
});
|
|
65
|
-
}, [node?.polygon?.points]);
|
|
66
51
|
const handlers = useNodeEvents(node, 'site');
|
|
67
|
-
if (!node
|
|
52
|
+
if (!(node && floorShape && lineGeometry)) {
|
|
68
53
|
return null;
|
|
69
54
|
}
|
|
70
|
-
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))), _jsxs("mesh", { position: [0, Y_OFFSET - 0.005, 0], rotation: [-Math.PI / 2, 0, 0],
|
|
55
|
+
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))), _jsxs("mesh", { position: [0, Y_OFFSET - 0.005, 0], receiveShadow: true, rotation: [-Math.PI / 2, 0, 0], children: [_jsx("shapeGeometry", { args: [floorShape] }), _jsx("shadowMaterial", { opacity: 0.75, transparent: true })] }), _jsx("line", { frustumCulled: false, geometry: lineGeometry, renderOrder: 9, children: _jsx("lineBasicMaterial", { color: "#f59e0b", linewidth: 2, opacity: 0.6, transparent: true }) })] }));
|
|
71
56
|
};
|
|
@@ -6,5 +6,5 @@ export const SlabRenderer = ({ node }) => {
|
|
|
6
6
|
const ref = useRef(null);
|
|
7
7
|
useRegistry(node.id, 'slab', ref);
|
|
8
8
|
const handlers = useNodeEvents(node, 'slab');
|
|
9
|
-
return (_jsxs("mesh", {
|
|
9
|
+
return (_jsxs("mesh", { castShadow: true, receiveShadow: true, ref: ref, ...handlers, visible: node.visible, children: [_jsx("boxGeometry", { args: [0, 0, 0] }), _jsx("meshStandardMaterial", { color: "#e5e5e5" })] }));
|
|
10
10
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wall-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/wall/wall-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,
|
|
1
|
+
{"version":3,"file":"wall-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/wall/wall-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,4CAqBxD,CAAA"}
|
|
@@ -7,5 +7,5 @@ export const WallRenderer = ({ node }) => {
|
|
|
7
7
|
const ref = useRef(null);
|
|
8
8
|
useRegistry(node.id, 'wall', ref);
|
|
9
9
|
const handlers = useNodeEvents(node, 'wall');
|
|
10
|
-
return (_jsxs("mesh", {
|
|
10
|
+
return (_jsxs("mesh", { castShadow: true, 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)))] }));
|
|
11
11
|
};
|
|
@@ -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;AAK/D,eAAO,MAAM,cAAc,GAAI,UAAU;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,
|
|
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;AAK/D,eAAO,MAAM,cAAc,GAAI,UAAU;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,4CAsB5D,CAAA"}
|
|
@@ -6,5 +6,6 @@ export const WindowRenderer = ({ node }) => {
|
|
|
6
6
|
const ref = useRef(null);
|
|
7
7
|
useRegistry(node.id, 'window', ref);
|
|
8
8
|
const handlers = useNodeEvents(node, 'window');
|
|
9
|
-
|
|
9
|
+
const isTransient = !!node.metadata?.isTransient;
|
|
10
|
+
return (_jsxs("mesh", { castShadow: true, position: node.position, receiveShadow: true, ref: ref, rotation: node.rotation, visible: node.visible, ...(isTransient ? {} : handlers), children: [_jsx("boxGeometry", { args: [0, 0, 0] }), _jsx("meshStandardMaterial", { color: "#d1d5db" })] }));
|
|
10
11
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zone-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/zone/zone-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"zone-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/zone/zone-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAyG7D,eAAO,MAAM,YAAY,GAAI,UAAU;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,mDAqJxD,CAAA"}
|
|
@@ -6,6 +6,7 @@ import { BufferGeometry, Color, DoubleSide, Float32BufferAttribute, Shape } from
|
|
|
6
6
|
import { color, float, uniform, uv } from 'three/tsl';
|
|
7
7
|
import { MeshBasicNodeMaterial } from 'three/webgpu';
|
|
8
8
|
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
9
|
+
import { ZONE_LAYER } from '../../../lib/layers';
|
|
9
10
|
const Y_OFFSET = 0.01;
|
|
10
11
|
const WALL_HEIGHT = 2.3;
|
|
11
12
|
/**
|
|
@@ -24,11 +25,11 @@ const createWallGradientMaterial = (zoneColor) => {
|
|
|
24
25
|
colorNode: baseColor,
|
|
25
26
|
opacityNode: finalOpacity,
|
|
26
27
|
side: DoubleSide,
|
|
27
|
-
depthWrite:
|
|
28
|
+
depthWrite: true,
|
|
28
29
|
depthTest: false,
|
|
29
30
|
userData: {
|
|
30
31
|
uOpacity: opacity,
|
|
31
|
-
}
|
|
32
|
+
},
|
|
32
33
|
});
|
|
33
34
|
};
|
|
34
35
|
/**
|
|
@@ -44,7 +45,7 @@ const createFloorMaterial = (zoneColor) => {
|
|
|
44
45
|
side: DoubleSide,
|
|
45
46
|
depthWrite: false,
|
|
46
47
|
depthTest: false,
|
|
47
|
-
userData: { uOpacity: opacity }
|
|
48
|
+
userData: { uOpacity: opacity },
|
|
48
49
|
});
|
|
49
50
|
};
|
|
50
51
|
/**
|
|
@@ -144,18 +145,37 @@ export const ZoneRenderer = ({ node }) => {
|
|
|
144
145
|
return createWallGradientMaterial(node.color);
|
|
145
146
|
}, [node?.color]);
|
|
146
147
|
const handlers = useNodeEvents(node, 'zone');
|
|
147
|
-
if (!node
|
|
148
|
+
if (!(node && floorShape && wallGeometry && floorMaterial && wallMaterial)) {
|
|
148
149
|
return null;
|
|
149
150
|
}
|
|
150
|
-
return (_jsxs("group", { ref: ref, ...handlers, children: [_jsx(Html, { name: "label", position: [centroid[0], 1, centroid[1]], style: {
|
|
151
|
-
pointerEvents: 'none'
|
|
152
|
-
}, zIndexRange: [10, 0], children: _jsx("div", { style: {
|
|
153
|
-
transform: 'translate3d(-50%, -50%, 0)',
|
|
154
|
-
width: 'max-content',
|
|
155
|
-
color: 'white',
|
|
156
|
-
textShadow: `-1px -1px 0 ${node.color}, 1px -1px 0 ${node.color}, -1px 1px 0 ${node.color}, 1px 1px 0 ${node.color}`,
|
|
151
|
+
return (_jsxs("group", { ref: ref, ...handlers, userData: { labelPosition: [centroid[0], 1, centroid[1]] }, children: [_jsx(Html, { name: "label", position: [centroid[0], 1, centroid[1]], style: { pointerEvents: 'none' }, zIndexRange: [10, 0], children: _jsxs("div", { id: `${node.id}-label`, style: {
|
|
157
152
|
display: 'flex',
|
|
158
|
-
|
|
153
|
+
flexDirection: 'column',
|
|
159
154
|
alignItems: 'center',
|
|
160
|
-
|
|
155
|
+
transform: 'translate3d(-50%, -50%, 0)',
|
|
156
|
+
opacity: 0,
|
|
157
|
+
transition: 'opacity 0.3s ease-in-out',
|
|
158
|
+
}, children: [_jsx("div", { style: {
|
|
159
|
+
width: 'max-content',
|
|
160
|
+
color: 'white',
|
|
161
|
+
textShadow: `-1px -1px 0 ${node.color}, 1px -1px 0 ${node.color}, -1px 1px 0 ${node.color}, 1px 1px 0 ${node.color}`,
|
|
162
|
+
textAlign: 'center',
|
|
163
|
+
}, children: _jsx("span", { children: node.name }) }), _jsxs("div", { className: "label-pin", style: {
|
|
164
|
+
display: 'flex',
|
|
165
|
+
flexDirection: 'column',
|
|
166
|
+
alignItems: 'center',
|
|
167
|
+
marginTop: '2px',
|
|
168
|
+
opacity: 0,
|
|
169
|
+
transition: 'opacity 0.5s ease-in-out',
|
|
170
|
+
}, children: [_jsx("div", { style: {
|
|
171
|
+
width: '2px',
|
|
172
|
+
height: '40px',
|
|
173
|
+
backgroundColor: node.color,
|
|
174
|
+
} }), _jsx("div", { style: {
|
|
175
|
+
width: '10px',
|
|
176
|
+
height: '10px',
|
|
177
|
+
borderRadius: '50%',
|
|
178
|
+
backgroundColor: node.color,
|
|
179
|
+
border: '1px solid white',
|
|
180
|
+
} })] })] }) }), _jsx("mesh", { layers: ZONE_LAYER, material: floorMaterial, name: "floor", position: [0, Y_OFFSET, 0], rotation: [-Math.PI / 2, 0, 0], children: _jsx("shapeGeometry", { args: [floorShape] }) }), _jsx("mesh", { geometry: wallGeometry, layers: ZONE_LAYER, material: wallMaterial, name: "walls" })] }));
|
|
161
181
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ground-occluder.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/ground-occluder.tsx"],"names":[],"mappings":"AAMA,eAAO,MAAM,cAAc,+CAqE1B,CAAA"}
|