@pascal-app/viewer 1.0.6 → 1.0.7
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/viewer/daylight-atmosphere.d.ts +9 -0
- package/dist/components/viewer/daylight-atmosphere.d.ts.map +1 -0
- package/dist/components/viewer/daylight-atmosphere.js +315 -0
- package/dist/components/viewer/drone-controller.d.ts +15 -0
- package/dist/components/viewer/drone-controller.d.ts.map +1 -0
- package/dist/components/viewer/drone-controller.js +194 -0
- package/dist/components/viewer/glb-interactive.d.ts +4 -18
- package/dist/components/viewer/glb-interactive.d.ts.map +1 -1
- package/dist/components/viewer/glb-interactive.js +32 -268
- package/dist/components/viewer/glb-scene-motion.d.ts +8 -0
- package/dist/components/viewer/glb-scene-motion.d.ts.map +1 -0
- package/dist/components/viewer/glb-scene-motion.js +57 -0
- package/dist/components/viewer/glb-scene.d.ts +3 -1
- package/dist/components/viewer/glb-scene.d.ts.map +1 -1
- package/dist/components/viewer/glb-scene.js +289 -49
- package/dist/components/viewer/glb-selection-callout.d.ts +17 -0
- package/dist/components/viewer/glb-selection-callout.d.ts.map +1 -0
- package/dist/components/viewer/glb-selection-callout.js +54 -0
- package/dist/components/viewer/glb-walkthrough-controller.d.ts.map +1 -1
- package/dist/components/viewer/glb-walkthrough-controller.js +6 -3
- package/dist/components/viewer/index.d.ts +5 -0
- package/dist/components/viewer/index.d.ts.map +1 -1
- package/dist/components/viewer/index.js +30 -6
- package/dist/components/viewer/lights.d.ts.map +1 -1
- package/dist/components/viewer/lights.js +192 -5
- package/dist/components/viewer/post-processing.d.ts.map +1 -1
- package/dist/components/viewer/post-processing.js +150 -94
- package/dist/components/viewer/scene-environment.js +4 -5
- package/dist/components/viewer/selection-callout.d.ts +51 -0
- package/dist/components/viewer/selection-callout.d.ts.map +1 -0
- package/dist/components/viewer/selection-callout.js +197 -0
- package/dist/components/viewer/use-daylight-rooms.d.ts +13 -0
- package/dist/components/viewer/use-daylight-rooms.d.ts.map +1 -0
- package/dist/components/viewer/use-daylight-rooms.js +236 -0
- package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
- package/dist/components/viewer/viewer-camera.js +12 -1
- package/dist/index.d.ts +21 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -7
- package/dist/lib/daylight/ground.d.ts +21 -0
- package/dist/lib/daylight/ground.d.ts.map +1 -0
- package/dist/lib/daylight/ground.js +53 -0
- package/dist/lib/daylight/location.d.ts +32 -0
- package/dist/lib/daylight/location.d.ts.map +1 -0
- package/dist/lib/daylight/location.js +74 -0
- package/dist/lib/daylight/look.d.ts +33 -0
- package/dist/lib/daylight/look.d.ts.map +1 -0
- package/dist/lib/daylight/look.js +13 -0
- package/dist/lib/daylight/material-hooks.d.ts +2 -0
- package/dist/lib/daylight/material-hooks.d.ts.map +1 -0
- package/dist/lib/daylight/material-hooks.js +150 -0
- package/dist/lib/daylight/room-context.d.ts +12 -0
- package/dist/lib/daylight/room-context.d.ts.map +1 -0
- package/dist/lib/daylight/room-context.js +53 -0
- package/dist/lib/daylight/room-scene.d.ts +54 -0
- package/dist/lib/daylight/room-scene.d.ts.map +1 -0
- package/dist/lib/daylight/room-scene.js +356 -0
- package/dist/lib/daylight/rooms.d.ts +262 -0
- package/dist/lib/daylight/rooms.d.ts.map +1 -0
- package/dist/lib/daylight/rooms.js +1223 -0
- package/dist/lib/daylight/runtime.d.ts +33 -0
- package/dist/lib/daylight/runtime.d.ts.map +1 -0
- package/dist/lib/daylight/runtime.js +42 -0
- package/dist/lib/daylight/sky.d.ts +26 -0
- package/dist/lib/daylight/sky.d.ts.map +1 -0
- package/dist/lib/daylight/sky.js +217 -0
- package/dist/lib/daylight/solar.d.ts +46 -0
- package/dist/lib/daylight/solar.d.ts.map +1 -0
- package/dist/lib/daylight/solar.js +156 -0
- package/dist/lib/device-tier.d.ts +29 -0
- package/dist/lib/device-tier.d.ts.map +1 -0
- package/dist/lib/device-tier.js +71 -0
- package/dist/lib/dispose-object3d.d.ts.map +1 -1
- package/dist/lib/dispose-object3d.js +4 -2
- package/dist/lib/drone-flight.d.ts +44 -0
- package/dist/lib/drone-flight.d.ts.map +1 -0
- package/dist/lib/drone-flight.js +121 -0
- package/dist/lib/editor-highlight.d.ts +16 -0
- package/dist/lib/editor-highlight.d.ts.map +1 -0
- package/dist/lib/editor-highlight.js +33 -0
- package/dist/lib/foundation-materials.d.ts.map +1 -1
- package/dist/lib/foundation-materials.js +2 -4
- package/dist/lib/glb-merge.d.ts +23 -0
- package/dist/lib/glb-merge.d.ts.map +1 -0
- package/dist/lib/glb-merge.js +184 -0
- package/dist/lib/hardware-geometry.d.ts +21 -0
- package/dist/lib/hardware-geometry.d.ts.map +1 -0
- package/dist/lib/hardware-geometry.js +283 -0
- package/dist/lib/layer-pass.d.ts +2 -0
- package/dist/lib/layer-pass.d.ts.map +1 -1
- package/dist/lib/layer-pass.js +20 -0
- package/dist/lib/materials.d.ts.map +1 -1
- package/dist/lib/materials.js +50 -23
- package/dist/lib/merged-outline-node.d.ts.map +1 -1
- package/dist/lib/merged-outline-node.js +7 -1
- package/dist/lib/occlusion-culler.d.ts +34 -0
- package/dist/lib/occlusion-culler.d.ts.map +1 -0
- package/dist/lib/occlusion-culler.js +698 -0
- package/dist/lib/post-processing-resources.d.ts +4 -0
- package/dist/lib/post-processing-resources.d.ts.map +1 -1
- package/dist/lib/post-processing-resources.js +5 -0
- package/dist/lib/readback-rows.d.ts +14 -0
- package/dist/lib/readback-rows.d.ts.map +1 -0
- package/dist/lib/readback-rows.js +31 -0
- package/dist/lib/reveal-pose.d.ts.map +1 -1
- package/dist/lib/reveal-pose.js +3 -0
- package/dist/lib/scene-matrices.d.ts +12 -0
- package/dist/lib/scene-matrices.d.ts.map +1 -0
- package/dist/lib/scene-matrices.js +287 -0
- package/dist/lib/scene-pass-mrt.d.ts +5 -2
- package/dist/lib/scene-pass-mrt.d.ts.map +1 -1
- package/dist/lib/scene-pass-mrt.js +7 -2
- package/dist/lib/scene-themes.d.ts +0 -2
- package/dist/lib/scene-themes.d.ts.map +1 -1
- package/dist/lib/scene-themes.js +0 -2
- package/dist/lib/scene-visibility.d.ts +2 -0
- package/dist/lib/scene-visibility.d.ts.map +1 -1
- package/dist/lib/scene-visibility.js +12 -3
- package/dist/lib/site-surfaces.d.ts +1 -8
- package/dist/lib/site-surfaces.d.ts.map +1 -1
- package/dist/lib/site-surfaces.js +0 -19
- package/dist/lib/snapshot-pipeline.d.ts.map +1 -1
- package/dist/lib/snapshot-pipeline.js +24 -40
- package/dist/lib/texture-reference.d.ts.map +1 -1
- package/dist/lib/texture-reference.js +10 -8
- package/dist/lib/underground-ambiance.d.ts +0 -2
- package/dist/lib/underground-ambiance.d.ts.map +1 -1
- package/dist/lib/underground-ambiance.js +0 -2
- package/dist/store/use-item-light-pool.d.ts +3 -6
- package/dist/store/use-item-light-pool.d.ts.map +1 -1
- package/dist/store/use-item-light-pool.js +12 -0
- package/dist/store/use-viewer.d.ts +72 -15
- package/dist/store/use-viewer.d.ts.map +1 -1
- package/dist/store/use-viewer.js +103 -18
- package/dist/systems/ceiling/ceiling-system.d.ts +5 -0
- package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
- package/dist/systems/ceiling/ceiling-system.js +11 -1
- package/dist/systems/door/door-system.d.ts.map +1 -1
- package/dist/systems/door/door-system.js +501 -145
- package/dist/systems/door/leaf-profile.d.ts +11 -0
- package/dist/systems/door/leaf-profile.d.ts.map +1 -0
- package/dist/systems/door/leaf-profile.js +140 -0
- package/dist/systems/interactive/callout-actions.d.ts +72 -0
- package/dist/systems/interactive/callout-actions.d.ts.map +1 -0
- package/dist/systems/interactive/callout-actions.js +212 -0
- package/dist/systems/interactive/interactive-system.d.ts +5 -1
- package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
- package/dist/systems/interactive/interactive-system.js +44 -121
- package/dist/systems/interactive/opening-settled.d.ts +16 -0
- package/dist/systems/interactive/opening-settled.d.ts.map +1 -0
- package/dist/systems/interactive/opening-settled.js +53 -0
- package/dist/systems/item-light/item-light-system.d.ts.map +1 -1
- package/dist/systems/item-light/item-light-system.js +22 -214
- package/dist/systems/item-light/pooled-lights.d.ts +70 -0
- package/dist/systems/item-light/pooled-lights.d.ts.map +1 -0
- package/dist/systems/item-light/pooled-lights.js +449 -0
- package/dist/systems/level/level-system.d.ts.map +1 -1
- package/dist/systems/level/level-system.js +19 -1
- package/dist/systems/roof/roof-material-slots.d.ts +14 -0
- package/dist/systems/roof/roof-material-slots.d.ts.map +1 -0
- package/dist/systems/roof/roof-material-slots.js +13 -0
- package/dist/systems/roof/roof-materials.d.ts +25 -2
- package/dist/systems/roof/roof-materials.d.ts.map +1 -1
- package/dist/systems/roof/roof-materials.js +85 -26
- package/dist/systems/roof/roof-system.d.ts +34 -1
- package/dist/systems/roof/roof-system.d.ts.map +1 -1
- package/dist/systems/roof/roof-system.js +477 -105
- package/dist/systems/wall/opening-casing-geometry.d.ts +14 -0
- package/dist/systems/wall/opening-casing-geometry.d.ts.map +1 -0
- package/dist/systems/wall/opening-casing-geometry.js +102 -0
- package/dist/systems/wall/opening-cutout-geometry.d.ts +9 -0
- package/dist/systems/wall/opening-cutout-geometry.d.ts.map +1 -1
- package/dist/systems/wall/opening-cutout-geometry.js +7 -3
- package/dist/systems/wall/wall-materials.d.ts.map +1 -1
- package/dist/systems/wall/wall-materials.js +28 -35
- package/dist/systems/wall/wall-system.d.ts.map +1 -1
- package/dist/systems/wall/wall-system.js +34 -19
- package/dist/systems/wall/wall-t-junctions.d.ts +15 -0
- package/dist/systems/wall/wall-t-junctions.d.ts.map +1 -0
- package/dist/systems/wall/wall-t-junctions.js +149 -0
- package/dist/systems/window/window-animation-system.d.ts +3 -12
- package/dist/systems/window/window-animation-system.d.ts.map +1 -1
- package/dist/systems/window/window-animation-system.js +37 -122
- package/dist/systems/window/window-pose.d.ts +31 -0
- package/dist/systems/window/window-pose.d.ts.map +1 -0
- package/dist/systems/window/window-pose.js +152 -0
- package/dist/systems/window/window-roller-shutter.d.ts +8 -0
- package/dist/systems/window/window-roller-shutter.d.ts.map +1 -0
- package/dist/systems/window/window-roller-shutter.js +64 -0
- package/dist/systems/window/window-system.d.ts +2 -11
- package/dist/systems/window/window-system.d.ts.map +1 -1
- package/dist/systems/window/window-system.js +470 -303
- package/package.json +6 -6
- package/dist/systems/interactive/control-widget.d.ts +0 -10
- package/dist/systems/interactive/control-widget.d.ts.map +0 -1
- package/dist/systems/interactive/control-widget.js +0 -35
- package/dist/systems/interactive/procedural-controls.d.ts +0 -13
- package/dist/systems/interactive/procedural-controls.d.ts.map +0 -1
- package/dist/systems/interactive/procedural-controls.js +0 -17
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { BoxGeometry, CapsuleGeometry, CatmullRomCurve3, CylinderGeometry, LatheGeometry, SphereGeometry, TubeGeometry, Vector2, Vector3, } from 'three';
|
|
2
|
+
import { RoundedBoxGeometry } from 'three/addons/geometries/RoundedBoxGeometry.js';
|
|
3
|
+
import { mergeGeometries } from 'three/addons/utils/BufferGeometryUtils.js';
|
|
4
|
+
const cache = new Map();
|
|
5
|
+
const mm = (value) => Math.round(value * 1000);
|
|
6
|
+
function cached(name, dimensions, build) {
|
|
7
|
+
const sizes = dimensions.map(mm);
|
|
8
|
+
const key = `${name}:${sizes.join(':')}`;
|
|
9
|
+
let geometry = cache.get(key);
|
|
10
|
+
if (!geometry) {
|
|
11
|
+
geometry = build(...sizes.map((size) => size / 1000));
|
|
12
|
+
geometry.userData.shared = true;
|
|
13
|
+
cache.set(key, geometry);
|
|
14
|
+
}
|
|
15
|
+
return geometry;
|
|
16
|
+
}
|
|
17
|
+
function scaleUVs(geometry, u, v) {
|
|
18
|
+
const uv = geometry.getAttribute('uv');
|
|
19
|
+
for (let i = 0; i < uv.count; i++)
|
|
20
|
+
uv.setXY(i, uv.getX(i) * u, uv.getY(i) * v);
|
|
21
|
+
}
|
|
22
|
+
function lathe(profile, segments) {
|
|
23
|
+
const geometry = new LatheGeometry(profile.map(([r, y]) => new Vector2(r, y)), segments);
|
|
24
|
+
geometry.normalizeNormals();
|
|
25
|
+
const uv = geometry.getAttribute('uv');
|
|
26
|
+
let distance = 0;
|
|
27
|
+
for (let j = 0; j < profile.length; j++) {
|
|
28
|
+
const [r, y] = profile[j];
|
|
29
|
+
if (j > 0) {
|
|
30
|
+
const [previousR, previousY] = profile[j - 1];
|
|
31
|
+
distance += Math.hypot(r - previousR, y - previousY);
|
|
32
|
+
}
|
|
33
|
+
for (let i = 0; i <= segments; i++) {
|
|
34
|
+
uv.setXY(i * profile.length + j, (i / segments) * 2 * Math.PI * r, distance);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return geometry;
|
|
38
|
+
}
|
|
39
|
+
function cylinder(radius, length, segments) {
|
|
40
|
+
const geometry = new CylinderGeometry(radius, radius, length, segments);
|
|
41
|
+
const uv = geometry.getAttribute('uv');
|
|
42
|
+
const position = geometry.getAttribute('position');
|
|
43
|
+
const sideVertices = (segments + 1) * 2;
|
|
44
|
+
for (let i = 0; i < uv.count; i++) {
|
|
45
|
+
if (i < sideVertices) {
|
|
46
|
+
uv.setXY(i, uv.getX(i) * 2 * Math.PI * radius, uv.getY(i) * length);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
uv.setXY(i, position.getX(i), position.getZ(i));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return geometry;
|
|
53
|
+
}
|
|
54
|
+
function merge(parts) {
|
|
55
|
+
const indexed = parts.every((part) => part.index !== null);
|
|
56
|
+
const normalized = parts.map((part) => (!indexed && part.index ? part.toNonIndexed() : part));
|
|
57
|
+
const geometry = mergeGeometries(normalized);
|
|
58
|
+
for (const part of normalized)
|
|
59
|
+
if (!parts.includes(part))
|
|
60
|
+
part.dispose();
|
|
61
|
+
for (const part of parts)
|
|
62
|
+
part.dispose();
|
|
63
|
+
return geometry;
|
|
64
|
+
}
|
|
65
|
+
function hemisphere(radius, radialSegments, heightSegments) {
|
|
66
|
+
const geometry = new SphereGeometry(radius, radialSegments, heightSegments, 0, 2 * Math.PI, 0, Math.PI / 2);
|
|
67
|
+
scaleUVs(geometry, 2 * Math.PI * radius, (Math.PI * radius) / 2);
|
|
68
|
+
return geometry;
|
|
69
|
+
}
|
|
70
|
+
/** Origin is the spindle on the mounting face; the grip points along +X, out along +Z. */
|
|
71
|
+
function buildLever(curved, plate = false) {
|
|
72
|
+
return cached(plate ? 'lever-plate' : curved ? 'lever-curved' : 'lever-straight', [], () => {
|
|
73
|
+
const faceFillet = 0.0005;
|
|
74
|
+
const faceRadius = 0.0245 - faceFillet * (Math.SQRT2 - 1);
|
|
75
|
+
const profile = [
|
|
76
|
+
[0, 0],
|
|
77
|
+
[0.026, 0],
|
|
78
|
+
[0.026, 0.0075],
|
|
79
|
+
];
|
|
80
|
+
for (const angle of [Math.PI / 4, Math.PI / 2]) {
|
|
81
|
+
profile.push([
|
|
82
|
+
faceRadius + faceFillet * Math.cos(angle),
|
|
83
|
+
0.0085 + faceFillet * Math.sin(angle),
|
|
84
|
+
]);
|
|
85
|
+
}
|
|
86
|
+
profile.push([0, 0.009]);
|
|
87
|
+
const rose = plate
|
|
88
|
+
? roundedBox(0.04, 0.22, 0.006, 0.003).clone().translate(0, 0, 0.003)
|
|
89
|
+
: lathe(profile, 32).rotateX(Math.PI / 2);
|
|
90
|
+
const neck = cylinder(0.01, 0.037, 16)
|
|
91
|
+
.rotateX(Math.PI / 2)
|
|
92
|
+
.translate(0, 0, 0.009 + 0.037 / 2);
|
|
93
|
+
const neckProfile = [];
|
|
94
|
+
for (let i = 0; i <= 4; i++) {
|
|
95
|
+
const angle = (i * Math.PI) / 8;
|
|
96
|
+
neckProfile.push([0.009 + 0.001 * Math.cos(angle), 0.046 + 0.001 * Math.sin(angle)]);
|
|
97
|
+
}
|
|
98
|
+
neckProfile.push([0, 0.047]);
|
|
99
|
+
const fillet = lathe(neckProfile, 16);
|
|
100
|
+
const filletNormals = fillet.getAttribute('normal');
|
|
101
|
+
// Match the cylinder tangent exactly rather than averaging the first fillet chord.
|
|
102
|
+
for (let i = 0; i <= 16; i++) {
|
|
103
|
+
const phi = (i * 2 * Math.PI) / 16;
|
|
104
|
+
for (let j = 0; j < neckProfile.length; j++) {
|
|
105
|
+
const angle = (Math.min(j, 4) * Math.PI) / 8;
|
|
106
|
+
filletNormals.setXYZ(i * neckProfile.length + j, Math.cos(angle) * Math.sin(phi), Math.sin(angle), Math.cos(angle) * Math.cos(phi));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
fillet.rotateX(Math.PI / 2);
|
|
110
|
+
const curve = new CatmullRomCurve3((curved
|
|
111
|
+
? [
|
|
112
|
+
[0, 0],
|
|
113
|
+
[0.01, 0],
|
|
114
|
+
[0.02, 0],
|
|
115
|
+
[0.035, 0],
|
|
116
|
+
[0.065, -0.004],
|
|
117
|
+
[0.095, -0.011],
|
|
118
|
+
[0.105, -0.012],
|
|
119
|
+
[0.1155, -0.012],
|
|
120
|
+
]
|
|
121
|
+
: [
|
|
122
|
+
[0, 0],
|
|
123
|
+
[0.1155, 0],
|
|
124
|
+
]).map(([x, y]) => new Vector3(x, y, 0.047)));
|
|
125
|
+
const grip = new TubeGeometry(curve, curved ? 24 : 2, 0.0095, 16, false);
|
|
126
|
+
scaleUVs(grip, curve.getLength(), 2 * Math.PI * 0.0095);
|
|
127
|
+
const cap = hemisphere(0.0095, 12, 6)
|
|
128
|
+
.rotateZ(-Math.PI / 2)
|
|
129
|
+
.translate(0.1155, curved ? -0.012 : 0, 0.047);
|
|
130
|
+
// Close the heel where the horizontal grip meets the vertical neck.
|
|
131
|
+
const heel = hemisphere(0.0095, 12, 6)
|
|
132
|
+
.rotateZ(Math.PI / 2)
|
|
133
|
+
.translate(0, 0, 0.047);
|
|
134
|
+
if (plate)
|
|
135
|
+
for (const part of [neck, fillet, grip, cap, heel])
|
|
136
|
+
part.translate(0, 0, -0.003);
|
|
137
|
+
return merge([rose, neck, fillet, grip, cap, heel]);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
export function leverOnRose() {
|
|
141
|
+
return buildLever(false);
|
|
142
|
+
}
|
|
143
|
+
export function leverOnRoseCurved() {
|
|
144
|
+
return buildLever(true);
|
|
145
|
+
}
|
|
146
|
+
export function leverOnPlate() {
|
|
147
|
+
return buildLever(false, true);
|
|
148
|
+
}
|
|
149
|
+
/** Origin at the mounting face; a 56 mm ball on a 20 mm neck and 56 mm rose. */
|
|
150
|
+
export function knobOnRose() {
|
|
151
|
+
return cached('knob', [], () => {
|
|
152
|
+
const rose = cylinder(0.028, 0.006, 32)
|
|
153
|
+
.rotateX(Math.PI / 2)
|
|
154
|
+
.translate(0, 0, 0.003);
|
|
155
|
+
const neck = cylinder(0.01, 0.025, 16)
|
|
156
|
+
.rotateX(Math.PI / 2)
|
|
157
|
+
.translate(0, 0, 0.0185);
|
|
158
|
+
const ball = new SphereGeometry(0.028, 32, 16);
|
|
159
|
+
scaleUVs(ball, 2 * Math.PI * 0.028, Math.PI * 0.028);
|
|
160
|
+
ball.translate(0, 0, 0.052);
|
|
161
|
+
return merge([rose, neck, ball]);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
/** Origin at the face; a 40 × 120 mm open cup with a 3 mm rim, recessed 12 mm. */
|
|
165
|
+
export function flushPull() {
|
|
166
|
+
return cached('flush-pull', [], () => {
|
|
167
|
+
const parts = [];
|
|
168
|
+
const box = (w, h, d, x, y, z) => {
|
|
169
|
+
const geometry = new BoxGeometry(w, h, d);
|
|
170
|
+
boxUVs(geometry, w, h, d);
|
|
171
|
+
parts.push(geometry.translate(x, y, z));
|
|
172
|
+
};
|
|
173
|
+
box(0.04, 0.12, 0.002, 0, 0, -0.011);
|
|
174
|
+
for (const side of [-1, 1]) {
|
|
175
|
+
box(0.002, 0.12, 0.01, side * 0.019, 0, -0.005);
|
|
176
|
+
box(0.036, 0.002, 0.01, 0, side * 0.059, -0.005);
|
|
177
|
+
box(0.003, 0.126, 0.001, side * 0.0215, 0, 0.0005);
|
|
178
|
+
box(0.04, 0.003, 0.001, 0, side * 0.0615, 0.0005);
|
|
179
|
+
}
|
|
180
|
+
return merge(parts);
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
function hingePart(leaf, diameter, height) {
|
|
184
|
+
return cached(leaf ? 'hinge-leaf' : 'hinge-jamb', [diameter, height], (d, h) => {
|
|
185
|
+
const radialScale = d / 0.014;
|
|
186
|
+
const heightScale = h / 0.103;
|
|
187
|
+
const radius = d / 2;
|
|
188
|
+
const plateW = 0.0025 * radialScale;
|
|
189
|
+
const plateH = 0.089 * heightScale;
|
|
190
|
+
const plateD = 0.028 * radialScale;
|
|
191
|
+
const plate = new BoxGeometry(plateW, plateH, plateD);
|
|
192
|
+
boxUVs(plate, plateW, plateH, plateD);
|
|
193
|
+
plate.translate(leaf ? plateW / 2 : -plateW / 2, 0, -plateD / 2);
|
|
194
|
+
const parts = [plate];
|
|
195
|
+
for (let i = leaf ? 0 : 1; i < 5; i += 2) {
|
|
196
|
+
parts.push(cylinder(radius, 0.017 * heightScale, 16).translate(0, (-0.036 + i * 0.018) * heightScale, 0));
|
|
197
|
+
}
|
|
198
|
+
if (leaf) {
|
|
199
|
+
for (const side of [-1, 1]) {
|
|
200
|
+
const cap = hemisphere(radius, 12, d <= 0.01 ? 2 : 3);
|
|
201
|
+
const capScale = heightScale / radialScale;
|
|
202
|
+
cap.scale(1, capScale, 1);
|
|
203
|
+
const uv = cap.getAttribute('uv');
|
|
204
|
+
for (let i = 0; i < uv.count; i++)
|
|
205
|
+
uv.setY(i, uv.getY(i) * capScale);
|
|
206
|
+
if (side < 0)
|
|
207
|
+
cap.rotateX(Math.PI);
|
|
208
|
+
parts.push(cap.translate(0, side * 0.0445 * heightScale, 0));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return merge(parts);
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
/** Odd knuckles and plate on the leaf edge (+X), extending into the rebate (-Z). */
|
|
215
|
+
export function hingeLeafPart(diameter = 0.014, height = 0.103) {
|
|
216
|
+
return hingePart(true, diameter, height);
|
|
217
|
+
}
|
|
218
|
+
/** Even knuckles and plate on the jamb face (-X), extending into the rebate (-Z). */
|
|
219
|
+
export function hingeJambPart(diameter = 0.014, height = 0.103) {
|
|
220
|
+
return hingePart(false, diameter, height);
|
|
221
|
+
}
|
|
222
|
+
/** Origin on the mounting face; bar along Y, projecting along +Z. */
|
|
223
|
+
export function pullBar(doorScale = false) {
|
|
224
|
+
return cached(doorScale ? 'door-pull' : 'pull', [], () => {
|
|
225
|
+
if (doorScale) {
|
|
226
|
+
const bar = cylinder(0.0125, 0.4, 32).translate(0, 0, 0.05);
|
|
227
|
+
return merge([
|
|
228
|
+
bar,
|
|
229
|
+
...[-0.15, 0.15].map((y) => cylinder(0.008, 0.05, 16)
|
|
230
|
+
.rotateX(Math.PI / 2)
|
|
231
|
+
.translate(0, y, 0.025)),
|
|
232
|
+
]);
|
|
233
|
+
}
|
|
234
|
+
const bar = new CapsuleGeometry(0.009, 0.102, 6, 12);
|
|
235
|
+
scaleUVs(bar, 2 * Math.PI * 0.009, 0.102 + Math.PI * 0.009);
|
|
236
|
+
bar.translate(0, 0, 0.022);
|
|
237
|
+
const parts = [bar];
|
|
238
|
+
for (const y of [-0.02, 0.02]) {
|
|
239
|
+
parts.push(cylinder(0.005, 0.02, 16)
|
|
240
|
+
.rotateX(Math.PI / 2)
|
|
241
|
+
.translate(0, y, 0.01));
|
|
242
|
+
}
|
|
243
|
+
return merge(parts);
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
function boxUVs(geometry, width, height, depth) {
|
|
247
|
+
const uv = geometry.getAttribute('uv');
|
|
248
|
+
const faceSizes = [
|
|
249
|
+
[depth, height],
|
|
250
|
+
[depth, height],
|
|
251
|
+
[width, depth],
|
|
252
|
+
[width, depth],
|
|
253
|
+
[width, height],
|
|
254
|
+
[width, height],
|
|
255
|
+
];
|
|
256
|
+
const verticesPerFace = uv.count / 6;
|
|
257
|
+
for (let i = 0; i < uv.count; i++) {
|
|
258
|
+
const [u, v] = faceSizes[Math.floor(i / verticesPerFace)];
|
|
259
|
+
uv.setXY(i, uv.getX(i) * u, uv.getY(i) * v);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
export function roundedBox(w, h, d, r = Math.min(0.004, w / 4, h / 4, d / 4)) {
|
|
263
|
+
return cached('box', [w, h, d, r], (width, height, depth, radius) => {
|
|
264
|
+
const geometry = new RoundedBoxGeometry(width, height, depth, 1, radius);
|
|
265
|
+
boxUVs(geometry, width, height, depth);
|
|
266
|
+
return geometry;
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
/** A capped round bar along Y. */
|
|
270
|
+
export function tube(radius, length) {
|
|
271
|
+
return cached('tube', [2 * radius, length], (diameter, l) => cylinder(diameter / 2, l, 12));
|
|
272
|
+
}
|
|
273
|
+
export function sphere(radius) {
|
|
274
|
+
return cached('sphere', [2 * radius], (diameter) => {
|
|
275
|
+
const geometry = new SphereGeometry(diameter / 2, 12, 8);
|
|
276
|
+
scaleUVs(geometry, Math.PI * diameter, (Math.PI * diameter) / 2);
|
|
277
|
+
return geometry;
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
/** A wheel with its axle along Z. */
|
|
281
|
+
export function roller(radius, width) {
|
|
282
|
+
return cached('roller', [2 * radius, width], (diameter, w) => cylinder(diameter / 2, w, 20).rotateX(Math.PI / 2));
|
|
283
|
+
}
|
package/dist/lib/layer-pass.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type Camera, Object3D, type Scene } from 'three';
|
|
2
2
|
import { type NodeFrame, PassNode } from 'three/webgpu';
|
|
3
|
+
export declare function getLayoutRevision(): number;
|
|
4
|
+
export declare function isLayoutObserved(scene: Object3D): boolean;
|
|
3
5
|
export declare class LayerPassIndex {
|
|
4
6
|
readonly source: Scene;
|
|
5
7
|
private readonly members;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layer-pass.d.ts","sourceRoot":"","sources":["../../src/lib/layer-pass.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,MAAM,EAAiB,QAAQ,EAAE,KAAK,KAAK,EAAW,MAAM,OAAO,CAAA;AACjF,OAAO,EAAE,KAAK,SAAS,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"layer-pass.d.ts","sourceRoot":"","sources":["../../src/lib/layer-pass.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,MAAM,EAAiB,QAAQ,EAAE,KAAK,KAAK,EAAW,MAAM,OAAO,CAAA;AACjF,OAAO,EAAE,KAAK,SAAS,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAYvD,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAEzD;AAkBD,qBAAa,cAAc;IAKvB,QAAQ,CAAC,MAAM,EAAE,KAAK;IAJxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmC;IAC3D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkC;IAE3D,YACW,MAAM,EAAE,KAAK,EACtB,MAAM,EAAE,MAAM,EAAE,EAUjB;IAED,QAAQ,CAAC,OAAO,EAAE,QAAQ,QAQzB;IAED,OAAO,CAAC,MAAM,CA8Cb;IAED,OAAO,CAAC,MAAM;IAOd,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE;;;MAkCvC;IAED,OAAO,SAIN;CACF;AAiBD,qBAAa,aAAc,SAAQ,QAAQ;IAMvC,OAAO,CAAC,QAAQ,CAAC,KAAK;IAEtB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAR3B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgB;IACrC,OAAO,CAAC,UAAU,CAAO;IAEzB,YACmB,KAAK,EAAE,cAAc,EACtC,MAAM,EAAE,MAAM,EACG,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,QAAQ,EAYpC;IAEQ,OAAO,SAGf;IAEQ,YAAY,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CA+DjD;CACF"}
|
package/dist/lib/layer-pass.js
CHANGED
|
@@ -5,6 +5,19 @@
|
|
|
5
5
|
import { Object3D, Vector2 } from 'three';
|
|
6
6
|
import { PassNode } from 'three/webgpu';
|
|
7
7
|
const maskObserver = Symbol('LayerPassIndex.maskObserver');
|
|
8
|
+
/**
|
|
9
|
+
* Bumped on every observed mask change and child insertion or removal, so a per-frame walk can
|
|
10
|
+
* tell when what is drawable may have changed (scene-matrices.ts). Only meaningful for scenes
|
|
11
|
+
* with a live index ({@link isLayoutObserved}).
|
|
12
|
+
*/
|
|
13
|
+
let layoutRevision = 0;
|
|
14
|
+
const observedScenes = new WeakSet();
|
|
15
|
+
export function getLayoutRevision() {
|
|
16
|
+
return layoutRevision;
|
|
17
|
+
}
|
|
18
|
+
export function isLayoutObserved(scene) {
|
|
19
|
+
return observedScenes.has(scene);
|
|
20
|
+
}
|
|
8
21
|
function maskOwner(object) {
|
|
9
22
|
const get = Object.getOwnPropertyDescriptor(object.layers, 'mask')?.get;
|
|
10
23
|
return get?.[maskObserver];
|
|
@@ -22,6 +35,7 @@ export class LayerPassIndex {
|
|
|
22
35
|
this.members.set(1 << layer, new Set());
|
|
23
36
|
try {
|
|
24
37
|
this.attach(source);
|
|
38
|
+
observedScenes.add(source);
|
|
25
39
|
}
|
|
26
40
|
catch (error) {
|
|
27
41
|
this.dispose();
|
|
@@ -45,7 +59,11 @@ export class LayerPassIndex {
|
|
|
45
59
|
throw new Error('LayerPassIndex requires one index per scene and unshared Layers');
|
|
46
60
|
}
|
|
47
61
|
let mask = layers.mask;
|
|
62
|
+
// Passes switch the camera's layers every frame; that changes nothing in the scene.
|
|
63
|
+
const isCamera = object.isCamera === true;
|
|
48
64
|
const sync = () => {
|
|
65
|
+
if (!isCamera)
|
|
66
|
+
layoutRevision++;
|
|
49
67
|
for (const [bit, members] of this.members) {
|
|
50
68
|
if ((mask & bit) !== 0 && object !== this.source)
|
|
51
69
|
members.add(object);
|
|
@@ -88,6 +106,7 @@ export class LayerPassIndex {
|
|
|
88
106
|
this.attach(child);
|
|
89
107
|
};
|
|
90
108
|
detach(object) {
|
|
109
|
+
layoutRevision++;
|
|
91
110
|
for (const child of object.children)
|
|
92
111
|
this.detach(child);
|
|
93
112
|
this.cleanups.get(object)?.();
|
|
@@ -136,6 +155,7 @@ export class LayerPassIndex {
|
|
|
136
155
|
return { drawable, shadowLight };
|
|
137
156
|
}
|
|
138
157
|
dispose() {
|
|
158
|
+
observedScenes.delete(this.source);
|
|
139
159
|
for (const cleanup of this.cleanups.values())
|
|
140
160
|
cleanup();
|
|
141
161
|
this.cleanups.clear();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"materials.d.ts","sourceRoot":"","sources":["../../src/lib/materials.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,qBAAqB,EAE1B,KAAK,cAAc,EAKnB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,WAAW,EACjB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAE,uBAAuB,EAA4B,MAAM,cAAc,CAAA;AAOhF,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,UAAU,CAAA;AAChD,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,WAAW,CAAA;AAEjE,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAQpD,CAAA;AAID,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAQrD,CAAA;AAED,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAQpD,CAAA;AAED,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAQzD,CAAA;AAED,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAK5E,CAAA;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,WAAW,EACnB,YAAY,CAAC,EAAE,MAAM,GACpB,MAAM,CAKR;AAOD,eAAO,MAAM,aAAa,yBAKxB,CAAA;AAwBF,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AAED,wEAAwE;AACxE,wBAAgB,wBAAwB,IAAI;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAE9E;AAiGD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAWtF;
|
|
1
|
+
{"version":3,"file":"materials.d.ts","sourceRoot":"","sources":["../../src/lib/materials.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/materials.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getMaterialPresetByRef, materialColorPaint, parseMaterialColor, parseMaterialRef, resolveMaterial, } from '@pascal-app/core';
|
|
2
2
|
import { addAfterEffect, invalidate } from '@react-three/fiber';
|
|
3
3
|
import * as THREE from 'three';
|
|
4
|
-
import { float, mix,
|
|
4
|
+
import { float, mix, normalView, positionViewDirection } from 'three/tsl';
|
|
5
5
|
import { MeshLambertNodeMaterial, MeshStandardNodeMaterial } from 'three/webgpu';
|
|
6
6
|
import { resolveCdnUrl } from './asset-url';
|
|
7
7
|
import { isKtx2Url, ktx2Loader, whenKtx2Ready } from './ktx2-loader';
|
|
@@ -162,35 +162,41 @@ export function resolveTextureRepeat(repeat, scale) {
|
|
|
162
162
|
}
|
|
163
163
|
return [fallback, fallback];
|
|
164
164
|
}
|
|
165
|
+
function textureOffset(texture) {
|
|
166
|
+
return texture.offset ?? [0, 0];
|
|
167
|
+
}
|
|
168
|
+
function getPlacementKey(texture) {
|
|
169
|
+
const [repeatX, repeatY] = resolveTextureRepeat(texture.repeat, texture.scale);
|
|
170
|
+
const [offsetX, offsetY] = textureOffset(texture);
|
|
171
|
+
return `${repeatX}x${repeatY}+${offsetX},${offsetY}`;
|
|
172
|
+
}
|
|
165
173
|
export function getTextureKey(material) {
|
|
166
174
|
const texture = material?.texture;
|
|
167
175
|
if (!texture)
|
|
168
176
|
return 'none';
|
|
169
|
-
const
|
|
170
|
-
|
|
177
|
+
const normal = texture.normalMap ? `-n:${texture.normalMap}*${texture.normalScale ?? 1}` : '';
|
|
178
|
+
const bump = texture.bumpMap ? `-b:${texture.bumpMap}*${texture.bumpScale ?? 1}` : '';
|
|
179
|
+
return `${texture.url}-${getPlacementKey(texture)}${normal}${bump}`;
|
|
171
180
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
return undefined;
|
|
176
|
-
const cacheKey = getTextureKey(material);
|
|
181
|
+
/** One map of a native material's texture; every map shares the albedo's repeat and offset. */
|
|
182
|
+
function getTexture(textureConfig, url, slot) {
|
|
183
|
+
const cacheKey = `${url}-${getPlacementKey(textureConfig)}-${slot}`;
|
|
177
184
|
const cached = textureCache.get(cacheKey);
|
|
178
185
|
if (cached)
|
|
179
186
|
return cached;
|
|
180
|
-
const resolvedUrl = /^(?:asset|blob|data):/.test(
|
|
181
|
-
? textureConfig.url
|
|
182
|
-
: (resolveCdnUrl(textureConfig.url) ?? textureConfig.url);
|
|
187
|
+
const resolvedUrl = /^(?:asset|blob|data):/.test(url) ? url : (resolveCdnUrl(url) ?? url);
|
|
183
188
|
const texture = loadTexture(resolvedUrl, cacheKey);
|
|
184
189
|
texture.wrapS = THREE.RepeatWrapping;
|
|
185
190
|
texture.wrapT = THREE.RepeatWrapping;
|
|
186
191
|
const [repeatX, repeatY] = resolveTextureRepeat(textureConfig.repeat, textureConfig.scale);
|
|
187
192
|
texture.repeat.set(repeatX, repeatY);
|
|
193
|
+
texture.offset.set(...textureOffset(textureConfig));
|
|
188
194
|
texture.updateMatrix();
|
|
189
|
-
texture.colorSpace = THREE.SRGBColorSpace;
|
|
195
|
+
texture.colorSpace = slot === 'map' ? THREE.SRGBColorSpace : THREE.NoColorSpace;
|
|
190
196
|
stampPascalTextureRef(texture, {
|
|
191
197
|
kind: 'project-asset',
|
|
192
198
|
src: resolvedUrl,
|
|
193
|
-
slot
|
|
199
|
+
slot,
|
|
194
200
|
});
|
|
195
201
|
textureCache.set(cacheKey, texture);
|
|
196
202
|
return texture;
|
|
@@ -272,9 +278,14 @@ async function loadPresetTexture(path, props, slot) {
|
|
|
272
278
|
// materials can be created while a capture canvas's renderer is still
|
|
273
279
|
// initializing, and failing here would cache the material permanently
|
|
274
280
|
// texture-less (white).
|
|
275
|
-
const
|
|
276
|
-
? whenKtx2Ready().then(() => ktx2Loader.loadAsync(
|
|
277
|
-
: textureLoader.loadAsync(
|
|
281
|
+
const loadFrom = (url) => isKtx2Url(url)
|
|
282
|
+
? whenKtx2Ready().then(() => ktx2Loader.loadAsync(url))
|
|
283
|
+
: textureLoader.loadAsync(url);
|
|
284
|
+
// A catalog map ships in the app's public folder before the asset CDN has it
|
|
285
|
+
// (a branch, a deploy ahead of the CDN): a CDN miss retries the app's own copy.
|
|
286
|
+
const load = resolvedPath !== path && path.startsWith('/')
|
|
287
|
+
? loadFrom(resolvedPath).catch(() => loadFrom(path))
|
|
288
|
+
: loadFrom(resolvedPath);
|
|
278
289
|
const promise = load
|
|
279
290
|
.then((texture) => {
|
|
280
291
|
applyTextureProperties(texture, props, slot);
|
|
@@ -383,7 +394,9 @@ export function materialCastsShadow(material) {
|
|
|
383
394
|
* This is what makes glass read as a surface instead of a flat blue tint.
|
|
384
395
|
*/
|
|
385
396
|
function applyGlassFresnel(material) {
|
|
386
|
-
|
|
397
|
+
// The daylight look swaps in its tuned glass for flagged materials (lib/daylight/material-hooks).
|
|
398
|
+
material.userData.isGlass = true;
|
|
399
|
+
const facing = normalView.dot(positionViewDirection).clamp(0, 1);
|
|
387
400
|
const fresnel = facing.oneMinus().pow(3);
|
|
388
401
|
material.opacityNode = mix(float(material.opacity), float(0.92), fresnel);
|
|
389
402
|
material.envMapIntensity = 1.4;
|
|
@@ -449,22 +462,36 @@ export function createMaterial(material, shading = 'rendered') {
|
|
|
449
462
|
if (materialCache.has(cacheKey)) {
|
|
450
463
|
return materialCache.get(cacheKey);
|
|
451
464
|
}
|
|
452
|
-
const
|
|
465
|
+
const texture = material?.texture?.url ? material.texture : undefined;
|
|
453
466
|
const materialParams = {
|
|
454
467
|
color: props.color,
|
|
455
468
|
opacity: props.opacity,
|
|
456
469
|
transparent: props.transparent,
|
|
457
470
|
side: sideMap[props.side],
|
|
458
471
|
};
|
|
459
|
-
if (
|
|
460
|
-
materialParams.map = map;
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
472
|
+
if (texture)
|
|
473
|
+
materialParams.map = getTexture(texture, texture.url, 'map');
|
|
474
|
+
let threeMaterial;
|
|
475
|
+
if (shading === 'solid') {
|
|
476
|
+
threeMaterial = new MeshLambertNodeMaterial(materialParams);
|
|
477
|
+
}
|
|
478
|
+
else {
|
|
479
|
+
const standard = new MeshStandardNodeMaterial({
|
|
464
480
|
...materialParams,
|
|
465
481
|
roughness: props.roughness,
|
|
466
482
|
metalness: props.metalness,
|
|
467
483
|
});
|
|
484
|
+
if (texture?.normalMap) {
|
|
485
|
+
standard.normalMap = getTexture(texture, texture.normalMap, 'normalMap');
|
|
486
|
+
const normalScale = texture.normalScale ?? 1;
|
|
487
|
+
standard.normalScale.set(normalScale, normalScale);
|
|
488
|
+
}
|
|
489
|
+
if (texture?.bumpMap) {
|
|
490
|
+
standard.bumpMap = getTexture(texture, texture.bumpMap, 'bumpMap');
|
|
491
|
+
standard.bumpScale = texture.bumpScale ?? 1;
|
|
492
|
+
}
|
|
493
|
+
threeMaterial = standard;
|
|
494
|
+
}
|
|
468
495
|
// Explicit source transparency keeps its opacity; the glass preset owns this styling.
|
|
469
496
|
if (glassPreset)
|
|
470
497
|
maybeApplyGlassFresnel(threeMaterial);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merged-outline-node.d.ts","sourceRoot":"","sources":["../../src/lib/merged-outline-node.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AAEH,OAAO,EAIL,QAAQ,EAKT,MAAM,OAAO,CAAA;AA2Bd,OAAO,EAML,QAAQ,EACT,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"merged-outline-node.d.ts","sourceRoot":"","sources":["../../src/lib/merged-outline-node.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,6 +15,8 @@ import { DepthTexture, FloatType, Mesh, Object3D, RenderTarget, Scene, Sprite, V
|
|
|
15
15
|
import { builtin, color, exp, Fn, float, floatBitsToUint, int, Loop, min, mul, nodeObject, orthographicDepthToViewZ, passTexture, perspectiveDepthToViewZ, positionView, reference, screenUV, texture, textureSize, uniform, uv, vec2, vec3, vec4, } from 'three/tsl';
|
|
16
16
|
import { NodeMaterial, NodeUpdateType, QuadMesh, RendererUtils, SpriteNodeMaterial, TempNode, } from 'three/webgpu';
|
|
17
17
|
import { hasDrawableGeometry } from './drawable-geometry';
|
|
18
|
+
import { BATCHED_LAYER, SCENE_LAYER } from './layers';
|
|
19
|
+
import { isSetAsideForRender } from './scene-matrices';
|
|
18
20
|
const _quadMesh = new QuadMesh();
|
|
19
21
|
const _size = new Vector2();
|
|
20
22
|
const _BLUR_X = new Vector2(1.0, 0.0);
|
|
@@ -431,7 +433,8 @@ export class MergedOutlineNode extends TempNode {
|
|
|
431
433
|
let visible = true;
|
|
432
434
|
let attached = false;
|
|
433
435
|
for (let ancestor = source; ancestor; ancestor = ancestor.parent) {
|
|
434
|
-
|
|
436
|
+
// A subtree set aside for this render (nothing in it draws itself) is still shown.
|
|
437
|
+
if (!ancestor.visible && !isSetAsideForRender(ancestor))
|
|
435
438
|
visible = false;
|
|
436
439
|
if (ancestor === this.scene)
|
|
437
440
|
attached = true;
|
|
@@ -440,6 +443,9 @@ export class MergedOutlineNode extends TempNode {
|
|
|
440
443
|
proxy.material = source.material;
|
|
441
444
|
proxy.matrixWorld.copy(source.matrixWorld);
|
|
442
445
|
proxy.layers.mask = source.layers.mask;
|
|
446
|
+
// A batched source is drawn by its batch: outline it as if it drew itself.
|
|
447
|
+
if (proxy.layers.isEnabled(BATCHED_LAYER))
|
|
448
|
+
proxy.layers.enable(SCENE_LAYER);
|
|
443
449
|
proxy.visible = visible && attached;
|
|
444
450
|
proxy.renderOrder = source.renderOrder;
|
|
445
451
|
proxy.morphTargetInfluences = source.morphTargetInfluences;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
export type OcclusionStats = {
|
|
3
|
+
occluders: number;
|
|
4
|
+
triangles: number;
|
|
5
|
+
tested: number;
|
|
6
|
+
culled: number;
|
|
7
|
+
ms: number;
|
|
8
|
+
};
|
|
9
|
+
export type OcclusionCuller = {
|
|
10
|
+
/** Computes which drawn meshes the camera can't see. Returns the culled meshes. */
|
|
11
|
+
cull(camera: THREE.Camera): ReadonlySet<THREE.Mesh>;
|
|
12
|
+
/** Whether a mesh moved, appeared or vanished since the last call. */
|
|
13
|
+
changed(): boolean;
|
|
14
|
+
readonly stats: OcclusionStats;
|
|
15
|
+
dispose(): void;
|
|
16
|
+
};
|
|
17
|
+
export declare function createOcclusionCuller(root: THREE.Object3D): OcclusionCuller;
|
|
18
|
+
/** The part of three's drawing pipeline (`renderer.drawingPipeline`) the culler hooks into. */
|
|
19
|
+
export type CullingPipeline = {
|
|
20
|
+
isCulled(object: THREE.Object3D, camera: THREE.Camera): boolean;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Culls `culler`'s meshes from the renders of `scene` through the camera `getCamera` returns:
|
|
24
|
+
* recomputed when that camera moves or the scene changes, and answered through the drawing
|
|
25
|
+
* pipeline's own culling test while such a render collects its draws. Nothing about the meshes
|
|
26
|
+
* changes, so other cameras (the sun's shadow, captures) see them all.
|
|
27
|
+
*/
|
|
28
|
+
export declare function attachOcclusionCuller({ scene, pipeline, culler, getCamera, }: {
|
|
29
|
+
scene: THREE.Scene;
|
|
30
|
+
pipeline: CullingPipeline;
|
|
31
|
+
culler: OcclusionCuller;
|
|
32
|
+
getCamera: () => THREE.Camera;
|
|
33
|
+
}): () => void;
|
|
34
|
+
//# sourceMappingURL=occlusion-culler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"occlusion-culler.d.ts","sourceRoot":"","sources":["../../src/lib/occlusion-culler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAiD9B,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,EAAE,EAAE,MAAM,CAAA;CACX,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,mFAAmF;IACnF,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACnD,sEAAsE;IACtE,OAAO,IAAI,OAAO,CAAA;IAClB,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAA;IAC9B,OAAO,IAAI,IAAI,CAAA;CAChB,CAAA;AAUD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,GAAG,eAAe,CA6gB3E;AAED,+FAA+F;AAC/F,MAAM,MAAM,eAAe,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,OAAO,CAAA;CAAE,CAAA;AAEjG;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,KAAK,EACL,QAAQ,EACR,MAAM,EACN,SAAS,GACV,EAAE;IACD,KAAK,EAAE,KAAK,CAAC,KAAK,CAAA;IAClB,QAAQ,EAAE,eAAe,CAAA;IACzB,MAAM,EAAE,eAAe,CAAA;IACvB,SAAS,EAAE,MAAM,KAAK,CAAC,MAAM,CAAA;CAC9B,GAAG,MAAM,IAAI,CAoDb"}
|