@pascal-app/viewer 0.6.0 → 0.8.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/error-boundary.d.ts +7 -3
- package/dist/components/error-boundary.d.ts.map +1 -1
- package/dist/components/error-boundary.js +3 -1
- package/dist/components/renderers/ceiling/ceiling-renderer.d.ts.map +1 -1
- package/dist/components/renderers/ceiling/ceiling-renderer.js +19 -3
- package/dist/components/renderers/column/column-renderer.d.ts +5 -0
- package/dist/components/renderers/column/column-renderer.d.ts.map +1 -0
- package/dist/components/renderers/column/column-renderer.js +597 -0
- package/dist/components/renderers/door/door-renderer.d.ts.map +1 -1
- package/dist/components/renderers/door/door-renderer.js +4 -9
- package/dist/components/renderers/fence/fence-renderer.d.ts.map +1 -1
- package/dist/components/renderers/fence/fence-renderer.js +2 -16
- package/dist/components/renderers/guide/guide-renderer.js +1 -1
- package/dist/components/renderers/item/item-renderer.d.ts.map +1 -1
- package/dist/components/renderers/item/item-renderer.js +2 -1
- 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-renderer.d.ts.map +1 -1
- package/dist/components/renderers/roof/roof-renderer.js +1 -13
- package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts.map +1 -1
- package/dist/components/renderers/roof-segment/roof-segment-renderer.js +5 -20
- package/dist/components/renderers/slab/slab-renderer.d.ts.map +1 -1
- package/dist/components/renderers/slab/slab-renderer.js +38 -26
- package/dist/components/renderers/spawn/spawn-renderer.d.ts +5 -0
- package/dist/components/renderers/spawn/spawn-renderer.d.ts.map +1 -0
- package/dist/components/renderers/spawn/spawn-renderer.js +32 -0
- package/dist/components/renderers/stair/stair-renderer.d.ts.map +1 -1
- package/dist/components/renderers/stair/stair-renderer.js +7 -25
- package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts.map +1 -1
- package/dist/components/renderers/stair-segment/stair-segment-renderer.js +4 -2
- package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
- package/dist/components/viewer/frame-limiter.js +5 -1
- package/dist/components/viewer/index.d.ts +3 -0
- package/dist/components/viewer/index.d.ts.map +1 -1
- package/dist/components/viewer/index.js +89 -19
- package/dist/components/viewer/post-processing.d.ts +13 -1
- package/dist/components/viewer/post-processing.d.ts.map +1 -1
- package/dist/components/viewer/post-processing.js +71 -35
- package/dist/components/viewer/selection-manager.d.ts.map +1 -1
- package/dist/components/viewer/selection-manager.js +19 -2
- package/dist/hooks/use-node-events.d.ts +9 -1
- package/dist/hooks/use-node-events.d.ts.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/lib/materials.d.ts +3 -0
- package/dist/lib/materials.d.ts.map +1 -1
- package/dist/lib/materials.js +16 -3
- package/dist/lib/merged-outline-node.d.ts +2 -0
- package/dist/lib/merged-outline-node.d.ts.map +1 -1
- package/dist/lib/merged-outline-node.js +29 -16
- package/dist/store/use-viewer.d.ts +4 -4
- package/dist/store/use-viewer.d.ts.map +1 -1
- package/dist/store/use-viewer.js +2 -2
- package/dist/systems/ceiling/ceiling-system.d.ts +8 -0
- package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -0
- package/dist/systems/ceiling/ceiling-system.js +91 -0
- package/dist/systems/column/column-geometry.d.ts +22 -0
- package/dist/systems/column/column-geometry.d.ts.map +1 -0
- package/dist/systems/column/column-geometry.js +108 -0
- package/dist/systems/door/door-animation-system.d.ts +2 -0
- package/dist/systems/door/door-animation-system.d.ts.map +1 -0
- package/dist/systems/door/door-animation-system.js +53 -0
- package/dist/systems/door/door-system.d.ts +2 -0
- package/dist/systems/door/door-system.d.ts.map +1 -0
- package/dist/systems/door/door-system.js +867 -0
- package/dist/systems/fence/fence-system.d.ts +2 -0
- package/dist/systems/fence/fence-system.d.ts.map +1 -0
- package/dist/systems/fence/fence-system.js +185 -0
- package/dist/systems/guide/guide-system.d.ts.map +1 -1
- package/dist/systems/guide/guide-system.js +1 -26
- package/dist/systems/item/item-system.d.ts +2 -0
- package/dist/systems/item/item-system.d.ts.map +1 -0
- package/dist/systems/item/item-system.js +44 -0
- package/dist/systems/roof/roof-materials.d.ts.map +1 -1
- package/dist/systems/roof/roof-materials.js +18 -1
- package/dist/systems/roof/roof-system.d.ts +16 -0
- package/dist/systems/roof/roof-system.d.ts.map +1 -0
- package/dist/systems/roof/roof-system.js +800 -0
- 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 +8 -0
- package/dist/systems/slab/slab-system.d.ts.map +1 -0
- package/dist/systems/slab/slab-system.js +155 -0
- package/dist/systems/stair/stair-materials.d.ts.map +1 -1
- package/dist/systems/stair/stair-materials.js +25 -2
- package/dist/systems/stair/stair-system.d.ts +2 -0
- package/dist/systems/stair/stair-system.d.ts.map +1 -0
- package/dist/systems/stair/stair-system.js +770 -0
- package/dist/systems/wall/wall-materials.d.ts.map +1 -1
- package/dist/systems/wall/wall-materials.js +2 -2
- package/dist/systems/wall/wall-system.d.ts +11 -0
- package/dist/systems/wall/wall-system.d.ts.map +1 -0
- package/dist/systems/wall/wall-system.js +595 -0
- package/dist/systems/window/window-animation-system.d.ts +2 -0
- package/dist/systems/window/window-animation-system.d.ts.map +1 -0
- package/dist/systems/window/window-animation-system.js +135 -0
- package/dist/systems/window/window-system.d.ts +12 -0
- package/dist/systems/window/window-system.d.ts.map +1 -0
- package/dist/systems/window/window-system.js +1430 -0
- package/package.json +6 -4
- 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
|
@@ -2,25 +2,11 @@ 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 {
|
|
5
|
+
import { 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(() =>
|
|
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
|
-
]);
|
|
9
|
+
const material = useMemo(() => DEFAULT_STAIR_MATERIAL, []);
|
|
24
10
|
useRegistry(node.id, 'fence', ref);
|
|
25
11
|
useLayoutEffect(() => {
|
|
26
12
|
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,
|
|
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 {
|
|
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;
|
|
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,
|
|
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"}
|
|
@@ -21,19 +21,7 @@ export const RoofRenderer = ({ node }) => {
|
|
|
21
21
|
geometry.addGroup(0, 0, 3);
|
|
22
22
|
return geometry;
|
|
23
23
|
}, []);
|
|
24
|
-
const customMaterial = useMemo(() => getRoofMaterialArray(node), [
|
|
25
|
-
node.materialPreset,
|
|
26
|
-
node.material,
|
|
27
|
-
node.material?.preset,
|
|
28
|
-
node.material?.properties,
|
|
29
|
-
node.material?.texture,
|
|
30
|
-
node.topMaterial,
|
|
31
|
-
node.topMaterialPreset,
|
|
32
|
-
node.edgeMaterial,
|
|
33
|
-
node.edgeMaterialPreset,
|
|
34
|
-
node.wallMaterial,
|
|
35
|
-
node.wallMaterialPreset,
|
|
36
|
-
]);
|
|
24
|
+
const customMaterial = useMemo(() => getRoofMaterialArray(node), [node]);
|
|
37
25
|
const material = debugColors ? roofDebugMaterials : customMaterial || roofMaterials;
|
|
38
26
|
useEffect(() => {
|
|
39
27
|
return () => {
|
|
@@ -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,
|
|
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,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useRegistry, useScene } from '@pascal-app/core';
|
|
2
|
+
import { useRegistry, useScene, } from '@pascal-app/core';
|
|
3
3
|
import { useEffect, useMemo, useRef } from 'react';
|
|
4
4
|
import * as THREE from 'three';
|
|
5
5
|
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
@@ -12,7 +12,9 @@ export const RoofSegmentRenderer = ({ node }) => {
|
|
|
12
12
|
useRegistry(node.id, 'roof-segment', ref);
|
|
13
13
|
const handlers = useNodeEvents(node, 'roof-segment');
|
|
14
14
|
const debugColors = useViewer((s) => s.debugColors);
|
|
15
|
-
const parentNode = node.parentId
|
|
15
|
+
const parentNode = node.parentId
|
|
16
|
+
? nodes[node.parentId]
|
|
17
|
+
: undefined;
|
|
16
18
|
const placeholderGeometry = useMemo(() => {
|
|
17
19
|
const geometry = new THREE.BufferGeometry();
|
|
18
20
|
geometry.setAttribute('position', new THREE.Float32BufferAttribute([], 3));
|
|
@@ -27,24 +29,7 @@ export const RoofSegmentRenderer = ({ node }) => {
|
|
|
27
29
|
return null;
|
|
28
30
|
}
|
|
29
31
|
return parentNode ? getRoofMaterialArray(parentNode) : null;
|
|
30
|
-
}, [
|
|
31
|
-
node.materialPreset,
|
|
32
|
-
node.material,
|
|
33
|
-
node.material?.preset,
|
|
34
|
-
node.material?.properties,
|
|
35
|
-
node.material?.texture,
|
|
36
|
-
parentNode?.materialPreset,
|
|
37
|
-
parentNode?.material,
|
|
38
|
-
parentNode?.material?.preset,
|
|
39
|
-
parentNode?.material?.properties,
|
|
40
|
-
parentNode?.material?.texture,
|
|
41
|
-
parentNode?.topMaterial,
|
|
42
|
-
parentNode?.topMaterialPreset,
|
|
43
|
-
parentNode?.edgeMaterial,
|
|
44
|
-
parentNode?.edgeMaterialPreset,
|
|
45
|
-
parentNode?.wallMaterial,
|
|
46
|
-
parentNode?.wallMaterialPreset,
|
|
47
|
-
]);
|
|
32
|
+
}, [node, parentNode]);
|
|
48
33
|
const material = debugColors ? roofDebugMaterials : customMaterial || roofMaterials;
|
|
49
34
|
useEffect(() => {
|
|
50
35
|
return () => {
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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"}
|
|
@@ -4,32 +4,49 @@ import { useEffect, useMemo, useRef } from 'react';
|
|
|
4
4
|
import * as THREE from 'three';
|
|
5
5
|
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
6
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
|
+
}
|
|
7
35
|
export const SlabRenderer = ({ node }) => {
|
|
8
36
|
const ref = useRef(null);
|
|
9
37
|
useRegistry(node.id, 'slab', ref);
|
|
10
38
|
const handlers = useNodeEvents(node, 'slab');
|
|
11
39
|
const material = useMemo(() => {
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
return slabMaterial;
|
|
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
|
+
});
|
|
33
50
|
}, [
|
|
34
51
|
node.material,
|
|
35
52
|
node.material?.preset,
|
|
@@ -37,10 +54,5 @@ export const SlabRenderer = ({ node }) => {
|
|
|
37
54
|
node.material?.texture,
|
|
38
55
|
node.materialPreset,
|
|
39
56
|
]);
|
|
40
|
-
useEffect(() => {
|
|
41
|
-
return () => {
|
|
42
|
-
material.dispose();
|
|
43
|
-
};
|
|
44
|
-
}, [material]);
|
|
45
57
|
return (_jsx("mesh", { castShadow: true, receiveShadow: true, ref: ref, ...handlers, material: material, visible: node.visible, children: _jsx("boxGeometry", { args: [0, 0, 0] }) }));
|
|
46
58
|
};
|
|
@@ -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,EAEL,KAAK,SAAS,EAIf,MAAM,kBAAkB,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"}
|
|
@@ -3,8 +3,8 @@ import { useRegistry, useScene, } from '@pascal-app/core';
|
|
|
3
3
|
import { useEffect, useLayoutEffect, useMemo, useRef } from 'react';
|
|
4
4
|
import * as THREE from 'three';
|
|
5
5
|
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
6
|
-
import { createMaterial, createMaterialFromPresetRef, DEFAULT_STAIR_MATERIAL } from '../../../lib/materials';
|
|
7
|
-
import {
|
|
6
|
+
import { createMaterial, createMaterialFromPresetRef, DEFAULT_STAIR_MATERIAL, } from '../../../lib/materials';
|
|
7
|
+
import { getStairBodyMaterials, getStairRailingMaterial, } from '../../../systems/stair/stair-materials';
|
|
8
8
|
import { NodeRenderer } from '../node-renderer';
|
|
9
9
|
export const StairRenderer = ({ node }) => {
|
|
10
10
|
const ref = useRef(null);
|
|
@@ -29,26 +29,8 @@ export const StairRenderer = ({ node }) => {
|
|
|
29
29
|
node.material?.properties,
|
|
30
30
|
node.material?.texture,
|
|
31
31
|
]);
|
|
32
|
-
const straightBodyMaterials = useMemo(() => getStairBodyMaterials(node), [
|
|
33
|
-
|
|
34
|
-
node.materialPreset,
|
|
35
|
-
node.railingMaterial,
|
|
36
|
-
node.railingMaterialPreset,
|
|
37
|
-
node.sideMaterial,
|
|
38
|
-
node.sideMaterialPreset,
|
|
39
|
-
node.treadMaterial,
|
|
40
|
-
node.treadMaterialPreset,
|
|
41
|
-
]);
|
|
42
|
-
const railingMaterial = useMemo(() => getStairRailingMaterial(node), [
|
|
43
|
-
node.material,
|
|
44
|
-
node.materialPreset,
|
|
45
|
-
node.railingMaterial,
|
|
46
|
-
node.railingMaterialPreset,
|
|
47
|
-
node.sideMaterial,
|
|
48
|
-
node.sideMaterialPreset,
|
|
49
|
-
node.treadMaterial,
|
|
50
|
-
node.treadMaterialPreset,
|
|
51
|
-
]);
|
|
32
|
+
const straightBodyMaterials = useMemo(() => getStairBodyMaterials(node), [node]);
|
|
33
|
+
const railingMaterial = useMemo(() => getStairRailingMaterial(node), [node]);
|
|
52
34
|
const straightPlaceholderGeometry = useMemo(() => {
|
|
53
35
|
const geometry = new THREE.BufferGeometry();
|
|
54
36
|
geometry.setAttribute('position', new THREE.Float32BufferAttribute([], 3));
|
|
@@ -61,7 +43,7 @@ export const StairRenderer = ({ node }) => {
|
|
|
61
43
|
straightPlaceholderGeometry.dispose();
|
|
62
44
|
};
|
|
63
45
|
}, [straightPlaceholderGeometry]);
|
|
64
|
-
return (_jsxs("group", { "position-x": node.position[0], "position-z": node.position[2], ref: ref, "rotation-y": node.rotation, visible: node.visible, ...handlers, children: [isSegmentBasedStair ? (_jsx("mesh", { castShadow: true, geometry: straightPlaceholderGeometry, material: straightBodyMaterials, name: "merged-stair", receiveShadow: true })) : null,
|
|
46
|
+
return (_jsxs("group", { "position-x": node.position[0], "position-z": node.position[2], ref: ref, "rotation-y": node.rotation, visible: node.visible, ...handlers, children: [isSegmentBasedStair ? (_jsx("mesh", { castShadow: true, geometry: straightPlaceholderGeometry, material: straightBodyMaterials, name: "merged-stair", receiveShadow: true })) : null, isSegmentBasedStair ? null : (_jsx(CurvedStairBody, { bodyMaterials: straightBodyMaterials, stair: node })), _jsx(StairRailings, { material: railingMaterial, stair: node }), isSegmentBasedStair ? (_jsx("group", { name: "segments-wrapper", visible: false, children: (node.children ?? []).map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId))) })) : null] }));
|
|
65
47
|
};
|
|
66
48
|
function StairRailings({ stair, material }) {
|
|
67
49
|
const nodes = useScene((state) => state.nodes);
|
|
@@ -125,7 +107,7 @@ function StairRailings({ stair, material }) {
|
|
|
125
107
|
return (_jsxs("group", { children: [_jsx(RailSegment, { end: [nextPoint[2], nextPoint[1] + railHeight, nextPoint[0]], material: material, radius: railRadius, start: [point[2], point[1] + railHeight, point[0]] }), _jsx(RailSegment, { end: [nextPoint[2], nextPoint[1] + midRailHeight, nextPoint[0]], material: material, radius: railRadius * 0.8, start: [point[2], point[1] + midRailHeight, point[0]] })] }, `${segmentPath.layout.segment.id}-${sidePath.side}-rail-${pointIndex}`));
|
|
126
108
|
})] }, `${segmentPath.layout.segment.id}-${sidePath.side}-${sideIndex}`))) }, `${segmentPath.layout.segment.id}-railing`))), railPaths.slice(1).map((segmentPath, index) => {
|
|
127
109
|
const previousPath = railPaths[index];
|
|
128
|
-
if (!previousPath
|
|
110
|
+
if (!(previousPath && segmentPath.connectFromPrevious))
|
|
129
111
|
return null;
|
|
130
112
|
if (previousPath.layout.segment.segmentType === 'landing')
|
|
131
113
|
return null;
|
|
@@ -209,7 +191,7 @@ function CurvedStairBody({ stair, bodyMaterials, }) {
|
|
|
209
191
|
const spiralLandingThickness = isSpiral && (stair.topLandingMode ?? 'none') === 'integrated'
|
|
210
192
|
? Math.max(0.02, totalRise - spiralLastStepTop)
|
|
211
193
|
: 0;
|
|
212
|
-
return (_jsxs("group", { name: isSpiral ? 'spiral-stair' : 'curved-stair', children: [isSpiral && (stair.showCenterColumn ?? true) ? (_jsx("mesh", { castShadow: true,
|
|
194
|
+
return (_jsxs("group", { name: isSpiral ? 'spiral-stair' : 'curved-stair', children: [isSpiral && (stair.showCenterColumn ?? true) ? (_jsx("mesh", { castShadow: true, material: sideMaterial, name: "stair-side", position: [0, spiralColumnHeight / 2, 0], receiveShadow: true, children: _jsx("cylinderGeometry", { args: [spiralColumnRadius, spiralColumnRadius, spiralColumnHeight, 10] }) })) : null, Array.from({ length: stepCount }).map((_, index) => {
|
|
213
195
|
const currentHeight = stepHeight * (index + 1);
|
|
214
196
|
const actualStepHeight = isSpiral
|
|
215
197
|
? thickness
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stair-segment-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/stair-segment/stair-segment-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"stair-segment-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/stair-segment/stair-segment-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,gBAAgB,EAGtB,MAAM,kBAAkB,CAAA;AAMzB,eAAO,MAAM,oBAAoB,GAAI,UAAU;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,4CA6DxE,CAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useRegistry, useScene } from '@pascal-app/core';
|
|
2
|
+
import { useRegistry, useScene, } from '@pascal-app/core';
|
|
3
3
|
import { useEffect, useLayoutEffect, useMemo, useRef } from 'react';
|
|
4
4
|
import * as THREE from 'three';
|
|
5
5
|
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
@@ -12,7 +12,9 @@ export const StairSegmentRenderer = ({ node }) => {
|
|
|
12
12
|
useScene.getState().markDirty(node.id);
|
|
13
13
|
}, [node.id]);
|
|
14
14
|
const handlers = useNodeEvents(node, 'stair-segment');
|
|
15
|
-
const parentNode = node.parentId
|
|
15
|
+
const parentNode = node.parentId
|
|
16
|
+
? nodes[node.parentId]
|
|
17
|
+
: undefined;
|
|
16
18
|
const material = useMemo(() => {
|
|
17
19
|
return getStraightStairSegmentBodyMaterials(node, parentNode);
|
|
18
20
|
}, [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frame-limiter.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/frame-limiter.tsx"],"names":[],"mappings":"AAGA,KAAK,iBAAiB,GAAG;IACvB,GAAG,CAAC,EAAE,MAAM,CAAA;CACb,CAAA;AAED,QAAA,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,
|
|
1
|
+
{"version":3,"file":"frame-limiter.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/frame-limiter.tsx"],"names":[],"mappings":"AAGA,KAAK,iBAAiB,GAAG;IACvB,GAAG,CAAC,EAAE,MAAM,CAAA;CACb,CAAA;AAED,QAAA,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAiC7C,CAAA;AAED,eAAe,YAAY,CAAA"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { useThree } from '@react-three/fiber';
|
|
2
2
|
import { useLayoutEffect } from 'react';
|
|
3
3
|
const FrameLimiter = ({ fps = 50 }) => {
|
|
4
|
-
const { advance, set, frameloop: initFrameloop } = useThree();
|
|
4
|
+
const { advance, set, frameloop: initFrameloop, scene, clock } = useThree();
|
|
5
|
+
const renderer = useThree((state) => state.gl);
|
|
5
6
|
useLayoutEffect(() => {
|
|
6
7
|
let elapsed = 0;
|
|
7
8
|
let then = 0;
|
|
@@ -17,8 +18,11 @@ const FrameLimiter = ({ fps = 50 }) => {
|
|
|
17
18
|
then = t - (elapsed % interval);
|
|
18
19
|
}
|
|
19
20
|
}
|
|
21
|
+
// Set frameloop to never, it will shut down the default render loop
|
|
20
22
|
set({ frameloop: 'never' });
|
|
23
|
+
// Kick off custom render loop
|
|
21
24
|
raf = requestAnimationFrame(tick);
|
|
25
|
+
// Restore initial setting
|
|
22
26
|
return () => {
|
|
23
27
|
if (raf) {
|
|
24
28
|
cancelAnimationFrame(raf);
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { type ThreeToJSXElements } from '@react-three/fiber';
|
|
2
2
|
import * as THREE from 'three/webgpu';
|
|
3
|
+
import { type HoverStyles } from './post-processing';
|
|
3
4
|
declare module '@react-three/fiber' {
|
|
4
5
|
interface ThreeElements extends ThreeToJSXElements<typeof THREE> {
|
|
5
6
|
}
|
|
6
7
|
}
|
|
7
8
|
interface ViewerProps {
|
|
8
9
|
children?: React.ReactNode;
|
|
10
|
+
hoverStyles?: HoverStyles;
|
|
9
11
|
selectionManager?: 'default' | 'custom';
|
|
10
12
|
perf?: boolean;
|
|
13
|
+
useBvh?: boolean;
|
|
11
14
|
}
|
|
12
15
|
declare const Viewer: React.FC<ViewerProps>;
|
|
13
16
|
export default Viewer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/index.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAkB,KAAK,kBAAkB,EAAsB,MAAM,oBAAoB,CAAA;AAEhG,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAwBrC,OAAuB,EAAwB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAA;AA+B1F,OAAO,QAAQ,oBAAoB,CAAC;IAClC,UAAU,aAAc,SAAQ,kBAAkB,CAAC,OAAO,KAAK,CAAC;KAAG;CACpE;AAkFD,UAAU,WAAW;IACnB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC1B,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,gBAAgB,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;IACvC,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,QAAA,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CAwGjC,CAAA;AASD,eAAe,MAAM,CAAA"}
|
|
@@ -1,22 +1,33 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { CeilingSystem, DoorSystem, FenceSystem, ItemSystem, RoofSystem, SlabSystem, StairSystem, WallSystem, WindowSystem, } from '@pascal-app/core';
|
|
4
3
|
import { Bvh } from '@react-three/drei';
|
|
5
4
|
import { Canvas, extend, useFrame, useThree } from '@react-three/fiber';
|
|
6
5
|
import { useEffect, useMemo, useRef } from 'react';
|
|
7
6
|
import * as THREE from 'three/webgpu';
|
|
8
7
|
import useViewer from '../../store/use-viewer';
|
|
8
|
+
import { CeilingSystem } from '../../systems/ceiling/ceiling-system';
|
|
9
|
+
import { DoorAnimationSystem } from '../../systems/door/door-animation-system';
|
|
10
|
+
import { DoorSystem } from '../../systems/door/door-system';
|
|
11
|
+
import { FenceSystem } from '../../systems/fence/fence-system';
|
|
9
12
|
import { GuideSystem } from '../../systems/guide/guide-system';
|
|
13
|
+
import { ItemSystem } from '../../systems/item/item-system';
|
|
10
14
|
import { ItemLightSystem } from '../../systems/item-light/item-light-system';
|
|
11
15
|
import { LevelSystem } from '../../systems/level/level-system';
|
|
16
|
+
import { RoofSystem } from '../../systems/roof/roof-system';
|
|
12
17
|
import { ScanSystem } from '../../systems/scan/scan-system';
|
|
18
|
+
import { SlabSystem } from '../../systems/slab/slab-system';
|
|
19
|
+
import { StairSystem } from '../../systems/stair/stair-system';
|
|
13
20
|
import { WallCutout } from '../../systems/wall/wall-cutout';
|
|
21
|
+
import { WallSystem } from '../../systems/wall/wall-system';
|
|
22
|
+
import { WindowAnimationSystem } from '../../systems/window/window-animation-system';
|
|
23
|
+
import { WindowSystem } from '../../systems/window/window-system';
|
|
14
24
|
import { ZoneSystem } from '../../systems/zone/zone-system';
|
|
25
|
+
import { ErrorBoundary } from '../error-boundary';
|
|
15
26
|
import { SceneRenderer } from '../renderers/scene-renderer';
|
|
16
27
|
import FrameLimiter from './frame-limiter';
|
|
17
28
|
import { Lights } from './lights';
|
|
18
29
|
import { PerfMonitor } from './perf-monitor';
|
|
19
|
-
import PostProcessing from './post-processing';
|
|
30
|
+
import PostProcessing, { DEFAULT_HOVER_STYLES } from './post-processing';
|
|
20
31
|
import { SelectionManager } from './selection-manager';
|
|
21
32
|
import { ViewerCamera } from './viewer-camera';
|
|
22
33
|
function AnimatedBackground({ isDark }) {
|
|
@@ -41,40 +52,99 @@ function AnimatedBackground({ isDark }) {
|
|
|
41
52
|
return null;
|
|
42
53
|
}
|
|
43
54
|
extend(THREE);
|
|
55
|
+
// R3F's <Canvas> useLayoutEffect has no deps, so any re-render (theme switch,
|
|
56
|
+
// parent re-render, StrictMode double-mount) re-invokes `configure()`. With a
|
|
57
|
+
// sync `gl` factory that's harmless — the renderer is created once and reused.
|
|
58
|
+
// With an async factory (WebGPURenderer needs `await init()`), two configure
|
|
59
|
+
// calls can race: both see `state.gl == null` and both create a renderer. The
|
|
60
|
+
// first to resolve gets `setSize`/`setDpr` called on it; the second overwrites
|
|
61
|
+
// `state.gl` but R3F's store already holds the new size/dpr, so the new
|
|
62
|
+
// renderer is never resized and stays at the canvas's 300×150 default.
|
|
63
|
+
//
|
|
64
|
+
// Caching by canvas guarantees both branches return the same instance, so
|
|
65
|
+
// "duplicate" configure calls become no-ops on an already-sized renderer.
|
|
66
|
+
// We cache the in-flight Promise (not just the resolved renderer) so two
|
|
67
|
+
// concurrent configure() calls await the same init instead of creating two
|
|
68
|
+
// renderers in parallel and only caching the second.
|
|
69
|
+
const WEBGPU_RENDERER_CACHE = new WeakMap();
|
|
44
70
|
function GPUDeviceWatcher() {
|
|
45
71
|
const gl = useThree((s) => s.gl);
|
|
46
72
|
useEffect(() => {
|
|
47
73
|
const backend = gl.backend;
|
|
48
74
|
const device = backend?.device;
|
|
49
|
-
if (!device)
|
|
75
|
+
if (!device) {
|
|
76
|
+
console.warn('[viewer] No WebGPU device on backend — running on a fallback renderer.', {
|
|
77
|
+
backend: backend?.constructor?.name ?? 'unknown',
|
|
78
|
+
rendererType: gl.constructor?.name ?? 'unknown',
|
|
79
|
+
});
|
|
50
80
|
return;
|
|
81
|
+
}
|
|
82
|
+
console.log('[viewer] WebGPU device ready', {
|
|
83
|
+
label: device.label,
|
|
84
|
+
features: Array.from(device.features ?? []),
|
|
85
|
+
});
|
|
51
86
|
device.lost.then((info) => {
|
|
52
|
-
console.error(`[viewer] WebGPU device lost: reason="${info.reason
|
|
87
|
+
console.error(`[viewer] WebGPU device lost: reason="${info.reason}", message="${info.message}". ` +
|
|
53
88
|
'The page must be reloaded to recover the GPU context.');
|
|
54
89
|
});
|
|
90
|
+
// Uncaptured errors are normally silent (only console-warned by Chrome at
|
|
91
|
+
// best). Pipe them to console.error so silent mobile crashes show up.
|
|
92
|
+
const onUncapturedError = (event) => {
|
|
93
|
+
console.error('[viewer] WebGPU uncaptured error:', event.error.message, event.error);
|
|
94
|
+
};
|
|
95
|
+
device.addEventListener('uncapturederror', onUncapturedError);
|
|
96
|
+
return () => {
|
|
97
|
+
device.removeEventListener('uncapturederror', onUncapturedError);
|
|
98
|
+
};
|
|
55
99
|
}, [gl]);
|
|
56
100
|
return null;
|
|
57
101
|
}
|
|
58
|
-
const Viewer = ({ children, selectionManager = 'default', perf = false, }) => {
|
|
102
|
+
const Viewer = ({ children, hoverStyles = DEFAULT_HOVER_STYLES, selectionManager = 'default', perf = false, useBvh = true, }) => {
|
|
59
103
|
const theme = useViewer((state) => state.theme);
|
|
60
|
-
return (_jsxs(Canvas, { camera: { position: [50, 50, 50], fov: 50 }, className: `transition-colors duration-700 ${theme === 'dark' ? 'bg-[#1f2433]' : 'bg-[#fafafa]'}`, dpr: [1, 1.5], frameloop: "never", gl:
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
104
|
+
return (_jsxs(Canvas, { camera: { position: [50, 50, 50], fov: 50 }, className: `transition-colors duration-700 ${theme === 'dark' ? 'bg-[#1f2433]' : 'bg-[#fafafa]'}`, dpr: [1, 1.5], frameloop: "never", gl: ((props) => {
|
|
105
|
+
const canvas = props.canvas;
|
|
106
|
+
const cached = canvas ? WEBGPU_RENDERER_CACHE.get(canvas) : undefined;
|
|
107
|
+
if (cached)
|
|
108
|
+
return cached;
|
|
109
|
+
// Surface the env we're about to ask WebGPU for — catches "no
|
|
110
|
+
// navigator.gpu" / "adapter request failed" silently failing in
|
|
111
|
+
// mobile WebViews where WebGPU is gated behind flags.
|
|
112
|
+
const hasGpu = typeof navigator !== 'undefined' && 'gpu' in navigator;
|
|
113
|
+
console.log('[viewer] Creating WebGPURenderer', {
|
|
114
|
+
hasNavigatorGPU: hasGpu,
|
|
115
|
+
ua: typeof navigator !== 'undefined' ? navigator.userAgent : 'n/a',
|
|
116
|
+
});
|
|
117
|
+
const promise = (async () => {
|
|
118
|
+
try {
|
|
119
|
+
const renderer = new THREE.WebGPURenderer(props);
|
|
120
|
+
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
|
121
|
+
renderer.toneMappingExposure = 0.9;
|
|
122
|
+
await renderer.init();
|
|
123
|
+
console.log('[viewer] WebGPURenderer ready', {
|
|
124
|
+
backend: renderer.backend?.constructor?.name,
|
|
125
|
+
isWebGPU: renderer.isWebGPURenderer === true,
|
|
126
|
+
});
|
|
127
|
+
return renderer;
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
// Drop the failed promise from the cache so a future Canvas
|
|
131
|
+
// mount on the same DOM can retry instead of inheriting the
|
|
132
|
+
// rejection forever.
|
|
133
|
+
if (canvas)
|
|
134
|
+
WEBGPU_RENDERER_CACHE.delete(canvas);
|
|
135
|
+
console.error('[viewer] WebGPURenderer init failed', err);
|
|
136
|
+
throw err;
|
|
137
|
+
}
|
|
138
|
+
})();
|
|
139
|
+
if (canvas)
|
|
140
|
+
WEBGPU_RENDERER_CACHE.set(canvas, promise);
|
|
141
|
+
return promise;
|
|
142
|
+
}), resize: {
|
|
73
143
|
debounce: 100,
|
|
74
144
|
}, shadows: {
|
|
75
145
|
type: THREE.PCFShadowMap,
|
|
76
146
|
enabled: true,
|
|
77
|
-
}, children: [_jsx(FrameLimiter, { fps: 50 }), _jsx(ViewerCamera, {}), _jsx(Lights, {}), _jsx(Bvh, { children: _jsx(SceneRenderer, {}) }), _jsx(LevelSystem, {}), _jsx(GuideSystem, {}), _jsx(ScanSystem, {}), _jsx(WallCutout, {}), _jsx(CeilingSystem, {}), _jsx(DoorSystem, {}), _jsx(FenceSystem, {}), _jsx(ItemSystem, {}), _jsx(RoofSystem, {}), _jsx(SlabSystem, {}), _jsx(StairSystem, {}), _jsx(WallSystem, {}), _jsx(WindowSystem, {}), _jsx(ZoneSystem, {}), _jsx(PostProcessing, {
|
|
147
|
+
}, children: [_jsx(FrameLimiter, { fps: 50 }), _jsx(ViewerCamera, {}), _jsx(GPUDeviceWatcher, {}), _jsxs(ErrorBoundary, { fallback: null, scope: "viewer-scene", children: [_jsx(Lights, {}), useBvh ? (_jsx(Bvh, { children: _jsx(SceneRenderer, {}) })) : (_jsx(SceneRenderer, {})), _jsx(LevelSystem, {}), _jsx(GuideSystem, {}), _jsx(ScanSystem, {}), _jsx(WallCutout, {}), _jsx(CeilingSystem, {}), _jsx(DoorAnimationSystem, {}), _jsx(WindowAnimationSystem, {}), _jsx(DoorSystem, {}), _jsx(FenceSystem, {}), _jsx(ItemSystem, {}), _jsx(RoofSystem, {}), _jsx(SlabSystem, {}), _jsx(StairSystem, {}), _jsx(WallSystem, {}), _jsx(WindowSystem, {}), _jsx(ZoneSystem, {}), _jsx(PostProcessing, { hoverStyles: hoverStyles }), _jsx(ItemLightSystem, {}), selectionManager === 'default' && _jsx(SelectionManager, {}), perf && _jsx(PerfMonitor, {}), children] })] }));
|
|
78
148
|
};
|
|
79
149
|
const DebugRenderer = () => {
|
|
80
150
|
useFrame(({ gl, scene, camera }) => {
|