@pascal-app/viewer 0.3.0 → 0.3.2
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 +32 -28
- package/dist/components/renderers/door/door-renderer.d.ts.map +1 -1
- package/dist/components/renderers/door/door-renderer.js +10 -3
- package/dist/components/renderers/roof/roof-renderer.d.ts.map +1 -1
- package/dist/components/renderers/roof/roof-renderer.js +10 -2
- package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts.map +1 -1
- package/dist/components/renderers/roof-segment/roof-segment-renderer.js +10 -2
- package/dist/components/renderers/slab/slab-renderer.d.ts.map +1 -1
- package/dist/components/renderers/slab/slab-renderer.js +10 -3
- package/dist/components/renderers/wall/wall-renderer.d.ts.map +1 -1
- package/dist/components/renderers/wall/wall-renderer.js +9 -3
- package/dist/components/renderers/window/window-renderer.d.ts.map +1 -1
- package/dist/components/renderers/window/window-renderer.js +10 -3
- package/dist/components/viewer/index.d.ts.map +1 -1
- package/dist/components/viewer/index.js +4 -2
- package/dist/components/viewer/post-processing.d.ts.map +1 -1
- package/dist/components/viewer/post-processing.js +46 -63
- package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/lib/materials.d.ts +13 -0
- package/dist/lib/materials.d.ts.map +1 -0
- package/dist/lib/materials.js +58 -0
- package/dist/store/use-viewer.d.ts +2 -2
- package/dist/store/use-viewer.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout.js +127 -57
- package/package.json +1 -1
- package/dist/hooks/use-grid-events.d.ts +0 -12
- package/dist/hooks/use-grid-events.d.ts.map +0 -1
- package/dist/hooks/use-grid-events.js +0 -33
|
@@ -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;AAmChE,eAAO,MAAM,eAAe,GAAI,UAAU;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,4CAoC9D,CAAA"}
|
|
@@ -1,45 +1,49 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useRegistry } from '@pascal-app/core';
|
|
3
|
-
import { useRef } from 'react';
|
|
3
|
+
import { useMemo, useRef } from 'react';
|
|
4
4
|
import { float, mix, positionWorld, smoothstep } from 'three/tsl';
|
|
5
5
|
import { BackSide, FrontSide, MeshBasicNodeMaterial } from 'three/webgpu';
|
|
6
6
|
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
7
|
+
import { DEFAULT_CEILING_MATERIAL } from '../../../lib/materials';
|
|
7
8
|
import { NodeRenderer } from '../node-renderer';
|
|
8
|
-
|
|
9
|
-
// - Back face (looking up at ceiling from below): solid
|
|
10
|
-
// - Front face (looking down at ceiling from above): 30% opacity
|
|
11
|
-
const ceilingTopMaterial = new MeshBasicNodeMaterial({
|
|
12
|
-
color: 0xb5_a7_8d,
|
|
13
|
-
transparent: true,
|
|
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,
|
|
23
|
-
});
|
|
24
|
-
// Create grid pattern based on local position
|
|
25
|
-
const gridScale = 5; // Grid cells per meter (1 = 1m grid)
|
|
9
|
+
const gridScale = 5;
|
|
26
10
|
const gridX = positionWorld.x.mul(gridScale).fract();
|
|
27
11
|
const gridY = positionWorld.z.mul(gridScale).fract();
|
|
28
|
-
|
|
29
|
-
const lineWidth = 0.05; // Width of grid lines (0-1 range within cell)
|
|
30
|
-
// Create visible lines at edges (near 0 and near 1)
|
|
12
|
+
const lineWidth = 0.05;
|
|
31
13
|
const lineX = smoothstep(lineWidth, 0, gridX).add(smoothstep(1.0 - lineWidth, 1.0, gridX));
|
|
32
14
|
const lineY = smoothstep(lineWidth, 0, gridY).add(smoothstep(1.0 - lineWidth, 1.0, gridY));
|
|
33
|
-
// Combine: if either X or Y is a line, show the line
|
|
34
15
|
const gridPattern = lineX.max(lineY);
|
|
35
|
-
// Grid lines at 0.6 opacity, spaces at 0.2 opacity
|
|
36
16
|
const gridOpacity = mix(float(0.2), float(0.6), gridPattern);
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
17
|
+
function createCeilingMaterials(color = '#999999') {
|
|
18
|
+
const topMaterial = new MeshBasicNodeMaterial({
|
|
19
|
+
color,
|
|
20
|
+
transparent: true,
|
|
21
|
+
depthWrite: false,
|
|
22
|
+
side: FrontSide,
|
|
23
|
+
});
|
|
24
|
+
topMaterial.opacityNode = gridOpacity;
|
|
25
|
+
const bottomMaterial = new MeshBasicNodeMaterial({
|
|
26
|
+
color,
|
|
27
|
+
transparent: true,
|
|
28
|
+
side: BackSide,
|
|
29
|
+
});
|
|
30
|
+
return { topMaterial, bottomMaterial };
|
|
31
|
+
}
|
|
40
32
|
export const CeilingRenderer = ({ node }) => {
|
|
41
33
|
const ref = useRef(null);
|
|
42
34
|
useRegistry(node.id, 'ceiling', ref);
|
|
43
35
|
const handlers = useNodeEvents(node, 'ceiling');
|
|
44
|
-
|
|
36
|
+
const materials = useMemo(() => {
|
|
37
|
+
const mat = node.material;
|
|
38
|
+
if (mat) {
|
|
39
|
+
const props = mat.properties;
|
|
40
|
+
const color = props?.color || '#999999';
|
|
41
|
+
return createCeilingMaterials(color);
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
topMaterial: createCeilingMaterials().topMaterial,
|
|
45
|
+
bottomMaterial: DEFAULT_CEILING_MATERIAL,
|
|
46
|
+
};
|
|
47
|
+
}, [node.material, node.material?.preset, node.material?.properties, node.material?.texture]);
|
|
48
|
+
return (_jsxs("mesh", { material: materials.bottomMaterial, ref: ref, children: [_jsx("boxGeometry", { args: [0, 0, 0] }), _jsx("mesh", { material: materials.topMaterial, name: "ceiling-grid", ...handlers, scale: 0, visible: false, children: _jsx("boxGeometry", { args: [0, 0, 0] }) }), node.children.map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId)))] }));
|
|
45
49
|
};
|
|
@@ -1 +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;
|
|
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;AAM7D,eAAO,MAAM,YAAY,GAAI,UAAU;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,4CA2BxD,CAAA"}
|
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useRegistry } from '@pascal-app/core';
|
|
3
|
-
import { useRef } from 'react';
|
|
3
|
+
import { useMemo, useRef } from 'react';
|
|
4
4
|
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
5
|
+
import { createMaterial, DEFAULT_DOOR_MATERIAL } from '../../../lib/materials';
|
|
5
6
|
export const DoorRenderer = ({ node }) => {
|
|
6
7
|
const ref = useRef(null);
|
|
7
8
|
useRegistry(node.id, 'door', ref);
|
|
8
9
|
const handlers = useNodeEvents(node, 'door');
|
|
9
10
|
const isTransient = !!node.metadata?.isTransient;
|
|
10
|
-
|
|
11
|
+
const material = useMemo(() => {
|
|
12
|
+
const mat = node.material;
|
|
13
|
+
if (!mat)
|
|
14
|
+
return DEFAULT_DOOR_MATERIAL;
|
|
15
|
+
return createMaterial(mat);
|
|
16
|
+
}, [node.material, node.material?.preset, node.material?.properties, node.material?.texture]);
|
|
17
|
+
return (_jsx("mesh", { castShadow: true, material: material, position: node.position, receiveShadow: true, ref: ref, rotation: node.rotation, visible: node.visible, ...(isTransient ? {} : handlers), children: _jsx("boxGeometry", { args: [0, 0, 0] }) }));
|
|
11
18
|
};
|
|
@@ -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;AAS7D,eAAO,MAAM,YAAY,GAAI,UAAU;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,4CAkCxD,CAAA"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useRegistry } from '@pascal-app/core';
|
|
3
|
-
import { useRef } from 'react';
|
|
3
|
+
import { useMemo, useRef } from 'react';
|
|
4
4
|
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
5
|
+
import { createMaterial } from '../../../lib/materials';
|
|
5
6
|
import useViewer from '../../../store/use-viewer';
|
|
6
7
|
import { NodeRenderer } from '../node-renderer';
|
|
7
8
|
import { roofDebugMaterials, roofMaterials } from './roof-materials';
|
|
@@ -10,5 +11,12 @@ export const RoofRenderer = ({ node }) => {
|
|
|
10
11
|
useRegistry(node.id, 'roof', ref);
|
|
11
12
|
const handlers = useNodeEvents(node, 'roof');
|
|
12
13
|
const debugColors = useViewer((s) => s.debugColors);
|
|
13
|
-
|
|
14
|
+
const customMaterial = useMemo(() => {
|
|
15
|
+
const mat = node.material;
|
|
16
|
+
if (!mat)
|
|
17
|
+
return null;
|
|
18
|
+
return createMaterial(mat);
|
|
19
|
+
}, [node.material, node.material?.preset, node.material?.properties, node.material?.texture]);
|
|
20
|
+
const material = debugColors ? roofDebugMaterials : customMaterial || roofMaterials;
|
|
21
|
+
return (_jsxs("group", { position: node.position, ref: ref, "rotation-y": node.rotation, visible: node.visible, ...handlers, children: [_jsx("mesh", { castShadow: true, material: material, name: "merged-roof", receiveShadow: true, children: _jsx("boxGeometry", { args: [0, 0, 0] }) }), _jsx("group", { name: "segments-wrapper", visible: false, children: (node.children ?? []).map((childId) => (_jsx(NodeRenderer, { nodeId: childId }, childId))) })] }));
|
|
14
22
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"roof-segment-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/roof-segment/roof-segment-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAe,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"roof-segment-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/roof-segment/roof-segment-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAe,MAAM,kBAAkB,CAAA;AAQpE,eAAO,MAAM,mBAAmB,GAAI,UAAU;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,4CA6BtE,CAAA"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useRegistry } from '@pascal-app/core';
|
|
3
|
-
import { useRef } from 'react';
|
|
3
|
+
import { useMemo, useRef } from 'react';
|
|
4
4
|
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
5
|
+
import { createMaterial } from '../../../lib/materials';
|
|
5
6
|
import useViewer from '../../../store/use-viewer';
|
|
6
7
|
import { roofDebugMaterials, roofMaterials } from '../roof/roof-materials';
|
|
7
8
|
export const RoofSegmentRenderer = ({ node }) => {
|
|
@@ -9,5 +10,12 @@ export const RoofSegmentRenderer = ({ node }) => {
|
|
|
9
10
|
useRegistry(node.id, 'roof-segment', ref);
|
|
10
11
|
const handlers = useNodeEvents(node, 'roof-segment');
|
|
11
12
|
const debugColors = useViewer((s) => s.debugColors);
|
|
12
|
-
|
|
13
|
+
const customMaterial = useMemo(() => {
|
|
14
|
+
const mat = node.material;
|
|
15
|
+
if (!mat)
|
|
16
|
+
return null;
|
|
17
|
+
return createMaterial(mat);
|
|
18
|
+
}, [node.material, node.material?.preset, node.material?.properties, node.material?.texture]);
|
|
19
|
+
const material = debugColors ? roofDebugMaterials : customMaterial || roofMaterials;
|
|
20
|
+
return (_jsx("mesh", { material: material, position: node.position, ref: ref, "rotation-y": node.rotation, visible: node.visible, ...handlers, children: _jsx("boxGeometry", { args: [0, 0, 0] }) }));
|
|
13
21
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slab-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/slab/slab-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAe,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"slab-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/slab/slab-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAe,MAAM,kBAAkB,CAAA;AAM7D,eAAO,MAAM,YAAY,GAAI,UAAU;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,4CAyBxD,CAAA"}
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useRegistry } from '@pascal-app/core';
|
|
3
|
-
import { useRef } from 'react';
|
|
3
|
+
import { useMemo, useRef } from 'react';
|
|
4
4
|
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
5
|
+
import { createMaterial, DEFAULT_SLAB_MATERIAL } from '../../../lib/materials';
|
|
5
6
|
export const SlabRenderer = ({ node }) => {
|
|
6
7
|
const ref = useRef(null);
|
|
7
8
|
useRegistry(node.id, 'slab', ref);
|
|
8
9
|
const handlers = useNodeEvents(node, 'slab');
|
|
9
|
-
|
|
10
|
+
const material = useMemo(() => {
|
|
11
|
+
const mat = node.material;
|
|
12
|
+
if (!mat)
|
|
13
|
+
return DEFAULT_SLAB_MATERIAL;
|
|
14
|
+
return createMaterial(mat);
|
|
15
|
+
}, [node.material, node.material?.preset, node.material?.properties, node.material?.texture]);
|
|
16
|
+
return (_jsx("mesh", { castShadow: true, receiveShadow: true, ref: ref, ...handlers, visible: node.visible, material: material, children: _jsx("boxGeometry", { args: [0, 0, 0] }) }));
|
|
10
17
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wall-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/wall/wall-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"wall-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/wall/wall-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAOvE,eAAO,MAAM,YAAY,GAAI,UAAU;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,4CA6BxD,CAAA"}
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useRegistry, useScene } from '@pascal-app/core';
|
|
3
|
-
import { useLayoutEffect, useRef } from 'react';
|
|
3
|
+
import { useLayoutEffect, useMemo, useRef } from 'react';
|
|
4
4
|
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
5
|
+
import { createMaterial, DEFAULT_WALL_MATERIAL } from '../../../lib/materials';
|
|
5
6
|
import { NodeRenderer } from '../node-renderer';
|
|
6
7
|
export const WallRenderer = ({ node }) => {
|
|
7
8
|
const ref = useRef(null);
|
|
8
9
|
useRegistry(node.id, 'wall', ref);
|
|
9
|
-
// Mark dirty on mount so WallSystem rebuilds geometry when wall (re)appears
|
|
10
10
|
useLayoutEffect(() => {
|
|
11
11
|
useScene.getState().markDirty(node.id);
|
|
12
12
|
}, [node.id]);
|
|
13
13
|
const handlers = useNodeEvents(node, 'wall');
|
|
14
|
-
|
|
14
|
+
const material = useMemo(() => {
|
|
15
|
+
const mat = node.material;
|
|
16
|
+
if (!mat)
|
|
17
|
+
return DEFAULT_WALL_MATERIAL;
|
|
18
|
+
return createMaterial(mat);
|
|
19
|
+
}, [node.material, node.material?.preset, node.material?.properties, node.material?.texture]);
|
|
20
|
+
return (_jsxs("mesh", { castShadow: true, receiveShadow: true, ref: ref, visible: node.visible, material: material, 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)))] }));
|
|
15
21
|
};
|
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"window-renderer.d.ts","sourceRoot":"","sources":["../../../../src/components/renderers/window/window-renderer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAM/D,eAAO,MAAM,cAAc,GAAI,UAAU;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,4CA2B5D,CAAA"}
|
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useRegistry } from '@pascal-app/core';
|
|
3
|
-
import { useRef } from 'react';
|
|
3
|
+
import { useMemo, useRef } from 'react';
|
|
4
4
|
import { useNodeEvents } from '../../../hooks/use-node-events';
|
|
5
|
+
import { createMaterial, DEFAULT_WINDOW_MATERIAL } from '../../../lib/materials';
|
|
5
6
|
export const WindowRenderer = ({ node }) => {
|
|
6
7
|
const ref = useRef(null);
|
|
7
8
|
useRegistry(node.id, 'window', ref);
|
|
8
9
|
const handlers = useNodeEvents(node, 'window');
|
|
9
10
|
const isTransient = !!node.metadata?.isTransient;
|
|
10
|
-
|
|
11
|
+
const material = useMemo(() => {
|
|
12
|
+
const mat = node.material;
|
|
13
|
+
if (!mat)
|
|
14
|
+
return DEFAULT_WINDOW_MATERIAL;
|
|
15
|
+
return createMaterial(mat);
|
|
16
|
+
}, [node.material, node.material?.preset, node.material?.properties, node.material?.texture]);
|
|
17
|
+
return (_jsx("mesh", { castShadow: true, material: material, position: node.position, receiveShadow: true, ref: ref, rotation: node.rotation, visible: node.visible, ...(isTransient ? {} : handlers), children: _jsx("boxGeometry", { args: [0, 0, 0] }) }));
|
|
11
18
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/index.tsx"],"names":[],"mappings":"AAYA,OAAO,EAAkB,KAAK,kBAAkB,EAAsB,MAAM,oBAAoB,CAAA;AAEhG,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/index.tsx"],"names":[],"mappings":"AAYA,OAAO,EAAkB,KAAK,kBAAkB,EAAsB,MAAM,oBAAoB,CAAA;AAEhG,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AA0CrC,OAAO,QAAQ,oBAAoB,CAAC;IAClC,UAAU,aAAc,SAAQ,kBAAkB,CAAC,OAAO,KAAK,CAAC;KAAG;CACpE;AA+BD,UAAU,WAAW;IACnB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC1B,gBAAgB,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;IACvC,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED,QAAA,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CA6DjC,CAAA;AASD,eAAe,MAAM,CAAA"}
|
|
@@ -13,7 +13,6 @@ import { ScanSystem } from '../../systems/scan/scan-system';
|
|
|
13
13
|
import { WallCutout } from '../../systems/wall/wall-cutout';
|
|
14
14
|
import { ZoneSystem } from '../../systems/zone/zone-system';
|
|
15
15
|
import { SceneRenderer } from '../renderers/scene-renderer';
|
|
16
|
-
import { GroundOccluder } from './ground-occluder';
|
|
17
16
|
import { Lights } from './lights';
|
|
18
17
|
import { PerfMonitor } from './perf-monitor';
|
|
19
18
|
import PostProcessing from './post-processing';
|
|
@@ -68,11 +67,14 @@ const Viewer = ({ children, selectionManager = 'default', perf = false, }) => {
|
|
|
68
67
|
const renderer = new THREE.WebGPURenderer(props);
|
|
69
68
|
renderer.toneMapping = THREE.ACESFilmicToneMapping;
|
|
70
69
|
renderer.toneMappingExposure = 0.9;
|
|
70
|
+
// renderer.init() // Only use when using <DebugRenderer />
|
|
71
71
|
return renderer;
|
|
72
|
+
}, resize: {
|
|
73
|
+
debounce: 100,
|
|
72
74
|
}, shadows: {
|
|
73
75
|
type: THREE.PCFShadowMap,
|
|
74
76
|
enabled: true,
|
|
75
|
-
}, children: [_jsx(
|
|
77
|
+
}, children: [_jsx(ViewerCamera, {}), _jsx(Lights, {}), _jsx(Bvh, { children: _jsx(SceneRenderer, {}) }), _jsx(LevelSystem, {}), _jsx(GuideSystem, {}), _jsx(ScanSystem, {}), _jsx(WallCutout, {}), _jsx(CeilingSystem, {}), _jsx(DoorSystem, {}), _jsx(ItemSystem, {}), _jsx(RoofSystem, {}), _jsx(SlabSystem, {}), _jsx(WallSystem, {}), _jsx(WindowSystem, {}), _jsx(ZoneSystem, {}), _jsx(PostProcessing, {}), _jsx(GPUDeviceWatcher, {}), _jsx(ItemLightSystem, {}), selectionManager === 'default' && _jsx(SelectionManager, {}), perf && _jsx(PerfMonitor, {}), children] }));
|
|
76
78
|
};
|
|
77
79
|
const DebugRenderer = () => {
|
|
78
80
|
useFrame(({ gl, scene, camera }) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"post-processing.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/post-processing.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"post-processing.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/post-processing.tsx"],"names":[],"mappings":"AA4BA,eAAO,MAAM,WAAW;;;;;;;;;;;;;CAavB,CAAA;AAQD,QAAA,MAAM,oBAAoB,YAkRzB,CAAA;AAED,eAAe,oBAAoB,CAAA"}
|
|
@@ -3,9 +3,8 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
|
3
3
|
import { Color, Layers, UnsignedByteType } from 'three';
|
|
4
4
|
import { outline } from 'three/addons/tsl/display/OutlineNode.js';
|
|
5
5
|
import { ssgi } from 'three/addons/tsl/display/SSGINode.js';
|
|
6
|
-
import { traa } from 'three/addons/tsl/display/TRAANode.js';
|
|
7
6
|
import { denoise } from 'three/examples/jsm/tsl/display/DenoiseNode.js';
|
|
8
|
-
import { add, colorToDirection, diffuseColor, directionToColor, float, mix, mrt, normalView, oscSine, output, pass, sample, time, uniform, vec4,
|
|
7
|
+
import { add, colorToDirection, diffuseColor, directionToColor, float, mix, mrt, normalView, oscSine, output, pass, sample, time, uniform, vec4, } from 'three/tsl';
|
|
9
8
|
import { RenderPipeline } from 'three/webgpu';
|
|
10
9
|
import { SCENE_LAYER, ZONE_LAYER } from '../../lib/layers';
|
|
11
10
|
import useViewer from '../../store/use-viewer';
|
|
@@ -93,64 +92,58 @@ const PostProcessingPasses = () => {
|
|
|
93
92
|
outliner.selectedObjects.length = 0;
|
|
94
93
|
outliner.hoveredObjects.length = 0;
|
|
95
94
|
try {
|
|
96
|
-
// Scene pass with MRT for SSGI
|
|
97
95
|
const scenePass = pass(scene, camera);
|
|
98
|
-
scenePass.setMRT(mrt({
|
|
99
|
-
output,
|
|
100
|
-
diffuseColor,
|
|
101
|
-
normal: directionToColor(normalView),
|
|
102
|
-
velocity,
|
|
103
|
-
}));
|
|
104
|
-
// Get texture outputs
|
|
105
|
-
const scenePassColor = scenePass.getTextureNode('output');
|
|
106
|
-
const scenePassDiffuse = scenePass.getTextureNode('diffuseColor');
|
|
107
|
-
const scenePassDepth = scenePass.getTextureNode('depth');
|
|
108
|
-
const scenePassNormal = scenePass.getTextureNode('normal');
|
|
109
|
-
const scenePassVelocity = scenePass.getTextureNode('velocity');
|
|
110
|
-
// Optimize texture bandwidth
|
|
111
|
-
const diffuseTexture = scenePass.getTexture('diffuseColor');
|
|
112
|
-
diffuseTexture.type = UnsignedByteType;
|
|
113
|
-
const normalTexture = scenePass.getTexture('normal');
|
|
114
|
-
normalTexture.type = UnsignedByteType;
|
|
115
|
-
// Extract normal from color-encoded texture
|
|
116
|
-
const sceneNormal = sample((uv) => {
|
|
117
|
-
return colorToDirection(scenePassNormal.sample(uv));
|
|
118
|
-
});
|
|
119
96
|
const zonePass = pass(scene, camera);
|
|
120
97
|
zonePass.setLayers(zoneLayers);
|
|
121
|
-
|
|
122
|
-
const giPass = ssgi(scenePassColor, scenePassDepth, sceneNormal, camera);
|
|
123
|
-
giPass.sliceCount.value = SSGI_PARAMS.sliceCount;
|
|
124
|
-
giPass.stepCount.value = SSGI_PARAMS.stepCount;
|
|
125
|
-
giPass.radius.value = SSGI_PARAMS.radius;
|
|
126
|
-
giPass.expFactor.value = SSGI_PARAMS.expFactor;
|
|
127
|
-
giPass.thickness.value = SSGI_PARAMS.thickness;
|
|
128
|
-
giPass.backfaceLighting.value = SSGI_PARAMS.backfaceLighting;
|
|
129
|
-
giPass.aoIntensity.value = SSGI_PARAMS.aoIntensity;
|
|
130
|
-
giPass.giIntensity.value = SSGI_PARAMS.giIntensity;
|
|
131
|
-
giPass.useLinearThickness.value = SSGI_PARAMS.useLinearThickness;
|
|
132
|
-
giPass.useScreenSpaceSampling.value = SSGI_PARAMS.useScreenSpaceSampling;
|
|
133
|
-
giPass.useTemporalFiltering = SSGI_PARAMS.useTemporalFiltering;
|
|
134
|
-
const giTexture = giPass.getTextureNode();
|
|
135
|
-
// DenoiseNode only denoises RGB — alpha is passed through unchanged.
|
|
136
|
-
// SSGI packs AO into alpha, so we remap it into RGB before denoising.
|
|
137
|
-
// convertToTexture() inside denoise() will call rtt() on this vec4 node automatically.
|
|
138
|
-
const aoAsRgb = vec4(giTexture.a, giTexture.a, giTexture.a, float(1));
|
|
139
|
-
const denoisePass = denoise(aoAsRgb, scenePassDepth, sceneNormal, camera);
|
|
140
|
-
denoisePass.index.value = 0;
|
|
141
|
-
denoisePass.radius.value = 4;
|
|
142
|
-
const gi = giPass.rgb;
|
|
143
|
-
const ao = denoisePass.r;
|
|
144
|
-
// const gi = giPass.rgb;
|
|
145
|
-
// const ao = giPass.a;
|
|
98
|
+
const scenePassColor = scenePass.getTextureNode('output');
|
|
146
99
|
// Background detection via alpha: renderer clears with alpha=0 (setClearAlpha(0) in useFrame),
|
|
147
100
|
// so background pixels have scenePassColor.a=0 while geometry pixels have output.a=1.
|
|
148
101
|
// WebGPU only applies clearColorValue to MRT attachment 0 (output), so scenePassColor.a
|
|
149
102
|
// is the reliable geometry mask — no normals, no flicker.
|
|
150
103
|
const hasGeometry = scenePassColor.a;
|
|
151
104
|
const contentAlpha = hasGeometry.max(zonePass.a);
|
|
152
|
-
|
|
153
|
-
|
|
105
|
+
let sceneColor = scenePassColor;
|
|
106
|
+
if (SSGI_PARAMS.enabled) {
|
|
107
|
+
// MRT only needed for SSGI (diffuse for GI, normal for SSGI sampling)
|
|
108
|
+
scenePass.setMRT(mrt({
|
|
109
|
+
output,
|
|
110
|
+
diffuseColor,
|
|
111
|
+
normal: directionToColor(normalView),
|
|
112
|
+
}));
|
|
113
|
+
const scenePassDiffuse = scenePass.getTextureNode('diffuseColor');
|
|
114
|
+
const scenePassDepth = scenePass.getTextureNode('depth');
|
|
115
|
+
const scenePassNormal = scenePass.getTextureNode('normal');
|
|
116
|
+
// Optimize texture bandwidth
|
|
117
|
+
const diffuseTexture = scenePass.getTexture('diffuseColor');
|
|
118
|
+
diffuseTexture.type = UnsignedByteType;
|
|
119
|
+
const normalTexture = scenePass.getTexture('normal');
|
|
120
|
+
normalTexture.type = UnsignedByteType;
|
|
121
|
+
// Extract normal from color-encoded texture
|
|
122
|
+
const sceneNormal = sample((uv) => colorToDirection(scenePassNormal.sample(uv)));
|
|
123
|
+
const giPass = ssgi(scenePassColor, scenePassDepth, sceneNormal, camera);
|
|
124
|
+
giPass.sliceCount.value = SSGI_PARAMS.sliceCount;
|
|
125
|
+
giPass.stepCount.value = SSGI_PARAMS.stepCount;
|
|
126
|
+
giPass.radius.value = SSGI_PARAMS.radius;
|
|
127
|
+
giPass.expFactor.value = SSGI_PARAMS.expFactor;
|
|
128
|
+
giPass.thickness.value = SSGI_PARAMS.thickness;
|
|
129
|
+
giPass.backfaceLighting.value = SSGI_PARAMS.backfaceLighting;
|
|
130
|
+
giPass.aoIntensity.value = SSGI_PARAMS.aoIntensity;
|
|
131
|
+
giPass.giIntensity.value = SSGI_PARAMS.giIntensity;
|
|
132
|
+
giPass.useLinearThickness.value = SSGI_PARAMS.useLinearThickness;
|
|
133
|
+
giPass.useScreenSpaceSampling.value = SSGI_PARAMS.useScreenSpaceSampling;
|
|
134
|
+
giPass.useTemporalFiltering = SSGI_PARAMS.useTemporalFiltering;
|
|
135
|
+
const giTexture = giPass.getTextureNode();
|
|
136
|
+
// DenoiseNode only denoises RGB — alpha is passed through unchanged.
|
|
137
|
+
// SSGI packs AO into alpha, so we remap it into RGB before denoising.
|
|
138
|
+
const aoAsRgb = vec4(giTexture.a, giTexture.a, giTexture.a, float(1));
|
|
139
|
+
const denoisePass = denoise(aoAsRgb, scenePassDepth, sceneNormal, camera);
|
|
140
|
+
denoisePass.index.value = 0;
|
|
141
|
+
denoisePass.radius.value = 4;
|
|
142
|
+
const gi = giPass.rgb;
|
|
143
|
+
const ao = denoisePass.r;
|
|
144
|
+
// Composite: scene * AO + diffuse * GI
|
|
145
|
+
sceneColor = vec4(add(scenePassColor.rgb.mul(ao), add(zonePass.rgb, scenePassDiffuse.rgb.mul(gi))), contentAlpha);
|
|
146
|
+
}
|
|
154
147
|
function generateSelectedOutlinePass() {
|
|
155
148
|
const edgeStrength = uniform(3);
|
|
156
149
|
const edgeGlow = uniform(0);
|
|
@@ -193,18 +186,8 @@ const PostProcessingPasses = () => {
|
|
|
193
186
|
}
|
|
194
187
|
const selectedOutlinePass = generateSelectedOutlinePass();
|
|
195
188
|
const hoverOutlinePass = generateHoverOutlinePass();
|
|
196
|
-
|
|
197
|
-
const
|
|
198
|
-
? vec4(add(compositePass.rgb, selectedOutlinePass.add(hoverOutlinePass)), compositePass.a)
|
|
199
|
-
: vec4(add(scenePassColor.rgb, selectedOutlinePass.add(hoverOutlinePass)), scenePassColor.a);
|
|
200
|
-
// TRAA (Temporal Reprojection Anti-Aliasing) - applied AFTER combining everything
|
|
201
|
-
const traaOutput = traa(compositeWithOutlines, scenePassDepth, scenePassVelocity, camera);
|
|
202
|
-
// For zone-over-background pixels, scenePassDepth=1.0 (no scene geometry) causes TRAA
|
|
203
|
-
// to output black. Use hasGeometry to blend: geometry pixels use traaRgb, all others
|
|
204
|
-
// (zones over background, pure background) use compositePass.rgb directly.
|
|
205
|
-
const traaRgb = traaOutput.rgb;
|
|
206
|
-
const colorSource = mix(compositePass.rgb, traaRgb, hasGeometry);
|
|
207
|
-
const finalOutput = vec4(mix(bgUniform.current, colorSource, contentAlpha), float(1));
|
|
189
|
+
const compositeWithOutlines = vec4(add(sceneColor.rgb, selectedOutlinePass.add(hoverOutlinePass)), sceneColor.a);
|
|
190
|
+
const finalOutput = vec4(mix(bgUniform.current, compositeWithOutlines.rgb, contentAlpha), float(1));
|
|
208
191
|
const renderPipeline = new RenderPipeline(renderer);
|
|
209
192
|
renderPipeline.outputNode = finalOutput;
|
|
210
193
|
renderPipelineRef.current = renderPipeline;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-gltf-ktx2.d.ts","sourceRoot":"","sources":["../../src/hooks/use-gltf-ktx2.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAU3C,QAAA,MAAM,WAAW,GAAI,MAAM,MAAM,KAAG,UAAU,CAAC,OAAO,OAAO,CA2B5D,CAAA;
|
|
1
|
+
{"version":3,"file":"use-gltf-ktx2.d.ts","sourceRoot":"","sources":["../../src/hooks/use-gltf-ktx2.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAU3C,QAAA,MAAM,WAAW,GAAI,MAAM,MAAM,KAAG,UAAU,CAAC,OAAO,OAAO,CA2B5D,CAAA;AAED,OAAO,EAAE,WAAW,EAAE,CAAA"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { default as Viewer } from './components/viewer';
|
|
2
2
|
export { ASSETS_CDN_URL, resolveAssetUrl, resolveCdnUrl } from './lib/asset-url';
|
|
3
3
|
export { SCENE_LAYER, ZONE_LAYER } from './lib/layers';
|
|
4
|
+
export { clearMaterialCache, createDefaultMaterial, createMaterial, DEFAULT_CEILING_MATERIAL, DEFAULT_DOOR_MATERIAL, DEFAULT_ROOF_MATERIAL, DEFAULT_SLAB_MATERIAL, DEFAULT_WALL_MATERIAL, DEFAULT_WINDOW_MATERIAL, disposeMaterial, } from './lib/materials';
|
|
4
5
|
export { default as useViewer } from './store/use-viewer';
|
|
5
6
|
export { InteractiveSystem } from './systems/interactive/interactive-system';
|
|
6
7
|
export { snapLevelsToTruePositions } from './systems/level/level-utils';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAChF,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACtD,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAA;AAC5E,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAChF,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACtD,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,cAAc,EACd,wBAAwB,EACxB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,uBAAuB,EACvB,eAAe,GAChB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAA;AAC5E,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { default as Viewer } from './components/viewer';
|
|
2
2
|
export { ASSETS_CDN_URL, resolveAssetUrl, resolveCdnUrl } from './lib/asset-url';
|
|
3
3
|
export { SCENE_LAYER, ZONE_LAYER } from './lib/layers';
|
|
4
|
+
export { clearMaterialCache, createDefaultMaterial, createMaterial, DEFAULT_CEILING_MATERIAL, DEFAULT_DOOR_MATERIAL, DEFAULT_ROOF_MATERIAL, DEFAULT_SLAB_MATERIAL, DEFAULT_WALL_MATERIAL, DEFAULT_WINDOW_MATERIAL, disposeMaterial, } from './lib/materials';
|
|
4
5
|
export { default as useViewer } from './store/use-viewer';
|
|
5
6
|
export { InteractiveSystem } from './systems/interactive/interactive-system';
|
|
6
7
|
export { snapLevelsToTruePositions } from './systems/level/level-utils';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type MaterialSchema } from '@pascal-app/core';
|
|
2
|
+
import * as THREE from 'three';
|
|
3
|
+
export declare function createMaterial(material?: MaterialSchema): THREE.MeshStandardMaterial;
|
|
4
|
+
export declare function createDefaultMaterial(color?: string, roughness?: number): THREE.MeshStandardMaterial;
|
|
5
|
+
export declare const DEFAULT_WALL_MATERIAL: THREE.MeshStandardMaterial;
|
|
6
|
+
export declare const DEFAULT_SLAB_MATERIAL: THREE.MeshStandardMaterial;
|
|
7
|
+
export declare const DEFAULT_DOOR_MATERIAL: THREE.MeshStandardMaterial;
|
|
8
|
+
export declare const DEFAULT_WINDOW_MATERIAL: THREE.MeshStandardMaterial;
|
|
9
|
+
export declare const DEFAULT_CEILING_MATERIAL: THREE.MeshStandardMaterial;
|
|
10
|
+
export declare const DEFAULT_ROOF_MATERIAL: THREE.MeshStandardMaterial;
|
|
11
|
+
export declare function disposeMaterial(material: THREE.Material): void;
|
|
12
|
+
export declare function clearMaterialCache(): void;
|
|
13
|
+
//# sourceMappingURL=materials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"materials.d.ts","sourceRoot":"","sources":["../../src/lib/materials.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,KAAK,cAAc,EAAmB,MAAM,kBAAkB,CAAA;AAChG,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAc9B,wBAAgB,cAAc,CAAC,QAAQ,CAAC,EAAE,cAAc,GAAG,KAAK,CAAC,oBAAoB,CAmBpF;AAED,wBAAgB,qBAAqB,CACnC,KAAK,GAAE,MAAkB,EACzB,SAAS,GAAE,MAAY,GACtB,KAAK,CAAC,oBAAoB,CAO5B;AAED,eAAO,MAAM,qBAAqB,4BAAwC,CAAA;AAC1E,eAAO,MAAM,qBAAqB,4BAAwC,CAAA;AAC1E,eAAO,MAAM,qBAAqB,4BAAwC,CAAA;AAC1E,eAAO,MAAM,uBAAuB,4BAOlC,CAAA;AACF,eAAO,MAAM,wBAAwB,4BAAyC,CAAA;AAC9E,eAAO,MAAM,qBAAqB,4BAAyC,CAAA;AAE3E,wBAAgB,eAAe,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,GAAG,IAAI,CAE9D;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAKzC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { resolveMaterial } from '@pascal-app/core';
|
|
2
|
+
import * as THREE from 'three';
|
|
3
|
+
const sideMap = {
|
|
4
|
+
front: THREE.FrontSide,
|
|
5
|
+
back: THREE.BackSide,
|
|
6
|
+
double: THREE.DoubleSide,
|
|
7
|
+
};
|
|
8
|
+
const materialCache = new Map();
|
|
9
|
+
function getCacheKey(props) {
|
|
10
|
+
return `${props.color}-${props.roughness}-${props.metalness}-${props.opacity}-${props.transparent}-${props.side}`;
|
|
11
|
+
}
|
|
12
|
+
export function createMaterial(material) {
|
|
13
|
+
const props = resolveMaterial(material);
|
|
14
|
+
const cacheKey = getCacheKey(props);
|
|
15
|
+
if (materialCache.has(cacheKey)) {
|
|
16
|
+
return materialCache.get(cacheKey);
|
|
17
|
+
}
|
|
18
|
+
const threeMaterial = new THREE.MeshStandardMaterial({
|
|
19
|
+
color: props.color,
|
|
20
|
+
roughness: props.roughness,
|
|
21
|
+
metalness: props.metalness,
|
|
22
|
+
opacity: props.opacity,
|
|
23
|
+
transparent: props.transparent,
|
|
24
|
+
side: sideMap[props.side],
|
|
25
|
+
});
|
|
26
|
+
materialCache.set(cacheKey, threeMaterial);
|
|
27
|
+
return threeMaterial;
|
|
28
|
+
}
|
|
29
|
+
export function createDefaultMaterial(color = '#ffffff', roughness = 0.9) {
|
|
30
|
+
return new THREE.MeshStandardMaterial({
|
|
31
|
+
color,
|
|
32
|
+
roughness,
|
|
33
|
+
metalness: 0,
|
|
34
|
+
side: THREE.FrontSide,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
export const DEFAULT_WALL_MATERIAL = createDefaultMaterial('#ffffff', 0.9);
|
|
38
|
+
export const DEFAULT_SLAB_MATERIAL = createDefaultMaterial('#e5e5e5', 0.8);
|
|
39
|
+
export const DEFAULT_DOOR_MATERIAL = createDefaultMaterial('#8b4513', 0.7);
|
|
40
|
+
export const DEFAULT_WINDOW_MATERIAL = new THREE.MeshStandardMaterial({
|
|
41
|
+
color: '#87ceeb',
|
|
42
|
+
roughness: 0.1,
|
|
43
|
+
metalness: 0.1,
|
|
44
|
+
opacity: 0.3,
|
|
45
|
+
transparent: true,
|
|
46
|
+
side: THREE.DoubleSide,
|
|
47
|
+
});
|
|
48
|
+
export const DEFAULT_CEILING_MATERIAL = createDefaultMaterial('#f5f5dc', 0.95);
|
|
49
|
+
export const DEFAULT_ROOF_MATERIAL = createDefaultMaterial('#808080', 0.85);
|
|
50
|
+
export function disposeMaterial(material) {
|
|
51
|
+
material.dispose();
|
|
52
|
+
}
|
|
53
|
+
export function clearMaterialCache() {
|
|
54
|
+
for (const material of materialCache.values()) {
|
|
55
|
+
material.dispose();
|
|
56
|
+
}
|
|
57
|
+
materialCache.clear();
|
|
58
|
+
}
|
|
@@ -40,8 +40,8 @@ type ViewerState = {
|
|
|
40
40
|
setSelection: (updates: Partial<SelectionPath>) => void;
|
|
41
41
|
resetSelection: () => void;
|
|
42
42
|
outliner: Outliner;
|
|
43
|
-
exportScene: (() => Promise<void>) | null;
|
|
44
|
-
setExportScene: (fn: (() => Promise<void>) | null) => void;
|
|
43
|
+
exportScene: ((format?: 'glb' | 'stl' | 'obj') => Promise<void>) | null;
|
|
44
|
+
setExportScene: (fn: ((format?: 'glb' | 'stl' | 'obj') => Promise<void>) | null) => void;
|
|
45
45
|
debugColors: boolean;
|
|
46
46
|
setDebugColors: (enabled: boolean) => void;
|
|
47
47
|
cameraDragging: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-viewer.d.ts","sourceRoot":"","sources":["../../src/store/use-viewer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC5F,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAKrC,KAAK,aAAa,GAAG;IACnB,UAAU,EAAE,YAAY,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IACrC,OAAO,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IAC/B,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IAC7B,WAAW,EAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAA;CAC9B,CAAA;AAED,KAAK,QAAQ,GAAG;IACd,eAAe,EAAE,QAAQ,EAAE,CAAA;IAC3B,cAAc,EAAE,QAAQ,EAAE,CAAA;CAC3B,CAAA;AAED,KAAK,WAAW,GAAG;IACjB,SAAS,EAAE,aAAa,CAAA;IACxB,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IAChD,YAAY,EAAE,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,KAAK,IAAI,CAAA;IAEjE,UAAU,EAAE,aAAa,GAAG,cAAc,CAAA;IAC1C,aAAa,EAAE,CAAC,IAAI,EAAE,aAAa,GAAG,cAAc,KAAK,IAAI,CAAA;IAE7D,KAAK,EAAE,OAAO,GAAG,MAAM,CAAA;IACvB,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,KAAK,IAAI,CAAA;IAE3C,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAA;IAC3B,OAAO,EAAE,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,KAAK,IAAI,CAAA;IAE9C,SAAS,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,CAAA;IACrD,YAAY,EAAE,CAAC,IAAI,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM,GAAG,QAAQ,KAAK,IAAI,CAAA;IAExE,QAAQ,EAAE,IAAI,GAAG,SAAS,GAAG,MAAM,CAAA;IACnC,WAAW,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,GAAG,MAAM,KAAK,IAAI,CAAA;IAEtD,SAAS,EAAE,OAAO,CAAA;IAClB,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IAErC,UAAU,EAAE,OAAO,CAAA;IACnB,aAAa,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IAEtC,QAAQ,EAAE,OAAO,CAAA;IACjB,WAAW,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IAEpC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IACzC,kBAAkB,EAAE,MAAM,CACxB,MAAM,EACN;QAAE,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAClE,CAAA;IAGD,YAAY,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC,KAAK,IAAI,CAAA;IACvD,cAAc,EAAE,MAAM,IAAI,CAAA;IAE1B,QAAQ,EAAE,QAAQ,CAAA;IAGlB,WAAW,EAAE,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"use-viewer.d.ts","sourceRoot":"","sources":["../../src/store/use-viewer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wall-cutout.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-cutout.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"wall-cutout.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-cutout.tsx"],"names":[],"mappings":"AAmIA,eAAO,MAAM,UAAU,YAiFtB,CAAA"}
|
|
@@ -7,92 +7,162 @@ import useViewer from '../../store/use-viewer';
|
|
|
7
7
|
const tmpVec = new Vector3();
|
|
8
8
|
const u = new Vector3();
|
|
9
9
|
const v = new Vector3();
|
|
10
|
-
// Dot pattern shader
|
|
11
10
|
const dotPattern = Fn(() => {
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
const dotSize = float(0.3); // Size of dots relative to grid
|
|
15
|
-
// Use XY coordinates for pattern on wall face
|
|
11
|
+
const scale = float(0.1);
|
|
12
|
+
const dotSize = float(0.3);
|
|
16
13
|
const uv = vec2(positionLocal.x, positionLocal.y).div(scale);
|
|
17
14
|
const gridUV = fract(uv);
|
|
18
|
-
// Distance from center of grid cell (creates circular dots)
|
|
19
15
|
const dist = length(gridUV.sub(0.5));
|
|
20
|
-
// Create dots: 1 where we want dots, 0 elsewhere
|
|
21
16
|
const dots = step(dist, dotSize.mul(0.5));
|
|
22
|
-
|
|
23
|
-
const fadeHeight = float(2.5); // Fade over 2.5 meters
|
|
17
|
+
const fadeHeight = float(2.5);
|
|
24
18
|
const yFade = float(1).sub(smoothstep(float(0), fadeHeight, positionLocal.y));
|
|
25
19
|
return dots.mul(yFade);
|
|
26
20
|
});
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
21
|
+
const wallMaterialCache = new Map();
|
|
22
|
+
function getMaterialHash(wallNode) {
|
|
23
|
+
if (!wallNode.material)
|
|
24
|
+
return 'none';
|
|
25
|
+
const mat = wallNode.material;
|
|
26
|
+
if (mat.preset && mat.preset !== 'custom') {
|
|
27
|
+
return `preset-${mat.preset}`;
|
|
28
|
+
}
|
|
29
|
+
if (mat.properties) {
|
|
30
|
+
return `props-${mat.properties.color}-${mat.properties.roughness}-${mat.properties.metalness}`;
|
|
31
|
+
}
|
|
32
|
+
return 'default';
|
|
33
|
+
}
|
|
34
|
+
const presetColors = {
|
|
35
|
+
white: '#ffffff',
|
|
36
|
+
brick: '#8b4513',
|
|
37
|
+
concrete: '#808080',
|
|
38
|
+
wood: '#deb887',
|
|
39
|
+
glass: '#87ceeb',
|
|
40
|
+
metal: '#c0c0c0',
|
|
41
|
+
plaster: '#f5f5dc',
|
|
42
|
+
tile: '#dcdcdc',
|
|
43
|
+
marble: '#f5f5f5',
|
|
44
|
+
};
|
|
45
|
+
function getPresetColor(preset) {
|
|
46
|
+
return presetColors[preset] ?? '#ffffff';
|
|
47
|
+
}
|
|
48
|
+
function getMaterialsForWall(wallNode) {
|
|
49
|
+
const cacheKey = wallNode.id;
|
|
50
|
+
const materialHash = getMaterialHash(wallNode);
|
|
51
|
+
const existing = wallMaterialCache.get(cacheKey);
|
|
52
|
+
if (existing && existing.materialHash === materialHash) {
|
|
53
|
+
return existing;
|
|
54
|
+
}
|
|
55
|
+
if (existing) {
|
|
56
|
+
existing.visible.dispose();
|
|
57
|
+
existing.invisible.dispose();
|
|
58
|
+
}
|
|
59
|
+
let userColor = '#ffffff';
|
|
60
|
+
if (wallNode.material?.properties?.color) {
|
|
61
|
+
userColor = wallNode.material.properties.color;
|
|
62
|
+
}
|
|
63
|
+
else if (wallNode.material?.preset && wallNode.material.preset !== 'custom') {
|
|
64
|
+
userColor = getPresetColor(wallNode.material.preset);
|
|
65
|
+
}
|
|
66
|
+
const visibleMat = new MeshStandardNodeMaterial({
|
|
67
|
+
color: userColor,
|
|
68
|
+
roughness: 1,
|
|
69
|
+
metalness: 0,
|
|
70
|
+
});
|
|
71
|
+
const invisibleMat = new MeshStandardNodeMaterial({
|
|
72
|
+
transparent: true,
|
|
73
|
+
opacityNode: mix(float(0.0), float(0.24), dotPattern()),
|
|
74
|
+
color: userColor,
|
|
75
|
+
depthWrite: false,
|
|
76
|
+
emissive: userColor,
|
|
77
|
+
});
|
|
78
|
+
const result = { visible: visibleMat, invisible: invisibleMat, materialHash };
|
|
79
|
+
wallMaterialCache.set(cacheKey, result);
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
function getWallHideState(wallNode, wallMesh, wallMode, cameraDir) {
|
|
83
|
+
let hideWall = wallNode.frontSide === 'interior' && wallNode.backSide === 'interior';
|
|
84
|
+
if (wallMode === 'up') {
|
|
85
|
+
hideWall = false;
|
|
86
|
+
}
|
|
87
|
+
else if (wallMode === 'down') {
|
|
88
|
+
hideWall = true;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
wallMesh.getWorldDirection(v);
|
|
92
|
+
if (v.dot(cameraDir) < 0) {
|
|
93
|
+
if (wallNode.frontSide === 'exterior' && wallNode.backSide !== 'exterior') {
|
|
94
|
+
hideWall = true;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else if (wallNode.backSide === 'exterior' && wallNode.frontSide !== 'exterior') {
|
|
98
|
+
hideWall = true;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return hideWall;
|
|
102
|
+
}
|
|
39
103
|
export const WallCutout = () => {
|
|
40
104
|
const lastCameraPosition = useRef(new Vector3());
|
|
41
105
|
const lastCameraTarget = useRef(new Vector3());
|
|
42
106
|
const lastUpdateTime = useRef(0);
|
|
43
107
|
const lastWallMode = useRef(useViewer.getState().wallMode);
|
|
44
108
|
const lastNumberOfWalls = useRef(0);
|
|
109
|
+
const lastWallMaterials = useRef(new Map());
|
|
45
110
|
useFrame(({ camera, clock }) => {
|
|
46
111
|
const wallMode = useViewer.getState().wallMode;
|
|
47
112
|
const currentTime = clock.elapsedTime;
|
|
48
113
|
const currentCameraPosition = camera.position;
|
|
49
114
|
camera.getWorldDirection(tmpVec);
|
|
50
115
|
tmpVec.add(currentCameraPosition);
|
|
51
|
-
// Throttle: only update if camera moved significantly AND enough time passed
|
|
52
116
|
const distanceMoved = currentCameraPosition.distanceTo(lastCameraPosition.current);
|
|
53
117
|
const directionChanged = tmpVec.distanceTo(lastCameraTarget.current);
|
|
54
118
|
const timeSinceUpdate = currentTime - lastUpdateTime.current;
|
|
55
|
-
|
|
56
|
-
if (((distanceMoved > 0.5 || directionChanged > 0.3) && timeSinceUpdate > 0.1) ||
|
|
119
|
+
const shouldUpdate = ((distanceMoved > 0.5 || directionChanged > 0.3) && timeSinceUpdate > 0.1) ||
|
|
57
120
|
lastWallMode.current !== wallMode ||
|
|
58
|
-
sceneRegistry.byType.wall.size !== lastNumberOfWalls.current
|
|
59
|
-
|
|
60
|
-
|
|
121
|
+
sceneRegistry.byType.wall.size !== lastNumberOfWalls.current;
|
|
122
|
+
const walls = sceneRegistry.byType.wall;
|
|
123
|
+
const currentWallIds = new Set();
|
|
124
|
+
walls.forEach((wallId) => {
|
|
125
|
+
const wallMesh = sceneRegistry.nodes.get(wallId);
|
|
126
|
+
if (!wallMesh)
|
|
127
|
+
return;
|
|
128
|
+
const wallNode = useScene.getState().nodes[wallId];
|
|
129
|
+
if (!wallNode || wallNode.type !== 'wall')
|
|
130
|
+
return;
|
|
131
|
+
currentWallIds.add(wallId);
|
|
132
|
+
const hideWall = getWallHideState(wallNode, wallMesh, wallMode, u);
|
|
133
|
+
if (shouldUpdate) {
|
|
134
|
+
const materials = getMaterialsForWall(wallNode);
|
|
135
|
+
wallMesh.material = hideWall ? materials.invisible : materials.visible;
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
const currentMaterial = wallMesh.material;
|
|
139
|
+
const materials = wallMaterialCache.get(wallId);
|
|
140
|
+
if (!materials ||
|
|
141
|
+
currentMaterial !== (hideWall ? materials.invisible : materials.visible)) {
|
|
142
|
+
const newMaterials = getMaterialsForWall(wallNode);
|
|
143
|
+
wallMesh.material = hideWall ? newMaterials.invisible : newMaterials.visible;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
if (shouldUpdate) {
|
|
61
148
|
lastCameraPosition.current.copy(currentCameraPosition);
|
|
62
149
|
lastCameraTarget.current.copy(tmpVec);
|
|
63
150
|
lastUpdateTime.current = currentTime;
|
|
64
151
|
camera.getWorldDirection(u);
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
let hideWall = wallNode.frontSide === 'interior' && wallNode.backSide === 'interior';
|
|
74
|
-
if (wallMode === 'up') {
|
|
75
|
-
hideWall = false;
|
|
76
|
-
}
|
|
77
|
-
else if (wallMode === 'down') {
|
|
78
|
-
hideWall = true;
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
wallMesh.getWorldDirection(v);
|
|
82
|
-
if (v.dot(u) < 0) {
|
|
83
|
-
// Front side
|
|
84
|
-
if (wallNode.frontSide === 'exterior' && wallNode.backSide !== 'exterior') {
|
|
85
|
-
hideWall = true;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
else if (wallNode.backSide === 'exterior' && wallNode.frontSide !== 'exterior') {
|
|
89
|
-
// Back side
|
|
90
|
-
hideWall = true;
|
|
91
|
-
}
|
|
152
|
+
if (lastWallMode.current !== wallMode) {
|
|
153
|
+
wallMaterialCache.clear();
|
|
154
|
+
}
|
|
155
|
+
for (const [wallId, mats] of lastWallMaterials.current) {
|
|
156
|
+
if (!currentWallIds.has(wallId)) {
|
|
157
|
+
mats.visible.dispose();
|
|
158
|
+
mats.invisible.dispose();
|
|
159
|
+
wallMaterialCache.delete(wallId);
|
|
92
160
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
161
|
+
}
|
|
162
|
+
lastWallMaterials.current.clear();
|
|
163
|
+
for (const [wallId, mats] of wallMaterialCache) {
|
|
164
|
+
lastWallMaterials.current.set(wallId, mats);
|
|
165
|
+
}
|
|
96
166
|
lastWallMode.current = wallMode;
|
|
97
167
|
lastNumberOfWalls.current = sceneRegistry.byType.wall.size;
|
|
98
168
|
}
|
package/package.json
CHANGED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { ThreeEvent } from "@react-three/fiber";
|
|
2
|
-
export declare function useGridEvents(): {
|
|
3
|
-
onPointerDown: (e: ThreeEvent<PointerEvent>) => void;
|
|
4
|
-
onPointerUp: (e: ThreeEvent<PointerEvent>) => void;
|
|
5
|
-
onClick: (e: ThreeEvent<PointerEvent>) => void;
|
|
6
|
-
onPointerEnter: (e: ThreeEvent<PointerEvent>) => void;
|
|
7
|
-
onPointerLeave: (e: ThreeEvent<PointerEvent>) => void;
|
|
8
|
-
onPointerMove: (e: ThreeEvent<PointerEvent>) => void;
|
|
9
|
-
onDoubleClick: (e: ThreeEvent<PointerEvent>) => void;
|
|
10
|
-
onContextMenu: (e: ThreeEvent<PointerEvent>) => void;
|
|
11
|
-
};
|
|
12
|
-
//# sourceMappingURL=use-grid-events.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"use-grid-events.d.ts","sourceRoot":"","sources":["../../src/hooks/use-grid-events.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,wBAAgB,aAAa;uBAYN,UAAU,CAAC,YAAY,CAAC;qBAI1B,UAAU,CAAC,YAAY,CAAC;iBAI5B,UAAU,CAAC,YAAY,CAAC;wBAIjB,UAAU,CAAC,YAAY,CAAC;wBACxB,UAAU,CAAC,YAAY,CAAC;uBACzB,UAAU,CAAC,YAAY,CAAC;uBACxB,UAAU,CAAC,YAAY,CAAC;uBACxB,UAAU,CAAC,YAAY,CAAC;EAE9C"}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { emitter } from "@pascal-app/core";
|
|
2
|
-
export function useGridEvents() {
|
|
3
|
-
const emit = (suffix, e) => {
|
|
4
|
-
const eventKey = `grid:${suffix}`;
|
|
5
|
-
const payload = {
|
|
6
|
-
position: [e.point.x, e.point.y, e.point.z],
|
|
7
|
-
nativeEvent: e,
|
|
8
|
-
};
|
|
9
|
-
emitter.emit(eventKey, payload);
|
|
10
|
-
};
|
|
11
|
-
return {
|
|
12
|
-
onPointerDown: (e) => {
|
|
13
|
-
if (e.button !== 0)
|
|
14
|
-
return;
|
|
15
|
-
emit("pointerdown", e);
|
|
16
|
-
},
|
|
17
|
-
onPointerUp: (e) => {
|
|
18
|
-
if (e.button !== 0)
|
|
19
|
-
return;
|
|
20
|
-
emit("pointerup", e);
|
|
21
|
-
},
|
|
22
|
-
onClick: (e) => {
|
|
23
|
-
if (e.button !== 0)
|
|
24
|
-
return;
|
|
25
|
-
emit("click", e);
|
|
26
|
-
},
|
|
27
|
-
onPointerEnter: (e) => emit("enter", e),
|
|
28
|
-
onPointerLeave: (e) => emit("leave", e),
|
|
29
|
-
onPointerMove: (e) => emit("move", e),
|
|
30
|
-
onDoubleClick: (e) => emit("double-click", e),
|
|
31
|
-
onContextMenu: (e) => emit("context-menu", e),
|
|
32
|
-
};
|
|
33
|
-
}
|