@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
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { sceneRegistry, useScene } from '@pascal-app/core';
|
|
2
|
+
import { useFrame } from '@react-three/fiber';
|
|
3
|
+
import * as THREE from 'three';
|
|
4
|
+
function ensureUv2Attribute(geometry) {
|
|
5
|
+
const uv = geometry.getAttribute('uv');
|
|
6
|
+
if (!uv)
|
|
7
|
+
return;
|
|
8
|
+
geometry.setAttribute('uv2', new THREE.Float32BufferAttribute(Array.from(uv.array), 2));
|
|
9
|
+
}
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// CEILING SYSTEM
|
|
12
|
+
// ============================================================================
|
|
13
|
+
export const CeilingSystem = () => {
|
|
14
|
+
const dirtyNodes = useScene((state) => state.dirtyNodes);
|
|
15
|
+
const clearDirty = useScene((state) => state.clearDirty);
|
|
16
|
+
useFrame(() => {
|
|
17
|
+
if (dirtyNodes.size === 0)
|
|
18
|
+
return;
|
|
19
|
+
const nodes = useScene.getState().nodes;
|
|
20
|
+
// Process dirty ceilings
|
|
21
|
+
dirtyNodes.forEach((id) => {
|
|
22
|
+
const node = nodes[id];
|
|
23
|
+
if (!node || node.type !== 'ceiling')
|
|
24
|
+
return;
|
|
25
|
+
const mesh = sceneRegistry.nodes.get(id);
|
|
26
|
+
if (mesh) {
|
|
27
|
+
updateCeilingGeometry(node, mesh);
|
|
28
|
+
clearDirty(id);
|
|
29
|
+
}
|
|
30
|
+
// If mesh not found, keep it dirty for next frame
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
return null;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Updates the geometry for a single ceiling
|
|
37
|
+
*/
|
|
38
|
+
function updateCeilingGeometry(node, mesh) {
|
|
39
|
+
const newGeo = generateCeilingGeometry(node);
|
|
40
|
+
mesh.geometry.dispose();
|
|
41
|
+
mesh.geometry = newGeo;
|
|
42
|
+
const gridMesh = mesh.getObjectByName('ceiling-grid');
|
|
43
|
+
if (gridMesh) {
|
|
44
|
+
gridMesh.geometry.dispose();
|
|
45
|
+
gridMesh.geometry = newGeo;
|
|
46
|
+
}
|
|
47
|
+
// Position at the ceiling height
|
|
48
|
+
mesh.position.y = (node.height ?? 2.5) - 0.01; // Slight offset to avoid z-fighting with upper-level slabs
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Generates flat ceiling geometry from polygon (no extrusion)
|
|
52
|
+
*/
|
|
53
|
+
export function generateCeilingGeometry(ceilingNode) {
|
|
54
|
+
const polygon = ceilingNode.polygon;
|
|
55
|
+
if (polygon.length < 3) {
|
|
56
|
+
return new THREE.BufferGeometry();
|
|
57
|
+
}
|
|
58
|
+
// Create shape from polygon
|
|
59
|
+
// Shape is in X-Y plane, we'll rotate to X-Z plane
|
|
60
|
+
const shape = new THREE.Shape();
|
|
61
|
+
const firstPt = polygon[0];
|
|
62
|
+
// Negate Y (which becomes Z) to get correct orientation after rotation
|
|
63
|
+
shape.moveTo(firstPt[0], -firstPt[1]);
|
|
64
|
+
for (let i = 1; i < polygon.length; i++) {
|
|
65
|
+
const pt = polygon[i];
|
|
66
|
+
shape.lineTo(pt[0], -pt[1]);
|
|
67
|
+
}
|
|
68
|
+
shape.closePath();
|
|
69
|
+
// Add holes to the shape
|
|
70
|
+
const holes = ceilingNode.holes || [];
|
|
71
|
+
for (const holePolygon of holes) {
|
|
72
|
+
if (holePolygon.length < 3)
|
|
73
|
+
continue;
|
|
74
|
+
const holePath = new THREE.Path();
|
|
75
|
+
const holeFirstPt = holePolygon[0];
|
|
76
|
+
holePath.moveTo(holeFirstPt[0], -holeFirstPt[1]);
|
|
77
|
+
for (let i = 1; i < holePolygon.length; i++) {
|
|
78
|
+
const pt = holePolygon[i];
|
|
79
|
+
holePath.lineTo(pt[0], -pt[1]);
|
|
80
|
+
}
|
|
81
|
+
holePath.closePath();
|
|
82
|
+
shape.holes.push(holePath);
|
|
83
|
+
}
|
|
84
|
+
// Create flat shape geometry (no extrusion)
|
|
85
|
+
const geometry = new THREE.ShapeGeometry(shape);
|
|
86
|
+
// Rotate so the shape lies flat in X-Z plane
|
|
87
|
+
geometry.rotateX(-Math.PI / 2);
|
|
88
|
+
geometry.computeVertexNormals();
|
|
89
|
+
ensureUv2Attribute(geometry);
|
|
90
|
+
return geometry;
|
|
91
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type BufferGeometry } from 'three';
|
|
2
|
+
export declare function createColumnBoxGeometry(width: number, height: number, depth: number, bevelRadius?: number): BufferGeometry<import("three").NormalBufferAttributes, import("three").BufferGeometryEventMap>;
|
|
3
|
+
export declare function createColumnCylinderGeometry({ height, radiusBottom, radiusTop, radiusX, radiusZ, segments, }: {
|
|
4
|
+
height: number;
|
|
5
|
+
radiusBottom: number;
|
|
6
|
+
radiusTop?: number;
|
|
7
|
+
radiusX?: number;
|
|
8
|
+
radiusZ?: number;
|
|
9
|
+
segments?: number;
|
|
10
|
+
}): BufferGeometry<import("three").NormalBufferAttributes, import("three").BufferGeometryEventMap>;
|
|
11
|
+
export declare function createColumnSphereGeometry(radius: number, widthSegments?: number, heightSegments?: number): BufferGeometry<import("three").NormalBufferAttributes, import("three").BufferGeometryEventMap>;
|
|
12
|
+
export declare function createColumnTorusGeometry({ arc, radialSegments, ringRadius, scaleX, scaleY, scaleZ, tubeRadius, tubularSegments, }: {
|
|
13
|
+
arc?: number;
|
|
14
|
+
radialSegments?: number;
|
|
15
|
+
ringRadius: number;
|
|
16
|
+
scaleX?: number;
|
|
17
|
+
scaleY?: number;
|
|
18
|
+
scaleZ?: number;
|
|
19
|
+
tubeRadius: number;
|
|
20
|
+
tubularSegments?: number;
|
|
21
|
+
}): BufferGeometry<import("three").NormalBufferAttributes, import("three").BufferGeometryEventMap>;
|
|
22
|
+
//# sourceMappingURL=column-geometry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"column-geometry.d.ts","sourceRoot":"","sources":["../../../src/systems/column/column-geometry.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EAKpB,MAAM,OAAO,CAAA;AAkHd,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,WAAW,SAAI,kGAOhB;AAED,wBAAgB,4BAA4B,CAAC,EAC3C,MAAM,EACN,YAAY,EACZ,SAAwB,EACxB,OAAW,EACX,OAAW,EACX,QAAa,GACd,EAAE;IACD,MAAM,EAAE,MAAM,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,kGASA;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,SAAK,EAAE,cAAc,SAAI,kGAEhG;AAED,wBAAgB,yBAAyB,CAAC,EACxC,GAAiB,EACjB,cAAmB,EACnB,UAAU,EACV,MAAmB,EACnB,MAAmB,EACnB,MAAU,EACV,UAAU,EACV,eAAoB,GACrB,EAAE;IACD,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB,kGAIA"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { BoxGeometry, CylinderGeometry, Float32BufferAttribute, SphereGeometry, TorusGeometry, } from 'three';
|
|
2
|
+
import { RoundedBoxGeometry } from 'three/examples/jsm/geometries/RoundedBoxGeometry.js';
|
|
3
|
+
const COLUMN_UV_SCALE = 1;
|
|
4
|
+
function setUvAttributes(geometry, uvs) {
|
|
5
|
+
geometry.setAttribute('uv', new Float32BufferAttribute(uvs, 2));
|
|
6
|
+
geometry.setAttribute('uv2', new Float32BufferAttribute(uvs.slice(), 2));
|
|
7
|
+
return geometry;
|
|
8
|
+
}
|
|
9
|
+
function toUvReadyGeometry(geometry) {
|
|
10
|
+
return geometry.index ? geometry.toNonIndexed() : geometry;
|
|
11
|
+
}
|
|
12
|
+
function applyPlanarColumnUvs(geometry) {
|
|
13
|
+
const mappedGeometry = toUvReadyGeometry(geometry);
|
|
14
|
+
const positions = mappedGeometry.getAttribute('position');
|
|
15
|
+
const normals = mappedGeometry.getAttribute('normal');
|
|
16
|
+
const uvs = [];
|
|
17
|
+
for (let index = 0; index < positions.count; index += 1) {
|
|
18
|
+
const x = positions.getX(index);
|
|
19
|
+
const y = positions.getY(index);
|
|
20
|
+
const z = positions.getZ(index);
|
|
21
|
+
const normalX = normals ? Math.abs(normals.getX(index)) : 0;
|
|
22
|
+
const normalY = normals ? Math.abs(normals.getY(index)) : 1;
|
|
23
|
+
const normalZ = normals ? Math.abs(normals.getZ(index)) : 0;
|
|
24
|
+
if (normalY >= normalX && normalY >= normalZ) {
|
|
25
|
+
uvs.push(x * COLUMN_UV_SCALE, z * COLUMN_UV_SCALE);
|
|
26
|
+
}
|
|
27
|
+
else if (normalX >= normalZ) {
|
|
28
|
+
uvs.push(z * COLUMN_UV_SCALE, y * COLUMN_UV_SCALE);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
uvs.push(x * COLUMN_UV_SCALE, y * COLUMN_UV_SCALE);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return setUvAttributes(mappedGeometry, uvs);
|
|
35
|
+
}
|
|
36
|
+
function ellipseCircumference(radiusX, radiusZ) {
|
|
37
|
+
const a = Math.max(0.001, Math.abs(radiusX));
|
|
38
|
+
const b = Math.max(0.001, Math.abs(radiusZ));
|
|
39
|
+
return Math.PI * (3 * (a + b) - Math.sqrt((3 * a + b) * (a + 3 * b)));
|
|
40
|
+
}
|
|
41
|
+
function applyCylindricalColumnUvs(geometry, sideCircumference, height) {
|
|
42
|
+
const mappedGeometry = toUvReadyGeometry(geometry);
|
|
43
|
+
const positions = mappedGeometry.getAttribute('position');
|
|
44
|
+
const normals = mappedGeometry.getAttribute('normal');
|
|
45
|
+
const defaultUvs = mappedGeometry.getAttribute('uv');
|
|
46
|
+
const halfHeight = height / 2;
|
|
47
|
+
const uvs = [];
|
|
48
|
+
for (let index = 0; index < positions.count; index += 1) {
|
|
49
|
+
const x = positions.getX(index);
|
|
50
|
+
const y = positions.getY(index);
|
|
51
|
+
const z = positions.getZ(index);
|
|
52
|
+
const normalY = normals ? Math.abs(normals.getY(index)) : 0;
|
|
53
|
+
if (normalY > 0.65) {
|
|
54
|
+
uvs.push(x * COLUMN_UV_SCALE, z * COLUMN_UV_SCALE);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const defaultU = defaultUvs ? defaultUvs.getX(index) : 0;
|
|
58
|
+
uvs.push(defaultU * sideCircumference * COLUMN_UV_SCALE, (y + halfHeight) * COLUMN_UV_SCALE);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return setUvAttributes(mappedGeometry, uvs);
|
|
62
|
+
}
|
|
63
|
+
function applySphericalColumnUvs(geometry, radius) {
|
|
64
|
+
const mappedGeometry = toUvReadyGeometry(geometry);
|
|
65
|
+
const defaultUvs = mappedGeometry.getAttribute('uv');
|
|
66
|
+
if (!defaultUvs)
|
|
67
|
+
return mappedGeometry;
|
|
68
|
+
const uvs = [];
|
|
69
|
+
const circumference = Math.PI * 2 * radius;
|
|
70
|
+
const arcHeight = Math.PI * radius;
|
|
71
|
+
for (let index = 0; index < defaultUvs.count; index += 1) {
|
|
72
|
+
uvs.push(defaultUvs.getX(index) * circumference * COLUMN_UV_SCALE, defaultUvs.getY(index) * arcHeight * COLUMN_UV_SCALE);
|
|
73
|
+
}
|
|
74
|
+
return setUvAttributes(mappedGeometry, uvs);
|
|
75
|
+
}
|
|
76
|
+
function applyTorusColumnUvs(geometry, ringRadius, tubeRadius) {
|
|
77
|
+
const mappedGeometry = toUvReadyGeometry(geometry);
|
|
78
|
+
const defaultUvs = mappedGeometry.getAttribute('uv');
|
|
79
|
+
if (!defaultUvs)
|
|
80
|
+
return mappedGeometry;
|
|
81
|
+
const uvs = [];
|
|
82
|
+
const ringLength = Math.PI * 2 * Math.max(0.001, ringRadius);
|
|
83
|
+
const tubeLength = Math.PI * 2 * Math.max(0.001, tubeRadius);
|
|
84
|
+
for (let index = 0; index < defaultUvs.count; index += 1) {
|
|
85
|
+
uvs.push(defaultUvs.getX(index) * ringLength * COLUMN_UV_SCALE, defaultUvs.getY(index) * tubeLength * COLUMN_UV_SCALE);
|
|
86
|
+
}
|
|
87
|
+
return setUvAttributes(mappedGeometry, uvs);
|
|
88
|
+
}
|
|
89
|
+
export function createColumnBoxGeometry(width, height, depth, bevelRadius = 0) {
|
|
90
|
+
const geometry = bevelRadius > 0.001
|
|
91
|
+
? new RoundedBoxGeometry(width, height, depth, 3, bevelRadius)
|
|
92
|
+
: new BoxGeometry(width, height, depth);
|
|
93
|
+
return applyPlanarColumnUvs(geometry);
|
|
94
|
+
}
|
|
95
|
+
export function createColumnCylinderGeometry({ height, radiusBottom, radiusTop = radiusBottom, radiusX = 1, radiusZ = 1, segments = 32, }) {
|
|
96
|
+
const geometry = new CylinderGeometry(radiusTop, radiusBottom, height, segments);
|
|
97
|
+
geometry.scale(radiusX, 1, radiusZ);
|
|
98
|
+
const sideRadius = Math.max(radiusTop, radiusBottom);
|
|
99
|
+
return applyCylindricalColumnUvs(geometry, ellipseCircumference(sideRadius * radiusX, sideRadius * radiusZ), height);
|
|
100
|
+
}
|
|
101
|
+
export function createColumnSphereGeometry(radius, widthSegments = 10, heightSegments = 8) {
|
|
102
|
+
return applySphericalColumnUvs(new SphereGeometry(radius, widthSegments, heightSegments), radius);
|
|
103
|
+
}
|
|
104
|
+
export function createColumnTorusGeometry({ arc = Math.PI * 2, radialSegments = 10, ringRadius, scaleX = ringRadius, scaleY = ringRadius, scaleZ = 1, tubeRadius, tubularSegments = 24, }) {
|
|
105
|
+
const geometry = new TorusGeometry(1, 0.18, radialSegments, tubularSegments, arc);
|
|
106
|
+
geometry.scale(scaleX, scaleY, scaleZ);
|
|
107
|
+
return applyTorusColumnUvs(geometry, ringRadius, tubeRadius);
|
|
108
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"door-animation-system.d.ts","sourceRoot":"","sources":["../../../src/systems/door/door-animation-system.tsx"],"names":[],"mappings":"AAYA,eAAO,MAAM,mBAAmB,YA8C/B,CAAA"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { emitter, useInteractive, useScene } from '@pascal-app/core';
|
|
2
|
+
import { useFrame } from '@react-three/fiber';
|
|
3
|
+
const easeDoorAnimation = (value) => value * value * (3 - 2 * value);
|
|
4
|
+
function markDoorDirty(doorId) {
|
|
5
|
+
const scene = useScene.getState();
|
|
6
|
+
const node = scene.nodes[doorId];
|
|
7
|
+
scene.dirtyNodes.add(doorId);
|
|
8
|
+
if (node?.parentId)
|
|
9
|
+
scene.dirtyNodes.add(node.parentId);
|
|
10
|
+
}
|
|
11
|
+
export const DoorAnimationSystem = () => {
|
|
12
|
+
useFrame(({ clock }) => {
|
|
13
|
+
const interactive = useInteractive.getState();
|
|
14
|
+
const entries = Object.entries(interactive.doorAnimations);
|
|
15
|
+
if (entries.length === 0)
|
|
16
|
+
return;
|
|
17
|
+
const now = clock.getElapsedTime() * 1000;
|
|
18
|
+
for (const [doorId, animation] of entries) {
|
|
19
|
+
const typedDoorId = doorId;
|
|
20
|
+
const scene = useScene.getState();
|
|
21
|
+
const node = scene.nodes[typedDoorId];
|
|
22
|
+
if (node?.type !== 'door') {
|
|
23
|
+
interactive.cancelDoorAnimation(typedDoorId);
|
|
24
|
+
interactive.removeDoorOpenState(typedDoorId);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const startedAt = animation.startedAt ?? now;
|
|
28
|
+
if (animation.startedAt === null) {
|
|
29
|
+
interactive.startDoorAnimation(typedDoorId, { ...animation, startedAt });
|
|
30
|
+
}
|
|
31
|
+
const progress = Math.min(1, (now - startedAt) / animation.durationMs);
|
|
32
|
+
const value = animation.from + (animation.to - animation.from) * easeDoorAnimation(progress);
|
|
33
|
+
interactive.setDoorOpenState(typedDoorId, { [animation.field]: value });
|
|
34
|
+
markDoorDirty(typedDoorId);
|
|
35
|
+
if (progress < 1)
|
|
36
|
+
continue;
|
|
37
|
+
interactive.cancelDoorAnimation(typedDoorId);
|
|
38
|
+
if (animation.persist) {
|
|
39
|
+
scene.updateNode(typedDoorId, { [animation.field]: animation.to });
|
|
40
|
+
interactive.removeDoorOpenState(typedDoorId);
|
|
41
|
+
markDoorDirty(typedDoorId);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
interactive.setDoorOpenState(typedDoorId, { [animation.field]: animation.to });
|
|
45
|
+
}
|
|
46
|
+
emitter.emit('door:animation-completed', {
|
|
47
|
+
doorId: typedDoorId,
|
|
48
|
+
field: animation.field,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}, 2);
|
|
52
|
+
return null;
|
|
53
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"door-system.d.ts","sourceRoot":"","sources":["../../../src/systems/door/door-system.tsx"],"names":[],"mappings":"AAiBA,eAAO,MAAM,UAAU,YA2BtB,CAAA"}
|