@pascal-app/viewer 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/renderers/building/building-renderer.d.ts.map +1 -1
- package/dist/components/renderers/ceiling/ceiling-renderer.d.ts.map +1 -1
- package/dist/components/renderers/ceiling/ceiling-renderer.js +4 -3
- package/dist/components/renderers/door/door-renderer.d.ts.map +1 -1
- package/dist/components/renderers/door/door-renderer.js +5 -2
- package/dist/components/renderers/fence/fence-renderer.d.ts +5 -0
- package/dist/components/renderers/fence/fence-renderer.d.ts.map +1 -0
- package/dist/components/renderers/fence/fence-renderer.js +29 -0
- package/dist/components/renderers/node-renderer.d.ts.map +1 -1
- package/dist/components/renderers/node-renderer.js +2 -1
- package/dist/components/renderers/roof/roof-renderer.d.ts.map +1 -1
- package/dist/components/renderers/roof/roof-renderer.js +31 -9
- package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts.map +1 -1
- package/dist/components/renderers/roof-segment/roof-segment-renderer.js +42 -8
- package/dist/components/renderers/site/site-renderer.d.ts.map +1 -1
- package/dist/components/renderers/site/site-renderer.js +7 -1
- package/dist/components/renderers/slab/slab-renderer.d.ts.map +1 -1
- package/dist/components/renderers/slab/slab-renderer.js +37 -8
- package/dist/components/renderers/stair/stair-renderer.d.ts.map +1 -1
- package/dist/components/renderers/stair/stair-renderer.js +605 -5
- package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts.map +1 -1
- package/dist/components/renderers/stair-segment/stair-segment-renderer.js +37 -8
- package/dist/components/renderers/wall/wall-renderer.d.ts.map +1 -1
- package/dist/components/renderers/wall/wall-renderer.js +4 -9
- package/dist/components/renderers/window/window-renderer.d.ts.map +1 -1
- package/dist/components/renderers/window/window-renderer.js +5 -2
- package/dist/components/viewer/frame-limiter.d.ts +6 -0
- package/dist/components/viewer/frame-limiter.d.ts.map +1 -0
- package/dist/components/viewer/frame-limiter.js +31 -0
- package/dist/components/viewer/ground-occluder.d.ts.map +1 -1
- package/dist/components/viewer/ground-occluder.js +5 -1
- package/dist/components/viewer/index.d.ts.map +1 -1
- package/dist/components/viewer/index.js +12 -12
- package/dist/components/viewer/post-processing.d.ts.map +1 -1
- package/dist/components/viewer/post-processing.js +72 -28
- package/dist/components/viewer/selection-manager.d.ts.map +1 -1
- package/dist/components/viewer/selection-manager.js +21 -2
- package/dist/hooks/use-node-events.d.ts +5 -1
- package/dist/hooks/use-node-events.d.ts.map +1 -1
- package/dist/hooks/use-node-events.js +2 -0
- 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 +4 -1
- package/dist/lib/materials.d.ts.map +1 -1
- package/dist/lib/materials.js +178 -2
- package/dist/lib/merged-outline-node.d.ts.map +1 -1
- package/dist/lib/merged-outline-node.js +11 -4
- package/dist/store/use-viewer.d.ts +2 -2
- package/dist/systems/guide/guide-system.d.ts.map +1 -1
- package/dist/systems/guide/guide-system.js +26 -1
- package/dist/systems/roof/roof-materials.d.ts +5 -0
- package/dist/systems/roof/roof-materials.d.ts.map +1 -0
- package/dist/systems/roof/roof-materials.js +29 -0
- package/dist/systems/scan/scan-system.d.ts.map +1 -1
- package/dist/systems/scan/scan-system.js +26 -1
- package/dist/systems/stair/stair-materials.d.ts +7 -0
- package/dist/systems/stair/stair-materials.d.ts.map +1 -0
- package/dist/systems/stair/stair-materials.js +33 -0
- package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
- package/dist/systems/wall/wall-cutout.js +39 -124
- package/dist/systems/wall/wall-materials.d.ts +16 -0
- package/dist/systems/wall/wall-materials.d.ts.map +1 -0
- package/dist/systems/wall/wall-materials.js +148 -0
- package/package.json +4 -4
package/dist/lib/materials.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { resolveMaterial } from '@pascal-app/core';
|
|
1
|
+
import { getMaterialPresetByRef, resolveMaterial, } from '@pascal-app/core';
|
|
2
2
|
import * as THREE from 'three';
|
|
3
3
|
const sideMap = {
|
|
4
4
|
front: THREE.FrontSide,
|
|
@@ -6,15 +6,185 @@ const sideMap = {
|
|
|
6
6
|
double: THREE.DoubleSide,
|
|
7
7
|
};
|
|
8
8
|
const materialCache = new Map();
|
|
9
|
+
const textureCache = new Map();
|
|
10
|
+
const textureLoadPromises = new Map();
|
|
11
|
+
const textureLoader = new THREE.TextureLoader();
|
|
12
|
+
const wrapMap = {
|
|
13
|
+
Repeat: THREE.RepeatWrapping,
|
|
14
|
+
ClampToEdge: THREE.ClampToEdgeWrapping,
|
|
15
|
+
MirroredRepeat: THREE.MirroredRepeatWrapping,
|
|
16
|
+
};
|
|
17
|
+
const SRGB_TEXTURE_SLOTS = ['map', 'emissiveMap'];
|
|
9
18
|
function getCacheKey(props) {
|
|
10
19
|
return `${props.color}-${props.roughness}-${props.metalness}-${props.opacity}-${props.transparent}-${props.side}`;
|
|
11
20
|
}
|
|
21
|
+
function getTextureKey(material) {
|
|
22
|
+
const texture = material?.texture;
|
|
23
|
+
if (!texture)
|
|
24
|
+
return 'none';
|
|
25
|
+
const repeat = texture.repeat?.join('x') ?? 'default';
|
|
26
|
+
const scale = texture.scale ?? 'default';
|
|
27
|
+
return `${texture.url}-${repeat}-${scale}`;
|
|
28
|
+
}
|
|
29
|
+
function getTexture(material) {
|
|
30
|
+
const textureConfig = material?.texture;
|
|
31
|
+
if (!textureConfig?.url)
|
|
32
|
+
return undefined;
|
|
33
|
+
const cacheKey = getTextureKey(material);
|
|
34
|
+
const cached = textureCache.get(cacheKey);
|
|
35
|
+
if (cached)
|
|
36
|
+
return cached;
|
|
37
|
+
const texture = textureLoader.load(textureConfig.url);
|
|
38
|
+
texture.wrapS = THREE.RepeatWrapping;
|
|
39
|
+
texture.wrapT = THREE.RepeatWrapping;
|
|
40
|
+
const repeatX = textureConfig.repeat?.[0] ?? textureConfig.scale ?? 1;
|
|
41
|
+
const repeatY = textureConfig.repeat?.[1] ?? textureConfig.scale ?? 1;
|
|
42
|
+
texture.repeat.set(repeatX, repeatY);
|
|
43
|
+
texture.colorSpace = THREE.SRGBColorSpace;
|
|
44
|
+
textureCache.set(cacheKey, texture);
|
|
45
|
+
return texture;
|
|
46
|
+
}
|
|
47
|
+
function isStandardMaterial(material) {
|
|
48
|
+
return (material instanceof THREE.MeshStandardMaterial ||
|
|
49
|
+
material instanceof THREE.MeshPhysicalMaterial);
|
|
50
|
+
}
|
|
51
|
+
function applyTextureProperties(texture, props, slot) {
|
|
52
|
+
texture.wrapS = wrapMap[props.wrapS];
|
|
53
|
+
texture.wrapT = wrapMap[props.wrapT];
|
|
54
|
+
texture.repeat.set(props.repeatX, props.repeatY);
|
|
55
|
+
texture.rotation = props.rotation;
|
|
56
|
+
texture.flipY = props.flipY;
|
|
57
|
+
texture.colorSpace = SRGB_TEXTURE_SLOTS.includes(slot ?? 'map')
|
|
58
|
+
? THREE.SRGBColorSpace
|
|
59
|
+
: THREE.NoColorSpace;
|
|
60
|
+
texture.needsUpdate = true;
|
|
61
|
+
return texture;
|
|
62
|
+
}
|
|
63
|
+
function getPresetTextureCacheKey(path, props, slot) {
|
|
64
|
+
return `${path}-${props.repeatX}-${props.repeatY}-${props.rotation}-${props.wrapS}-${props.wrapT}-${props.flipY}-${slot ?? 'map'}`;
|
|
65
|
+
}
|
|
66
|
+
function getPresetTexture(path, props, slot) {
|
|
67
|
+
const cacheKey = getPresetTextureCacheKey(path, props, slot);
|
|
68
|
+
const cached = textureCache.get(cacheKey);
|
|
69
|
+
if (cached)
|
|
70
|
+
return cached;
|
|
71
|
+
const texture = textureLoader.load(path);
|
|
72
|
+
applyTextureProperties(texture, props, slot);
|
|
73
|
+
textureCache.set(cacheKey, texture);
|
|
74
|
+
return texture;
|
|
75
|
+
}
|
|
76
|
+
async function loadPresetTexture(path, props, slot) {
|
|
77
|
+
const cacheKey = getPresetTextureCacheKey(path, props, slot);
|
|
78
|
+
const cached = textureCache.get(cacheKey);
|
|
79
|
+
if (cached)
|
|
80
|
+
return cached;
|
|
81
|
+
const existingPromise = textureLoadPromises.get(cacheKey);
|
|
82
|
+
if (existingPromise)
|
|
83
|
+
return existingPromise;
|
|
84
|
+
const promise = textureLoader
|
|
85
|
+
.loadAsync(path)
|
|
86
|
+
.then((texture) => {
|
|
87
|
+
applyTextureProperties(texture, props, slot);
|
|
88
|
+
textureCache.set(cacheKey, texture);
|
|
89
|
+
textureLoadPromises.delete(cacheKey);
|
|
90
|
+
return texture;
|
|
91
|
+
})
|
|
92
|
+
.catch((error) => {
|
|
93
|
+
console.warn('[viewer] Failed to load material texture', path, error);
|
|
94
|
+
textureLoadPromises.delete(cacheKey);
|
|
95
|
+
return null;
|
|
96
|
+
});
|
|
97
|
+
textureLoadPromises.set(cacheKey, promise);
|
|
98
|
+
return promise;
|
|
99
|
+
}
|
|
100
|
+
function queueTextureAssignment(material, slot, path, props) {
|
|
101
|
+
if (!path) {
|
|
102
|
+
material[slot] = null;
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const cacheKey = getPresetTextureCacheKey(path, props, slot);
|
|
106
|
+
const cached = textureCache.get(cacheKey);
|
|
107
|
+
if (cached) {
|
|
108
|
+
material[slot] = cached;
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
material[slot] = null;
|
|
112
|
+
void loadPresetTexture(path, props, slot).then((texture) => {
|
|
113
|
+
if (!texture)
|
|
114
|
+
return;
|
|
115
|
+
material[slot] = texture;
|
|
116
|
+
material.needsUpdate = true;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
function applyMaterialMapProperties(material, mapProperties) {
|
|
120
|
+
material.color.set(mapProperties.color);
|
|
121
|
+
material.roughness = mapProperties.roughness;
|
|
122
|
+
material.metalness = mapProperties.metalness;
|
|
123
|
+
material.emissiveIntensity = mapProperties.emissiveIntensity;
|
|
124
|
+
material.emissive.set(mapProperties.emissiveColor);
|
|
125
|
+
material.displacementScale = mapProperties.displacementScale;
|
|
126
|
+
material.bumpScale = mapProperties.bumpScale;
|
|
127
|
+
material.aoMapIntensity = mapProperties.aoMapIntensity;
|
|
128
|
+
material.lightMapIntensity = mapProperties.lightMapIntensity;
|
|
129
|
+
material.transparent = mapProperties.transparent;
|
|
130
|
+
material.opacity = mapProperties.opacity;
|
|
131
|
+
material.side =
|
|
132
|
+
mapProperties.side === 0
|
|
133
|
+
? THREE.FrontSide
|
|
134
|
+
: mapProperties.side === 1
|
|
135
|
+
? THREE.BackSide
|
|
136
|
+
: THREE.DoubleSide;
|
|
137
|
+
material.normalScale.set(mapProperties.normalScaleX, mapProperties.normalScaleY);
|
|
138
|
+
material.needsUpdate = true;
|
|
139
|
+
}
|
|
140
|
+
function applyMaterialPresetTextures(material, preset) {
|
|
141
|
+
const { maps, mapProperties } = preset;
|
|
142
|
+
queueTextureAssignment(material, 'map', maps.albedoMap, mapProperties);
|
|
143
|
+
queueTextureAssignment(material, 'normalMap', maps.normalMap, mapProperties);
|
|
144
|
+
queueTextureAssignment(material, 'roughnessMap', maps.roughnessMap, mapProperties);
|
|
145
|
+
queueTextureAssignment(material, 'metalnessMap', maps.metalnessMap, mapProperties);
|
|
146
|
+
queueTextureAssignment(material, 'displacementMap', maps.displacementMap, mapProperties);
|
|
147
|
+
queueTextureAssignment(material, 'aoMap', maps.aoMap, mapProperties);
|
|
148
|
+
queueTextureAssignment(material, 'bumpMap', maps.bumpMap, mapProperties);
|
|
149
|
+
queueTextureAssignment(material, 'alphaMap', maps.alphaMap, mapProperties);
|
|
150
|
+
queueTextureAssignment(material, 'lightMap', maps.lightMap, mapProperties);
|
|
151
|
+
queueTextureAssignment(material, 'emissiveMap', maps.emissiveMap, mapProperties);
|
|
152
|
+
material.needsUpdate = true;
|
|
153
|
+
}
|
|
154
|
+
export function applyMaterialPresetToMaterials(materialInput, preset) {
|
|
155
|
+
if (!preset)
|
|
156
|
+
return;
|
|
157
|
+
const materials = (Array.isArray(materialInput) ? materialInput : [materialInput]).filter(isStandardMaterial);
|
|
158
|
+
if (materials.length === 0)
|
|
159
|
+
return;
|
|
160
|
+
for (const material of materials) {
|
|
161
|
+
applyMaterialMapProperties(material, preset.mapProperties);
|
|
162
|
+
applyMaterialPresetTextures(material, preset);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
export function createMaterialFromPreset(preset) {
|
|
166
|
+
const cacheKey = JSON.stringify(preset);
|
|
167
|
+
if (materialCache.has(cacheKey)) {
|
|
168
|
+
return materialCache.get(cacheKey);
|
|
169
|
+
}
|
|
170
|
+
const material = new THREE.MeshStandardMaterial();
|
|
171
|
+
applyMaterialPresetToMaterials(material, preset);
|
|
172
|
+
materialCache.set(cacheKey, material);
|
|
173
|
+
return material;
|
|
174
|
+
}
|
|
175
|
+
export function createMaterialFromPresetRef(materialPreset) {
|
|
176
|
+
const preset = getMaterialPresetByRef(materialPreset);
|
|
177
|
+
if (!preset)
|
|
178
|
+
return null;
|
|
179
|
+
return createMaterialFromPreset(preset);
|
|
180
|
+
}
|
|
12
181
|
export function createMaterial(material) {
|
|
13
182
|
const props = resolveMaterial(material);
|
|
14
|
-
const cacheKey = getCacheKey(props)
|
|
183
|
+
const cacheKey = `${getCacheKey(props)}-${getTextureKey(material)}`;
|
|
15
184
|
if (materialCache.has(cacheKey)) {
|
|
16
185
|
return materialCache.get(cacheKey);
|
|
17
186
|
}
|
|
187
|
+
const map = getTexture(material);
|
|
18
188
|
const threeMaterial = new THREE.MeshStandardMaterial({
|
|
19
189
|
color: props.color,
|
|
20
190
|
roughness: props.roughness,
|
|
@@ -22,6 +192,7 @@ export function createMaterial(material) {
|
|
|
22
192
|
opacity: props.opacity,
|
|
23
193
|
transparent: props.transparent,
|
|
24
194
|
side: sideMap[props.side],
|
|
195
|
+
map,
|
|
25
196
|
});
|
|
26
197
|
materialCache.set(cacheKey, threeMaterial);
|
|
27
198
|
return threeMaterial;
|
|
@@ -56,4 +227,9 @@ export function clearMaterialCache() {
|
|
|
56
227
|
material.dispose();
|
|
57
228
|
}
|
|
58
229
|
materialCache.clear();
|
|
230
|
+
for (const texture of textureCache.values()) {
|
|
231
|
+
texture.dispose();
|
|
232
|
+
}
|
|
233
|
+
textureCache.clear();
|
|
234
|
+
textureLoadPromises.clear();
|
|
59
235
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merged-outline-node.d.ts","sourceRoot":"","sources":["../../src/lib/merged-outline-node.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"merged-outline-node.d.ts","sourceRoot":"","sources":["../../src/lib/merged-outline-node.ts"],"names":[],"mappings":"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"}
|
|
@@ -440,10 +440,17 @@ export class MergedOutlineNode extends TempNode {
|
|
|
440
440
|
}
|
|
441
441
|
_buildCache(objects, cache) {
|
|
442
442
|
for (const obj of objects) {
|
|
443
|
-
obj.traverse
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
443
|
+
if (!obj || !obj.traverse)
|
|
444
|
+
continue;
|
|
445
|
+
try {
|
|
446
|
+
obj.traverse((child) => {
|
|
447
|
+
if (child.isMesh || child.isSprite)
|
|
448
|
+
cache.add(child);
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
catch {
|
|
452
|
+
// Skip objects that were disposed or removed from the scene graph
|
|
453
|
+
}
|
|
447
454
|
}
|
|
448
455
|
}
|
|
449
456
|
}
|
|
@@ -61,7 +61,7 @@ declare const useViewer: import("zustand").UseBoundStore<Omit<import("zustand").
|
|
|
61
61
|
cameraMode: "perspective" | "orthographic";
|
|
62
62
|
theme: "light" | "dark";
|
|
63
63
|
unit: "metric" | "imperial";
|
|
64
|
-
levelMode: "
|
|
64
|
+
levelMode: "manual" | "stacked" | "exploded" | "solo";
|
|
65
65
|
wallMode: "up" | "cutaway" | "down";
|
|
66
66
|
projectPreferences: Record<string, {
|
|
67
67
|
showScans?: boolean;
|
|
@@ -78,7 +78,7 @@ declare const useViewer: import("zustand").UseBoundStore<Omit<import("zustand").
|
|
|
78
78
|
cameraMode: "perspective" | "orthographic";
|
|
79
79
|
theme: "light" | "dark";
|
|
80
80
|
unit: "metric" | "imperial";
|
|
81
|
-
levelMode: "
|
|
81
|
+
levelMode: "manual" | "stacked" | "exploded" | "solo";
|
|
82
82
|
wallMode: "up" | "cutaway" | "down";
|
|
83
83
|
projectPreferences: Record<string, {
|
|
84
84
|
showScans?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guide-system.d.ts","sourceRoot":"","sources":["../../../src/systems/guide/guide-system.tsx"],"names":[],"mappings":"AAIA,eAAO,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"guide-system.d.ts","sourceRoot":"","sources":["../../../src/systems/guide/guide-system.tsx"],"names":[],"mappings":"AAIA,eAAO,MAAM,WAAW,YAsCvB,CAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { sceneRegistry } from '@pascal-app/core';
|
|
1
|
+
import { emitter, sceneRegistry } from '@pascal-app/core';
|
|
2
2
|
import { useEffect } from 'react';
|
|
3
3
|
import useViewer from '../../store/use-viewer';
|
|
4
4
|
export const GuideSystem = () => {
|
|
@@ -12,5 +12,30 @@ export const GuideSystem = () => {
|
|
|
12
12
|
}
|
|
13
13
|
});
|
|
14
14
|
}, [showGuides]);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const hideForCapture = () => {
|
|
17
|
+
const guides = sceneRegistry.byType.guide || new Set();
|
|
18
|
+
guides.forEach((guideId) => {
|
|
19
|
+
const node = sceneRegistry.nodes.get(guideId);
|
|
20
|
+
if (node)
|
|
21
|
+
node.visible = false;
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
const restoreAfterCapture = () => {
|
|
25
|
+
const showGuidesNow = useViewer.getState().showGuides;
|
|
26
|
+
const guides = sceneRegistry.byType.guide || new Set();
|
|
27
|
+
guides.forEach((guideId) => {
|
|
28
|
+
const node = sceneRegistry.nodes.get(guideId);
|
|
29
|
+
if (node)
|
|
30
|
+
node.visible = showGuidesNow;
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
emitter.on('thumbnail:before-capture', hideForCapture);
|
|
34
|
+
emitter.on('thumbnail:after-capture', restoreAfterCapture);
|
|
35
|
+
return () => {
|
|
36
|
+
emitter.off('thumbnail:before-capture', hideForCapture);
|
|
37
|
+
emitter.off('thumbnail:after-capture', restoreAfterCapture);
|
|
38
|
+
};
|
|
39
|
+
}, []);
|
|
15
40
|
return null;
|
|
16
41
|
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type RoofNode } from '@pascal-app/core';
|
|
2
|
+
import * as THREE from 'three';
|
|
3
|
+
export type RoofMaterialArray = [THREE.Material, THREE.Material, THREE.Material, THREE.Material];
|
|
4
|
+
export declare function getRoofMaterialArray(node: RoofNode): RoofMaterialArray | null;
|
|
5
|
+
//# sourceMappingURL=roof-materials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roof-materials.d.ts","sourceRoot":"","sources":["../../../src/systems/roof/roof-materials.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;AAiBhG,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,QAAQ,GAAG,iBAAiB,GAAG,IAAI,CAmB7E"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { getEffectiveRoofSurfaceMaterial, } from '@pascal-app/core';
|
|
2
|
+
import * as THREE from 'three';
|
|
3
|
+
import { createMaterial, createMaterialFromPresetRef } from '../../lib/materials';
|
|
4
|
+
function createResolvedMaterial(material, materialPreset) {
|
|
5
|
+
if (materialPreset) {
|
|
6
|
+
return createMaterialFromPresetRef(materialPreset);
|
|
7
|
+
}
|
|
8
|
+
if (material) {
|
|
9
|
+
return createMaterial(material);
|
|
10
|
+
}
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
export function getRoofMaterialArray(node) {
|
|
14
|
+
const top = getEffectiveRoofSurfaceMaterial(node, 'top');
|
|
15
|
+
const edge = getEffectiveRoofSurfaceMaterial(node, 'edge');
|
|
16
|
+
const wall = getEffectiveRoofSurfaceMaterial(node, 'wall');
|
|
17
|
+
const topMaterial = createResolvedMaterial(top.material, top.materialPreset);
|
|
18
|
+
const edgeMaterial = createResolvedMaterial(edge.material, edge.materialPreset);
|
|
19
|
+
const wallMaterial = createResolvedMaterial(wall.material, wall.materialPreset);
|
|
20
|
+
if (!(topMaterial || edgeMaterial || wallMaterial)) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
return [
|
|
24
|
+
edgeMaterial ?? wallMaterial ?? topMaterial ?? new THREE.MeshStandardMaterial(),
|
|
25
|
+
wallMaterial ?? edgeMaterial ?? topMaterial ?? new THREE.MeshStandardMaterial(),
|
|
26
|
+
wallMaterial ?? edgeMaterial ?? topMaterial ?? new THREE.MeshStandardMaterial(),
|
|
27
|
+
topMaterial ?? wallMaterial ?? edgeMaterial ?? new THREE.MeshStandardMaterial(),
|
|
28
|
+
];
|
|
29
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scan-system.d.ts","sourceRoot":"","sources":["../../../src/systems/scan/scan-system.tsx"],"names":[],"mappings":"AAIA,eAAO,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"scan-system.d.ts","sourceRoot":"","sources":["../../../src/systems/scan/scan-system.tsx"],"names":[],"mappings":"AAIA,eAAO,MAAM,UAAU,YAsCtB,CAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { sceneRegistry } from '@pascal-app/core';
|
|
1
|
+
import { emitter, sceneRegistry } from '@pascal-app/core';
|
|
2
2
|
import { useEffect } from 'react';
|
|
3
3
|
import useViewer from '../../store/use-viewer';
|
|
4
4
|
export const ScanSystem = () => {
|
|
@@ -12,5 +12,30 @@ export const ScanSystem = () => {
|
|
|
12
12
|
}
|
|
13
13
|
});
|
|
14
14
|
}, [showScans]);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const hideForCapture = () => {
|
|
17
|
+
const scans = sceneRegistry.byType.scan || new Set();
|
|
18
|
+
scans.forEach((scanId) => {
|
|
19
|
+
const node = sceneRegistry.nodes.get(scanId);
|
|
20
|
+
if (node)
|
|
21
|
+
node.visible = false;
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
const restoreAfterCapture = () => {
|
|
25
|
+
const showScansNow = useViewer.getState().showScans;
|
|
26
|
+
const scans = sceneRegistry.byType.scan || new Set();
|
|
27
|
+
scans.forEach((scanId) => {
|
|
28
|
+
const node = sceneRegistry.nodes.get(scanId);
|
|
29
|
+
if (node)
|
|
30
|
+
node.visible = showScansNow;
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
emitter.on('thumbnail:before-capture', hideForCapture);
|
|
34
|
+
emitter.on('thumbnail:after-capture', restoreAfterCapture);
|
|
35
|
+
return () => {
|
|
36
|
+
emitter.off('thumbnail:before-capture', hideForCapture);
|
|
37
|
+
emitter.off('thumbnail:after-capture', restoreAfterCapture);
|
|
38
|
+
};
|
|
39
|
+
}, []);
|
|
15
40
|
return null;
|
|
16
41
|
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type StairNode, type StairSegmentNode } from '@pascal-app/core';
|
|
2
|
+
import type * as THREE from 'three';
|
|
3
|
+
export type StairBodyMaterials = [THREE.Material, THREE.Material];
|
|
4
|
+
export declare function getStairBodyMaterials(stair: StairNode): StairBodyMaterials;
|
|
5
|
+
export declare function getStairRailingMaterial(stair: StairNode): THREE.Material;
|
|
6
|
+
export declare function getStraightStairSegmentBodyMaterials(segment: StairSegmentNode, parentNode?: StairNode): StairBodyMaterials;
|
|
7
|
+
//# sourceMappingURL=stair-materials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stair-materials.d.ts","sourceRoot":"","sources":["../../../src/systems/stair/stair-materials.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,gBAAgB,EACtB,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAA;AAOnC,MAAM,MAAM,kBAAkB,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;AAiBjE,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,SAAS,GAAG,kBAAkB,CAQ1E;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,SAAS,GAAG,KAAK,CAAC,QAAQ,CAGxE;AAED,wBAAgB,oCAAoC,CAClD,OAAO,EAAE,gBAAgB,EACzB,UAAU,CAAC,EAAE,SAAS,GACrB,kBAAkB,CAWpB"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { getEffectiveStairSurfaceMaterial, } from '@pascal-app/core';
|
|
2
|
+
import { createMaterial, createMaterialFromPresetRef, DEFAULT_STAIR_MATERIAL, } from '../../lib/materials';
|
|
3
|
+
function createResolvedMaterial(material, materialPreset) {
|
|
4
|
+
if (materialPreset) {
|
|
5
|
+
return createMaterialFromPresetRef(materialPreset) ?? DEFAULT_STAIR_MATERIAL;
|
|
6
|
+
}
|
|
7
|
+
if (material) {
|
|
8
|
+
return createMaterial(material);
|
|
9
|
+
}
|
|
10
|
+
return DEFAULT_STAIR_MATERIAL;
|
|
11
|
+
}
|
|
12
|
+
export function getStairBodyMaterials(stair) {
|
|
13
|
+
const tread = getEffectiveStairSurfaceMaterial(stair, 'tread');
|
|
14
|
+
const side = getEffectiveStairSurfaceMaterial(stair, 'side');
|
|
15
|
+
return [
|
|
16
|
+
createResolvedMaterial(tread.material, tread.materialPreset),
|
|
17
|
+
createResolvedMaterial(side.material, side.materialPreset),
|
|
18
|
+
];
|
|
19
|
+
}
|
|
20
|
+
export function getStairRailingMaterial(stair) {
|
|
21
|
+
const railing = getEffectiveStairSurfaceMaterial(stair, 'railing');
|
|
22
|
+
return createResolvedMaterial(railing.material, railing.materialPreset);
|
|
23
|
+
}
|
|
24
|
+
export function getStraightStairSegmentBodyMaterials(segment, parentNode) {
|
|
25
|
+
if (segment.material !== undefined || typeof segment.materialPreset === 'string') {
|
|
26
|
+
const override = createResolvedMaterial(segment.material, segment.materialPreset);
|
|
27
|
+
return [override, override];
|
|
28
|
+
}
|
|
29
|
+
if (parentNode) {
|
|
30
|
+
return getStairBodyMaterials(parentNode);
|
|
31
|
+
}
|
|
32
|
+
return [DEFAULT_STAIR_MATERIAL, DEFAULT_STAIR_MATERIAL];
|
|
33
|
+
}
|
|
@@ -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":"AAsCA,eAAO,MAAM,UAAU,YAkHtB,CAAA"}
|
|
@@ -1,129 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { emitter, sceneRegistry, useScene } from '@pascal-app/core';
|
|
2
2
|
import { useFrame } from '@react-three/fiber';
|
|
3
|
-
import { useRef } from 'react';
|
|
4
|
-
import {
|
|
5
|
-
import { Fn, float, fract, length, mix, positionLocal, smoothstep, step, vec2 } from 'three/tsl';
|
|
6
|
-
import { MeshStandardNodeMaterial, Vector3 } from 'three/webgpu';
|
|
3
|
+
import { useEffect, useRef } from 'react';
|
|
4
|
+
import { Vector3 } from 'three/webgpu';
|
|
7
5
|
import useViewer from '../../store/use-viewer';
|
|
6
|
+
import { getMaterialsForWall } from './wall-materials';
|
|
8
7
|
const tmpVec = new Vector3();
|
|
9
8
|
const u = new Vector3();
|
|
10
9
|
const v = new Vector3();
|
|
11
|
-
const DEFAULT_WALL_COLOR = '#f2f0ed';
|
|
12
|
-
const WALL_HIGHLIGHT_PROFILES = {
|
|
13
|
-
delete: {
|
|
14
|
-
color: new Color('#dc2626'),
|
|
15
|
-
blend: 0.78,
|
|
16
|
-
emissiveIntensity: 0.46,
|
|
17
|
-
},
|
|
18
|
-
selection: {
|
|
19
|
-
color: new Color('#818cf8'),
|
|
20
|
-
blend: 0.32,
|
|
21
|
-
emissiveIntensity: 0.42,
|
|
22
|
-
},
|
|
23
|
-
};
|
|
24
|
-
const dotPattern = Fn(() => {
|
|
25
|
-
const scale = float(0.1);
|
|
26
|
-
const dotSize = float(0.3);
|
|
27
|
-
const uv = vec2(positionLocal.x, positionLocal.y).div(scale);
|
|
28
|
-
const gridUV = fract(uv);
|
|
29
|
-
const dist = length(gridUV.sub(0.5));
|
|
30
|
-
const dots = step(dist, dotSize.mul(0.5));
|
|
31
|
-
const fadeHeight = float(2.5);
|
|
32
|
-
const yFade = float(1).sub(smoothstep(float(0), fadeHeight, positionLocal.y));
|
|
33
|
-
return dots.mul(yFade);
|
|
34
|
-
});
|
|
35
|
-
const wallMaterialCache = new Map();
|
|
36
|
-
const presetColors = {
|
|
37
|
-
white: '#ffffff',
|
|
38
|
-
brick: '#8b4513',
|
|
39
|
-
concrete: '#808080',
|
|
40
|
-
wood: '#deb887',
|
|
41
|
-
glass: '#87ceeb',
|
|
42
|
-
metal: '#c0c0c0',
|
|
43
|
-
plaster: '#f5f5dc',
|
|
44
|
-
tile: '#dcdcdc',
|
|
45
|
-
marble: '#f5f5f5',
|
|
46
|
-
};
|
|
47
|
-
function getMaterialHash(wallNode) {
|
|
48
|
-
if (!wallNode.material)
|
|
49
|
-
return 'none';
|
|
50
|
-
const mat = wallNode.material;
|
|
51
|
-
if (mat.preset && mat.preset !== 'custom') {
|
|
52
|
-
return `preset-${mat.preset}`;
|
|
53
|
-
}
|
|
54
|
-
if (mat.properties) {
|
|
55
|
-
return `props-${mat.properties.color}-${mat.properties.roughness}-${mat.properties.metalness}`;
|
|
56
|
-
}
|
|
57
|
-
return 'default';
|
|
58
|
-
}
|
|
59
|
-
function getPresetColor(preset) {
|
|
60
|
-
return presetColors[preset] ?? '#ffffff';
|
|
61
|
-
}
|
|
62
|
-
function getHighlightedColor(color, kind) {
|
|
63
|
-
const profile = WALL_HIGHLIGHT_PROFILES[kind];
|
|
64
|
-
return new Color(color).lerp(profile.color, profile.blend);
|
|
65
|
-
}
|
|
66
|
-
function createHighlightedWallMaterial(material, baseColor, kind) {
|
|
67
|
-
const highlightedMaterial = material.clone();
|
|
68
|
-
const highlightedColor = getHighlightedColor(baseColor, kind);
|
|
69
|
-
const profile = WALL_HIGHLIGHT_PROFILES[kind];
|
|
70
|
-
highlightedMaterial.color = highlightedColor;
|
|
71
|
-
highlightedMaterial.emissive = highlightedColor.clone();
|
|
72
|
-
highlightedMaterial.emissiveIntensity = profile.emissiveIntensity;
|
|
73
|
-
return highlightedMaterial;
|
|
74
|
-
}
|
|
75
|
-
function getMaterialsForWall(wallNode) {
|
|
76
|
-
const cacheKey = wallNode.id;
|
|
77
|
-
const materialHash = getMaterialHash(wallNode);
|
|
78
|
-
const existing = wallMaterialCache.get(cacheKey);
|
|
79
|
-
if (existing && existing.materialHash === materialHash) {
|
|
80
|
-
return existing;
|
|
81
|
-
}
|
|
82
|
-
if (existing) {
|
|
83
|
-
existing.visible.dispose();
|
|
84
|
-
existing.invisible.dispose();
|
|
85
|
-
existing.deleteVisible.dispose();
|
|
86
|
-
existing.deleteInvisible.dispose();
|
|
87
|
-
existing.highlightedVisible.dispose();
|
|
88
|
-
existing.highlightedInvisible.dispose();
|
|
89
|
-
}
|
|
90
|
-
let userColor = DEFAULT_WALL_COLOR;
|
|
91
|
-
if (wallNode.material?.properties?.color) {
|
|
92
|
-
userColor = wallNode.material.properties.color;
|
|
93
|
-
}
|
|
94
|
-
else if (wallNode.material?.preset && wallNode.material.preset !== 'custom') {
|
|
95
|
-
userColor = getPresetColor(wallNode.material.preset);
|
|
96
|
-
}
|
|
97
|
-
const visibleMat = wallNode.material
|
|
98
|
-
? new MeshStandardNodeMaterial({
|
|
99
|
-
color: userColor,
|
|
100
|
-
roughness: 1,
|
|
101
|
-
metalness: 0,
|
|
102
|
-
})
|
|
103
|
-
: baseMaterial.clone();
|
|
104
|
-
const invisibleMat = new MeshStandardNodeMaterial({
|
|
105
|
-
transparent: true,
|
|
106
|
-
opacityNode: mix(float(0.0), float(0.24), dotPattern()),
|
|
107
|
-
color: userColor,
|
|
108
|
-
depthWrite: false,
|
|
109
|
-
emissive: userColor,
|
|
110
|
-
});
|
|
111
|
-
const highlightedVisible = createHighlightedWallMaterial(visibleMat, userColor, 'selection');
|
|
112
|
-
const highlightedInvisible = createHighlightedWallMaterial(invisibleMat, userColor, 'selection');
|
|
113
|
-
const deleteVisible = createHighlightedWallMaterial(visibleMat, userColor, 'delete');
|
|
114
|
-
const deleteInvisible = createHighlightedWallMaterial(invisibleMat, userColor, 'delete');
|
|
115
|
-
const result = {
|
|
116
|
-
visible: visibleMat,
|
|
117
|
-
invisible: invisibleMat,
|
|
118
|
-
deleteVisible,
|
|
119
|
-
deleteInvisible,
|
|
120
|
-
highlightedVisible,
|
|
121
|
-
highlightedInvisible,
|
|
122
|
-
materialHash,
|
|
123
|
-
};
|
|
124
|
-
wallMaterialCache.set(cacheKey, result);
|
|
125
|
-
return result;
|
|
126
|
-
}
|
|
127
10
|
function getWallHideState(wallNode, wallMesh, wallMode, cameraDir) {
|
|
128
11
|
let hideWall = wallNode.frontSide === 'interior' && wallNode.backSide === 'interior';
|
|
129
12
|
if (wallMode === 'up') {
|
|
@@ -206,9 +89,7 @@ export const WallCutout = () => {
|
|
|
206
89
|
? materials.deleteVisible
|
|
207
90
|
: isSelectionHighlighted
|
|
208
91
|
? materials.highlightedVisible
|
|
209
|
-
:
|
|
210
|
-
? materials.visible
|
|
211
|
-
: baseMaterial;
|
|
92
|
+
: materials.visible;
|
|
212
93
|
}
|
|
213
94
|
});
|
|
214
95
|
lastWallMode.current = wallMode;
|
|
@@ -216,5 +97,39 @@ export const WallCutout = () => {
|
|
|
216
97
|
lastHighlightKey.current = highlightKey;
|
|
217
98
|
}
|
|
218
99
|
});
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
const snapshot = new Map();
|
|
102
|
+
const restoreForCapture = () => {
|
|
103
|
+
sceneRegistry.byType.wall.forEach((wallId) => {
|
|
104
|
+
const wallMesh = sceneRegistry.nodes.get(wallId);
|
|
105
|
+
if (!wallMesh)
|
|
106
|
+
return;
|
|
107
|
+
const wallNode = useScene.getState().nodes[wallId];
|
|
108
|
+
if (!wallNode || wallNode.type !== 'wall')
|
|
109
|
+
return;
|
|
110
|
+
const mats = getMaterialsForWall(wallNode);
|
|
111
|
+
const current = wallMesh.material;
|
|
112
|
+
snapshot.set(wallMesh, current);
|
|
113
|
+
if (current === mats.highlightedVisible || current === mats.deleteVisible) {
|
|
114
|
+
wallMesh.material = mats.visible;
|
|
115
|
+
}
|
|
116
|
+
else if (current === mats.highlightedInvisible || current === mats.deleteInvisible) {
|
|
117
|
+
wallMesh.material = mats.invisible;
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
const reapplyAfterCapture = () => {
|
|
122
|
+
snapshot.forEach((mat, mesh) => {
|
|
123
|
+
mesh.material = mat;
|
|
124
|
+
});
|
|
125
|
+
snapshot.clear();
|
|
126
|
+
};
|
|
127
|
+
emitter.on('thumbnail:before-capture', restoreForCapture);
|
|
128
|
+
emitter.on('thumbnail:after-capture', reapplyAfterCapture);
|
|
129
|
+
return () => {
|
|
130
|
+
emitter.off('thumbnail:before-capture', restoreForCapture);
|
|
131
|
+
emitter.off('thumbnail:after-capture', reapplyAfterCapture);
|
|
132
|
+
};
|
|
133
|
+
}, []);
|
|
219
134
|
return null;
|
|
220
135
|
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type WallNode } from '@pascal-app/core';
|
|
2
|
+
import { type Material } from 'three';
|
|
3
|
+
export type WallMaterialArray = [Material, Material, Material];
|
|
4
|
+
export interface WallMaterials {
|
|
5
|
+
visible: WallMaterialArray;
|
|
6
|
+
invisible: WallMaterialArray;
|
|
7
|
+
deleteVisible: WallMaterialArray;
|
|
8
|
+
deleteInvisible: WallMaterialArray;
|
|
9
|
+
highlightedVisible: WallMaterialArray;
|
|
10
|
+
highlightedInvisible: WallMaterialArray;
|
|
11
|
+
materialHash: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function getWallMaterialHash(wallNode: WallNode): string;
|
|
14
|
+
export declare function getMaterialsForWall(wallNode: WallNode): WallMaterials;
|
|
15
|
+
export declare function getVisibleWallMaterials(wallNode: WallNode): WallMaterialArray;
|
|
16
|
+
//# sourceMappingURL=wall-materials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wall-materials.d.ts","sourceRoot":"","sources":["../../../src/systems/wall/wall-materials.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,QAAQ,EAEd,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAS,KAAK,QAAQ,EAAE,MAAM,OAAO,CAAA;AAwB5C,MAAM,MAAM,iBAAiB,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAA;AAE9D,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,iBAAiB,CAAA;IAC1B,SAAS,EAAE,iBAAiB,CAAA;IAC5B,aAAa,EAAE,iBAAiB,CAAA;IAChC,eAAe,EAAE,iBAAiB,CAAA;IAClC,kBAAkB,EAAE,iBAAiB,CAAA;IACrC,oBAAoB,EAAE,iBAAiB,CAAA;IACvC,YAAY,EAAE,MAAM,CAAA;CACrB;AA4GD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAS9D;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,GAAG,aAAa,CA2DrE;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,QAAQ,GAAG,iBAAiB,CAE7E"}
|