@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,356 @@
|
|
|
1
|
+
import { getLevelElevations, sceneRegistry, useInteractive, useScene, } from '@pascal-app/core';
|
|
2
|
+
import { Box3, Color, MathUtils, Vector3 } from 'three';
|
|
3
|
+
import { materialEmissive, smoothstep, uniform, vec3 } from 'three/tsl';
|
|
4
|
+
import { MeshStandardNodeMaterial } from 'three/webgpu';
|
|
5
|
+
import { isHighlight } from '../editor-highlight';
|
|
6
|
+
import { setSurfaceRaycastLayers } from '../layers';
|
|
7
|
+
import { daylightUniforms } from './runtime';
|
|
8
|
+
/** Item-light pools hand their lamps to the room system while the daylight look is active. */
|
|
9
|
+
export const lampFeeds = new Set();
|
|
10
|
+
const COVER_TYPES = {
|
|
11
|
+
ceiling: 'ceiling',
|
|
12
|
+
roof: 'roof',
|
|
13
|
+
'roof-segment': 'roof',
|
|
14
|
+
slab: 'slab',
|
|
15
|
+
};
|
|
16
|
+
const OPENING_TYPES = ['window', 'door'];
|
|
17
|
+
/** Lamps from every registered feed, in the room system's units. */
|
|
18
|
+
export function feedLamps() {
|
|
19
|
+
const lamps = [];
|
|
20
|
+
const position = new Vector3();
|
|
21
|
+
for (const feed of lampFeeds) {
|
|
22
|
+
for (const light of feed.lights()) {
|
|
23
|
+
if (!light.getWorldPosition(position))
|
|
24
|
+
continue;
|
|
25
|
+
// Light effects mix conventions: artist numbers (0.6–2) and physical-looking ones
|
|
26
|
+
// (110, 1800 cd with 55 m reach). Log-compress so both land in a sane band; rooms are
|
|
27
|
+
// soft-capped later.
|
|
28
|
+
const peak = light.peak ?? 1;
|
|
29
|
+
lamps.push({
|
|
30
|
+
itemId: light.key,
|
|
31
|
+
position: position.clone(),
|
|
32
|
+
color: new Color(light.color || '#ffe6c4'),
|
|
33
|
+
intensity: 4 + 3 * Math.log2(1 + Math.max(0, peak)),
|
|
34
|
+
candela: Math.max(0, peak),
|
|
35
|
+
range: MathUtils.clamp(light.distance || 5.5, 2.5, 9),
|
|
36
|
+
on: feed.intensity(light) > 0,
|
|
37
|
+
spot: light.spot ? lampSpot(light) : undefined,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return lamps;
|
|
42
|
+
}
|
|
43
|
+
/** A spot's cone, as the light pools read it (half-angle = angle / 2, penumbra = softness). */
|
|
44
|
+
function lampSpot(light) {
|
|
45
|
+
const direction = new Vector3(...(light.spot?.direction ?? [0, -1, 0]));
|
|
46
|
+
if (light.getWorldDirection)
|
|
47
|
+
light.getWorldDirection(direction);
|
|
48
|
+
const half = Math.min(Math.PI / 2, MathUtils.degToRad((light.spot?.angle ?? 60) / 2));
|
|
49
|
+
const penumbra = MathUtils.clamp(light.spot?.softness ?? 0, 0, 1);
|
|
50
|
+
return {
|
|
51
|
+
direction: direction.normalize(),
|
|
52
|
+
cosOuter: Math.cos(half),
|
|
53
|
+
cosInner: Math.cos(half * (1 - penumbra)),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/** Current on/off of a fed lamp (by key). */
|
|
57
|
+
export function feedLampOn(key) {
|
|
58
|
+
for (const feed of lampFeeds) {
|
|
59
|
+
for (const light of feed.lights())
|
|
60
|
+
if (light.key === key)
|
|
61
|
+
return feed.intensity(light) > 0;
|
|
62
|
+
}
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
/** Meshes under a root, minus skipped subtrees and invisible helpers (hole-less collision proxies). */
|
|
66
|
+
function meshesOf(root, skip) {
|
|
67
|
+
const out = [];
|
|
68
|
+
const visit = (object, isRoot) => {
|
|
69
|
+
if (!isRoot && (skip(object) || !object.visible))
|
|
70
|
+
return;
|
|
71
|
+
if (object.isMesh)
|
|
72
|
+
out.push(object);
|
|
73
|
+
for (const child of object.children)
|
|
74
|
+
visit(child, false);
|
|
75
|
+
};
|
|
76
|
+
visit(root, true);
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
/** The live parametric scene (registry + node data), or null while it has no zones. */
|
|
80
|
+
export function liveRoomScene(virtualLampsOn) {
|
|
81
|
+
const zoneIds = sceneRegistry.byType.zone;
|
|
82
|
+
if (!zoneIds || zoneIds.size === 0)
|
|
83
|
+
return null;
|
|
84
|
+
const { nodes } = useScene.getState();
|
|
85
|
+
const elevations = getLevelElevations(nodes);
|
|
86
|
+
const levels = Object.values(nodes)
|
|
87
|
+
.filter((node) => node?.type === 'level')
|
|
88
|
+
.map((node) => ({ id: node.id, y: elevations.get(node.id)?.baseY ?? 0 }))
|
|
89
|
+
.sort((a, b) => a.y - b.y);
|
|
90
|
+
const levelIndex = new Map(levels.map((level, index) => [level.id, index]));
|
|
91
|
+
const zones = [];
|
|
92
|
+
const point = new Vector3();
|
|
93
|
+
for (const id of zoneIds) {
|
|
94
|
+
const node = nodes[id];
|
|
95
|
+
const object = sceneRegistry.nodes.get(id);
|
|
96
|
+
if (!node?.polygon || node.polygon.length < 3 || !object || node.visible === false)
|
|
97
|
+
continue;
|
|
98
|
+
object.updateWorldMatrix(true, false);
|
|
99
|
+
const polygon = node.polygon.map(([x, z]) => {
|
|
100
|
+
object.localToWorld(point.set(x, 0, z));
|
|
101
|
+
return [point.x, point.z];
|
|
102
|
+
});
|
|
103
|
+
zones.push({ id, polygon, levelIndex: levelIndex.get(node.parentId ?? '') ?? 0 });
|
|
104
|
+
}
|
|
105
|
+
if (!zones.length)
|
|
106
|
+
return null;
|
|
107
|
+
const registered = new Set(sceneRegistry.nodes.values());
|
|
108
|
+
const covers = [];
|
|
109
|
+
for (const [type, kind] of Object.entries(COVER_TYPES)) {
|
|
110
|
+
for (const id of sceneRegistry.byType[type] ?? []) {
|
|
111
|
+
const object = sceneRegistry.nodes.get(id);
|
|
112
|
+
if (object)
|
|
113
|
+
covers.push({ object, kind });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const walls = [];
|
|
117
|
+
for (const id of sceneRegistry.byType.wall ?? []) {
|
|
118
|
+
const object = sceneRegistry.nodes.get(id);
|
|
119
|
+
// Doors, windows and items hang under their wall: a closed leaf mustn't close its doorway.
|
|
120
|
+
if (object)
|
|
121
|
+
walls.push(...meshesOf(object, (child) => registered.has(child)));
|
|
122
|
+
}
|
|
123
|
+
const openings = [];
|
|
124
|
+
for (const kind of OPENING_TYPES) {
|
|
125
|
+
for (const id of sceneRegistry.byType[kind] ?? []) {
|
|
126
|
+
const object = sceneRegistry.nodes.get(id);
|
|
127
|
+
if (object)
|
|
128
|
+
openings.push({ object, kind, id });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const bounds = new Box3();
|
|
132
|
+
for (const zone of zones)
|
|
133
|
+
for (const [x, z] of zone.polygon)
|
|
134
|
+
bounds.expandByPoint(point.set(x, 0, z));
|
|
135
|
+
return {
|
|
136
|
+
bounds,
|
|
137
|
+
levelY: levels.map((level) => level.y),
|
|
138
|
+
zones,
|
|
139
|
+
covers,
|
|
140
|
+
walls,
|
|
141
|
+
openings,
|
|
142
|
+
lamps: feedLamps(),
|
|
143
|
+
virtualLampsOn,
|
|
144
|
+
configureRaycaster: (raycaster) => setSurfaceRaycastLayers(raycaster.layers),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/** Live level objects in the room system's level order (for explode offsets). */
|
|
148
|
+
export function liveLevelObjects() {
|
|
149
|
+
const { nodes } = useScene.getState();
|
|
150
|
+
const elevations = getLevelElevations(nodes);
|
|
151
|
+
return Object.values(nodes)
|
|
152
|
+
.filter((node) => node?.type === 'level')
|
|
153
|
+
.sort((a, b) => (elevations.get(a.id)?.baseY ?? 0) - (elevations.get(b.id)?.baseY ?? 0))
|
|
154
|
+
.map((node) => sceneRegistry.nodes.get(node.id))
|
|
155
|
+
.filter((object) => !!object);
|
|
156
|
+
}
|
|
157
|
+
/** How open a live door is (0 closed … 1 fully open), the transient state first. */
|
|
158
|
+
export function liveDoorOpenness(id) {
|
|
159
|
+
const node = useScene.getState().nodes[id];
|
|
160
|
+
const transient = useInteractive.getState().doors[id];
|
|
161
|
+
const swing = transient?.swingAngle ?? node?.swingAngle ?? 0;
|
|
162
|
+
const operation = transient?.operationState ?? node?.operationState ?? 0;
|
|
163
|
+
return Math.max(operation, swing / (Math.PI / 2));
|
|
164
|
+
}
|
|
165
|
+
/** A baked artifact's identity tree (levels, zones, covers, walls, openings), or null. */
|
|
166
|
+
export function bakedRoomScene(root, virtualLampsOn) {
|
|
167
|
+
const levelObjects = [];
|
|
168
|
+
const zoneObjects = [];
|
|
169
|
+
const covers = [];
|
|
170
|
+
const wallRoots = [];
|
|
171
|
+
const openings = [];
|
|
172
|
+
root.traverse((object) => {
|
|
173
|
+
const extras = object.userData;
|
|
174
|
+
switch (extras.kind) {
|
|
175
|
+
case 'level':
|
|
176
|
+
levelObjects.push(object);
|
|
177
|
+
break;
|
|
178
|
+
case 'zone':
|
|
179
|
+
zoneObjects.push(object);
|
|
180
|
+
break;
|
|
181
|
+
case 'ceiling':
|
|
182
|
+
case 'roof':
|
|
183
|
+
case 'roof-segment':
|
|
184
|
+
case 'slab':
|
|
185
|
+
covers.push({ object, kind: COVER_TYPES[extras.kind] });
|
|
186
|
+
break;
|
|
187
|
+
case 'wall':
|
|
188
|
+
wallRoots.push(object);
|
|
189
|
+
break;
|
|
190
|
+
case 'window':
|
|
191
|
+
case 'door':
|
|
192
|
+
openings.push({ object, kind: extras.kind, id: extras.pascalId ?? object.uuid });
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
if (!zoneObjects.length)
|
|
197
|
+
return null;
|
|
198
|
+
root.updateWorldMatrix(true, true);
|
|
199
|
+
const levels = levelObjects
|
|
200
|
+
.map((object) => ({ object, y: object.getWorldPosition(new Vector3()).y }))
|
|
201
|
+
.sort((a, b) => a.y - b.y);
|
|
202
|
+
const levelOf = (object) => {
|
|
203
|
+
for (let x = object; x; x = x.parent) {
|
|
204
|
+
const index = levels.findIndex((level) => level.object === x);
|
|
205
|
+
if (index >= 0)
|
|
206
|
+
return index;
|
|
207
|
+
}
|
|
208
|
+
return 0;
|
|
209
|
+
};
|
|
210
|
+
const point = new Vector3();
|
|
211
|
+
const zones = [];
|
|
212
|
+
for (const object of zoneObjects) {
|
|
213
|
+
const polygon = object.userData.polygon;
|
|
214
|
+
if (!polygon || polygon.length < 3)
|
|
215
|
+
continue;
|
|
216
|
+
const levelIndex = levelOf(object);
|
|
217
|
+
// Polygons are level-local [x, z]: bring them to world XZ through the level's frame.
|
|
218
|
+
const frame = levels[levelIndex]?.object ?? object.parent ?? object;
|
|
219
|
+
zones.push({
|
|
220
|
+
id: object.userData.pascalId ?? object.uuid,
|
|
221
|
+
polygon: polygon.map(([x, z]) => {
|
|
222
|
+
point.set(x, 0, z).applyMatrix4(frame.matrixWorld);
|
|
223
|
+
return [point.x, point.z];
|
|
224
|
+
}),
|
|
225
|
+
levelIndex,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
if (!zones.length)
|
|
229
|
+
return null;
|
|
230
|
+
const isIdentity = (object) => {
|
|
231
|
+
const kind = object.userData.kind;
|
|
232
|
+
return kind === 'door' || kind === 'window' || kind === 'item';
|
|
233
|
+
};
|
|
234
|
+
const walls = wallRoots.flatMap((object) => meshesOf(object, isIdentity));
|
|
235
|
+
const bounds = new Box3();
|
|
236
|
+
for (const zone of zones)
|
|
237
|
+
for (const [x, z] of zone.polygon)
|
|
238
|
+
bounds.expandByPoint(point.set(x, 0, z));
|
|
239
|
+
return {
|
|
240
|
+
bounds,
|
|
241
|
+
levelY: levels.map((level) => level.y),
|
|
242
|
+
zones,
|
|
243
|
+
covers,
|
|
244
|
+
walls,
|
|
245
|
+
openings,
|
|
246
|
+
lamps: feedLamps(),
|
|
247
|
+
virtualLampsOn,
|
|
248
|
+
configureRaycaster: (raycaster) => setSurfaceRaycastLayers(raycaster.layers),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
function isEmitter(material) {
|
|
252
|
+
return (!material.transparent &&
|
|
253
|
+
// An editor highlight's tint isn't a glow: a selected lamp's frame must not light up.
|
|
254
|
+
!isHighlight(material) &&
|
|
255
|
+
!!material.emissive &&
|
|
256
|
+
material.emissive.getHex() !== 0 &&
|
|
257
|
+
(material.emissiveIntensity ?? 1) > 0);
|
|
258
|
+
}
|
|
259
|
+
function emissiveMeshes(root) {
|
|
260
|
+
const out = [];
|
|
261
|
+
root.traverse((object) => {
|
|
262
|
+
const mesh = object;
|
|
263
|
+
if (!mesh.isMesh || !mesh.material)
|
|
264
|
+
return;
|
|
265
|
+
const materials = (Array.isArray(mesh.material) ? mesh.material : [mesh.material]);
|
|
266
|
+
if (materials.some(isEmitter))
|
|
267
|
+
out.push(mesh);
|
|
268
|
+
});
|
|
269
|
+
return out;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* A lamp's private emitter material: the exported hue at a fixed display brightness (× display
|
|
273
|
+
* white, ÷ exposure) while its lamp is on — always reads as lit and blooms; dark when off, even
|
|
274
|
+
* by day. Dark emissive parts (cords, trims) stay dark.
|
|
275
|
+
*/
|
|
276
|
+
function lampEmitterMaterial(source, on) {
|
|
277
|
+
const material = new MeshStandardNodeMaterial();
|
|
278
|
+
for (const key of Object.keys(source)) {
|
|
279
|
+
if (key === 'type' || key === 'uuid' || key === 'id' || key === 'defines')
|
|
280
|
+
continue;
|
|
281
|
+
material[key] = source[key];
|
|
282
|
+
}
|
|
283
|
+
material.name = source.name;
|
|
284
|
+
material.userData = { ...source.userData, daylightLampMaterial: true };
|
|
285
|
+
const emissive = materialEmissive;
|
|
286
|
+
const lum = emissive.dot(vec3(0.2126, 0.7152, 0.0722)).max(1e-4);
|
|
287
|
+
material.emissiveNode = emissive
|
|
288
|
+
.div(lum)
|
|
289
|
+
.mul(smoothstep(0.02, 0.08, lum))
|
|
290
|
+
.mul(on)
|
|
291
|
+
.mul(daylightUniforms.emitterDisplay)
|
|
292
|
+
.mul(daylightUniforms.emitterExpInv);
|
|
293
|
+
return material;
|
|
294
|
+
}
|
|
295
|
+
const lampEmitters = new Map();
|
|
296
|
+
/**
|
|
297
|
+
* Lamp shades and bulbs follow their own lamp (the enhanced viewer's isolated emitters): each
|
|
298
|
+
* lamp's emissive meshes get private materials whose emission follows that lamp's switch. A light
|
|
299
|
+
* can sit on a child item while the glowing mesh belongs to its parent item: search the item, then
|
|
300
|
+
* its nearest item ancestor. Re-installed if the item swaps its materials.
|
|
301
|
+
*/
|
|
302
|
+
export function syncLampEmitters() {
|
|
303
|
+
const registered = new Set(sceneRegistry.nodes.values());
|
|
304
|
+
for (const feed of lampFeeds) {
|
|
305
|
+
for (const light of feed.lights()) {
|
|
306
|
+
const root = light.object ?? (light.nodeId ? sceneRegistry.nodes.get(light.nodeId) : undefined);
|
|
307
|
+
if (!root)
|
|
308
|
+
continue;
|
|
309
|
+
let meshes = emissiveMeshes(root);
|
|
310
|
+
for (let parent = root.parent; !meshes.length && parent; parent = parent.parent) {
|
|
311
|
+
const extras = parent.userData;
|
|
312
|
+
if (extras.pascalId || registered.has(parent)) {
|
|
313
|
+
if (extras.kind === undefined || extras.kind === 'item')
|
|
314
|
+
meshes = emissiveMeshes(parent);
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
const on = feed.intensity(light) > 0 ? 1 : 0;
|
|
319
|
+
for (const mesh of meshes) {
|
|
320
|
+
let emitter = lampEmitters.get(mesh);
|
|
321
|
+
if (emitter && mesh.material !== emitter.installed) {
|
|
322
|
+
for (const clone of emitter.clones)
|
|
323
|
+
clone.dispose();
|
|
324
|
+
emitter = undefined;
|
|
325
|
+
}
|
|
326
|
+
if (!emitter) {
|
|
327
|
+
const lit = uniform(on);
|
|
328
|
+
const originals = (Array.isArray(mesh.material) ? mesh.material : [mesh.material]);
|
|
329
|
+
const clones = [];
|
|
330
|
+
const next = originals.map((material) => {
|
|
331
|
+
if (!isEmitter(material))
|
|
332
|
+
return material;
|
|
333
|
+
const clone = lampEmitterMaterial(material, lit);
|
|
334
|
+
clones.push(clone);
|
|
335
|
+
return clone;
|
|
336
|
+
});
|
|
337
|
+
const installed = Array.isArray(mesh.material) ? next : next[0];
|
|
338
|
+
emitter = { original: mesh.material, installed, clones, on: lit };
|
|
339
|
+
lampEmitters.set(mesh, emitter);
|
|
340
|
+
mesh.material = installed;
|
|
341
|
+
}
|
|
342
|
+
emitter.on.value = on;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
/** The lamps' own materials back (the daylight look left). */
|
|
348
|
+
export function restoreLampEmitters() {
|
|
349
|
+
for (const [mesh, emitter] of lampEmitters) {
|
|
350
|
+
if (mesh.material === emitter.installed)
|
|
351
|
+
mesh.material = emitter.original;
|
|
352
|
+
for (const clone of emitter.clones)
|
|
353
|
+
clone.dispose();
|
|
354
|
+
}
|
|
355
|
+
lampEmitters.clear();
|
|
356
|
+
}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { Box3, type Camera, Color, type Object3D, Raycaster, Vector2, Vector3 } from 'three';
|
|
2
|
+
export type RoomLamp = {
|
|
3
|
+
position: Vector3;
|
|
4
|
+
color: Color;
|
|
5
|
+
/** Candela-ish, after the per-room soft cap. */
|
|
6
|
+
intensity: number;
|
|
7
|
+
range: number;
|
|
8
|
+
itemId?: string;
|
|
9
|
+
/** Lit right now (the lamp's own switch). */
|
|
10
|
+
on: boolean;
|
|
11
|
+
/** Spotlight cone (exterior lamps keep it; inside, lamps fill their room). */
|
|
12
|
+
spot?: {
|
|
13
|
+
direction: Vector3;
|
|
14
|
+
cosOuter: number;
|
|
15
|
+
cosInner: number;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* The light's authored intensity (cd, as the real light shines at it). Exterior lamps light
|
|
19
|
+
* façades and ground with it, as the scene's lights do — `intensity` is a room-metering value,
|
|
20
|
+
* log-compressed from it, and only caps it (layouts with physical-looking 1800 cd stay tame).
|
|
21
|
+
*/
|
|
22
|
+
candela?: number;
|
|
23
|
+
};
|
|
24
|
+
export type RoomZone = {
|
|
25
|
+
id: string;
|
|
26
|
+
/** World XZ. */
|
|
27
|
+
polygon: [number, number][];
|
|
28
|
+
levelIndex: number;
|
|
29
|
+
};
|
|
30
|
+
export type RoomOpeningRoot = {
|
|
31
|
+
object: Object3D;
|
|
32
|
+
kind: 'window' | 'door';
|
|
33
|
+
id: string;
|
|
34
|
+
};
|
|
35
|
+
/** What the room system needs from a scene (live parametric or baked artifact). */
|
|
36
|
+
export type RoomSceneInput = {
|
|
37
|
+
bounds: Box3;
|
|
38
|
+
/** World Y of each level, assembled (not exploded), ascending. */
|
|
39
|
+
levelY: number[];
|
|
40
|
+
zones: RoomZone[];
|
|
41
|
+
/** Ceilings, roofs and slabs (enclosure rays) with their kind. */
|
|
42
|
+
covers: {
|
|
43
|
+
object: Object3D;
|
|
44
|
+
kind: 'ceiling' | 'roof' | 'slab';
|
|
45
|
+
}[];
|
|
46
|
+
/** Wall meshes only (openings and their parts excluded), for walkable-opening rays. */
|
|
47
|
+
walls: Object3D[];
|
|
48
|
+
openings: RoomOpeningRoot[];
|
|
49
|
+
lamps: RoomLamp[];
|
|
50
|
+
/** Whether rooms without lamps of their own get their virtual ceiling lamp lit. */
|
|
51
|
+
virtualLampsOn: boolean;
|
|
52
|
+
/** Layers the enclosure/opening rays test (batched originals live off layer 0). */
|
|
53
|
+
configureRaycaster?: (raycaster: Raycaster) => void;
|
|
54
|
+
};
|
|
55
|
+
type Portal = {
|
|
56
|
+
center: Vector3;
|
|
57
|
+
/** Points into the room. */
|
|
58
|
+
normal: Vector3;
|
|
59
|
+
width: number;
|
|
60
|
+
height: number;
|
|
61
|
+
room: number;
|
|
62
|
+
/** 0 window glass · 1 interior opening (neighbour light) · 2 exterior opening. */
|
|
63
|
+
kind: 0 | 1 | 2;
|
|
64
|
+
neighbor: number;
|
|
65
|
+
doorId?: string;
|
|
66
|
+
gain: number;
|
|
67
|
+
};
|
|
68
|
+
type Opening = {
|
|
69
|
+
a: number;
|
|
70
|
+
b: number;
|
|
71
|
+
center: Vector3;
|
|
72
|
+
/** Horizontal, from room a towards b. */
|
|
73
|
+
normal: Vector3;
|
|
74
|
+
width: number;
|
|
75
|
+
height: number;
|
|
76
|
+
doorId?: string;
|
|
77
|
+
};
|
|
78
|
+
type Room = {
|
|
79
|
+
zone: RoomZone;
|
|
80
|
+
levelIndex: number;
|
|
81
|
+
enclosed: boolean;
|
|
82
|
+
area: number;
|
|
83
|
+
centroid: Vector3;
|
|
84
|
+
ceilingY: number;
|
|
85
|
+
/** Height of the topmost cover above the room, and whether it is opaque from above. */
|
|
86
|
+
coverY: number;
|
|
87
|
+
coverIsRoof: boolean;
|
|
88
|
+
/** Interior surface area (floor + ceiling + walls) — used by the meters. */
|
|
89
|
+
surfaces: number;
|
|
90
|
+
/** Average irradiance from lamps at full level (after the soft cap), and from daylight. */
|
|
91
|
+
lampE: number;
|
|
92
|
+
dayE: number;
|
|
93
|
+
lamps: RoomLamp[];
|
|
94
|
+
portals: Portal[];
|
|
95
|
+
};
|
|
96
|
+
export type SkySampler = (outward: Vector3) => Color;
|
|
97
|
+
export declare class RoomLighting {
|
|
98
|
+
rooms: Room[];
|
|
99
|
+
/** Lamps outside every room (lanterns, garden lights), lit analytically on outside surfaces. */
|
|
100
|
+
exteriorLamps: RoomLamp[];
|
|
101
|
+
portals: Portal[];
|
|
102
|
+
openings: Opening[];
|
|
103
|
+
levelY: number[];
|
|
104
|
+
private data;
|
|
105
|
+
private map;
|
|
106
|
+
private origin;
|
|
107
|
+
private gridW;
|
|
108
|
+
private gridH;
|
|
109
|
+
private levels;
|
|
110
|
+
private lampTexel;
|
|
111
|
+
private lastSky;
|
|
112
|
+
readonly portalGain: import("three/webgpu").UniformNode<"float", number>;
|
|
113
|
+
readonly bounceGain: import("three/webgpu").UniformNode<"float", number>;
|
|
114
|
+
readonly lampGain: import("three/webgpu").UniformNode<"float", number>;
|
|
115
|
+
/**
|
|
116
|
+
* Lamp light as drawn, not as metered: eased below 1 while the camera is outside at night, so
|
|
117
|
+
* lit rooms seen from out there keep an indoor-like exposure (the meters ignore it).
|
|
118
|
+
*/
|
|
119
|
+
readonly lampView: import("three/webgpu").UniformNode<"float", number>;
|
|
120
|
+
/** Sky-IBL multiplier in enclosed rooms. */
|
|
121
|
+
readonly indoorEnv: import("three/webgpu").UniformNode<"float", number>;
|
|
122
|
+
/** Window spill outside at night. */
|
|
123
|
+
readonly spillGain: import("three/webgpu").UniformNode<"float", number>;
|
|
124
|
+
/** Ceiling for lamp irradiance on any surface (soft): ≈3× the room-average target. */
|
|
125
|
+
readonly lampMaxE: import("three/webgpu").UniformNode<"float", number>;
|
|
126
|
+
/** Sky patch on the floor (0 on the Low tier). */
|
|
127
|
+
readonly skyPatch: import("three/webgpu").UniformNode<"float", number>;
|
|
128
|
+
/** Light through interior openings + threshold crossfade (off on the Low tier). */
|
|
129
|
+
readonly softRooms: import("three/webgpu").UniformNode<"float", number>;
|
|
130
|
+
readonly cameraY: import("three/webgpu").UniformNode<"float", number>;
|
|
131
|
+
readonly focusedLevel: import("three/webgpu").UniformNode<"int", number>;
|
|
132
|
+
/**
|
|
133
|
+
* The level arrays live in one shared group, uploaded when a level moves: an array uniform in
|
|
134
|
+
* the default (per-object) group is re-sent for every object in every pass, whether it changed
|
|
135
|
+
* or not — some 760 uploads a frame in a furnished flat.
|
|
136
|
+
*/
|
|
137
|
+
private readonly levelGroup;
|
|
138
|
+
private readonly u;
|
|
139
|
+
/** Rebuild from a scene: rooms, lamps, portals, openings, room-id map. */
|
|
140
|
+
build(input: RoomSceneInput): void;
|
|
141
|
+
/** CPU-side room lookup (same rule as the shader, without the normal offset). */
|
|
142
|
+
roomAt(p: Vector3): number;
|
|
143
|
+
/**
|
|
144
|
+
* Whether each level is on screen. A camera over a level whose upper floors are hidden stands
|
|
145
|
+
* in those floors' rooms by height alone, but it is outdoors: metered and white-balanced as
|
|
146
|
+
* such, not as a lamplit room it cannot see.
|
|
147
|
+
*/
|
|
148
|
+
levelShown: boolean[];
|
|
149
|
+
/**
|
|
150
|
+
* What the lit rooms in view add to an outside camera's meter: a blend of the exterior reading
|
|
151
|
+
* and the rooms' own irradiance (their indoor meter), weighted by the share of the frame each
|
|
152
|
+
* fills — fully where it is open from above (its level the top one shown), a little where only
|
|
153
|
+
* its windows show — counted twice over, as the eye goes to the lit rooms. Rooms only ever
|
|
154
|
+
* brighten the reading, so daytime views are unchanged. `roomsE` is the rooms' own reading (0
|
|
155
|
+
* with none in view): what they meter at from inside. Given the outdoor illuminant, `illuminant`
|
|
156
|
+
* is the light the eye adapts to: the outdoors shifted towards the rooms' light by their pull.
|
|
157
|
+
*/
|
|
158
|
+
viewedMeter(camera: Camera, exteriorE: number, skyE: number, outdoor?: Color, skyLum?: number): {
|
|
159
|
+
sceneE: number;
|
|
160
|
+
roomsE: number;
|
|
161
|
+
illuminant: Color | null;
|
|
162
|
+
};
|
|
163
|
+
isEnclosedAt(p: Vector3): {
|
|
164
|
+
room: number;
|
|
165
|
+
inside: boolean;
|
|
166
|
+
};
|
|
167
|
+
/** Explode / level motion: live world Y of each level (assembled + offset). */
|
|
168
|
+
setLevelOffsets(offsets: number[]): void;
|
|
169
|
+
/** Re-run the light transfer (after a quality-tier change of the soft room map). */
|
|
170
|
+
refresh(): void;
|
|
171
|
+
/** Lamp switches changed: rewrite lamp texels (+ the light carried through openings). */
|
|
172
|
+
setLampStates(isOn: (lamp: RoomLamp) => boolean): boolean;
|
|
173
|
+
/** Door openness (0 closed … 1 open) → portal gain; a closed interior door leaves a crack. */
|
|
174
|
+
updateDoors(openness: (doorId: string) => number): void;
|
|
175
|
+
/** 0…1: share of the room's lamp power currently lit. */
|
|
176
|
+
lampFraction(room: number): number;
|
|
177
|
+
/**
|
|
178
|
+
* Analytic light meter: the average irradiance a camera standing in `room` would see —
|
|
179
|
+
* daylight (portals + bounce, scaled like the shader's gains) + lamps + leftover sky IBL.
|
|
180
|
+
*/
|
|
181
|
+
meter(room: number, skyE: number): number;
|
|
182
|
+
/** The illuminant colour inside `room` (daylight through the windows + its lit lamps). */
|
|
183
|
+
roomIlluminant(room: number, outdoor: Color, skyLum: number): Color | null;
|
|
184
|
+
/**
|
|
185
|
+
* Per sun/sky change: radiance leaving each portal into the room, and the per-room sun
|
|
186
|
+
* bounce (sun patch on the floor re-radiating — the main thing screen-space GI misses).
|
|
187
|
+
* `skyRadiance(dir)` returns linear sky radiance seen *through* the window (outward dir).
|
|
188
|
+
*/
|
|
189
|
+
updateSky(skyRadiance: SkySampler, sunDir: Vector3, sunColor: Color, sunIrradiance: number): void;
|
|
190
|
+
private portalBase;
|
|
191
|
+
private activeBase;
|
|
192
|
+
private allocateData;
|
|
193
|
+
/**
|
|
194
|
+
* The outside is lit only by windows of rooms whose lamp is on and by exterior lamps that are
|
|
195
|
+
* on: list just those, so the per-pixel spill and lantern loops run over nothing by day and
|
|
196
|
+
* over the lit few at night, not over every window of the building.
|
|
197
|
+
*/
|
|
198
|
+
private writeActiveLists;
|
|
199
|
+
private uploadMap;
|
|
200
|
+
private markData;
|
|
201
|
+
private writeLamp;
|
|
202
|
+
/** Interior portals emit the neighbour room's average radiance: L = E_avg·ρ/π (lamps + daylight). */
|
|
203
|
+
private writeInteriorPortals;
|
|
204
|
+
private set;
|
|
205
|
+
/**
|
|
206
|
+
* Walk every boundary between two room cells (or a room and outside) on the room map and cast
|
|
207
|
+
* short horizontal rays against the walls at 0.35 m and 1.6 m: open at both heights = a walkable
|
|
208
|
+
* opening (doorway, arch, open-plan edge). Windows don't count (sills are above 0.35 m).
|
|
209
|
+
*/
|
|
210
|
+
private detectOpenings;
|
|
211
|
+
/** Level index for a world-space height (live, explode-offset level heights). */
|
|
212
|
+
private levelOfNode;
|
|
213
|
+
/** Room index (−1 outside) at a world-space position. */
|
|
214
|
+
roomAtNode: import("three/tsl").FnNode<[any], import("three/webgpu").Node<"int">>;
|
|
215
|
+
/** E' = Emax·(1 − e^(−E/Emax)), on luminance, hue kept — near-field hot spots compress, never blow out. */
|
|
216
|
+
private softClampLamps;
|
|
217
|
+
/**
|
|
218
|
+
* Dollhouse / roofless views: an enclosed room is open to the sky when the camera is above its
|
|
219
|
+
* topmost cover and that cover is hidden from there: a ceiling (hidden from above), or anything
|
|
220
|
+
* once its level is focused (the levels over it hidden). From inside, under the cover, the room
|
|
221
|
+
* stays enclosed whatever is focused: it must not take the full sky on top of its windows.
|
|
222
|
+
* `h2` is the room's third header texel: [coverY, roof, level, 0].
|
|
223
|
+
*/
|
|
224
|
+
private openToSky;
|
|
225
|
+
private texel;
|
|
226
|
+
/**
|
|
227
|
+
* vec4(irradiance, enclosure):
|
|
228
|
+
* irradiance — lamp + portal + bounce diffuse irradiance (linear)
|
|
229
|
+
* enclosure — multiplier for the sky IBL (1 outdoors, `indoorEnv` in enclosed rooms)
|
|
230
|
+
*/
|
|
231
|
+
lightingNode: import("three/tsl").FnNode<[], import("three/webgpu").JoinNode<"vec4">>;
|
|
232
|
+
/**
|
|
233
|
+
* Light thrown *out* of lit rooms through their windows onto exterior surfaces (ground,
|
|
234
|
+
* terraces, balconies, reveals). Two terms per exterior portal:
|
|
235
|
+
* - gobo: the room's main lamp projected through the window rectangle — the crisp,
|
|
236
|
+
* window-shaped pool on a lawn at night (exact for an unoccluded point lamp)
|
|
237
|
+
* - glow: the pane as a soft area emitter (lamp-lit room radiance), for the halo
|
|
238
|
+
* Receivers below the window's storey floor − 0.18 m get nothing, so upper windows can't
|
|
239
|
+
* project through slabs onto the ground.
|
|
240
|
+
*/
|
|
241
|
+
exteriorSpillNode: import("three/tsl").FnNode<[any, any], import("three/webgpu").Node<"vec3">>;
|
|
242
|
+
/**
|
|
243
|
+
* Exterior lamps (lanterns, bollards, garden lights) as sized sources with the same falloff and
|
|
244
|
+
* soft clamp as room lamps — no hot spots a bollard's height from the paving. Applied to
|
|
245
|
+
* outside surfaces only.
|
|
246
|
+
*/
|
|
247
|
+
exteriorLampsNode: import("three/tsl").FnNode<[any, any], any>;
|
|
248
|
+
/** 1 where exterior lights may shine (outside, or rooms open to the sky), 0 inside. */
|
|
249
|
+
exteriorMaskNode: import("three/tsl").FnNode<[], import("three/webgpu").VarNode<"float", import("three/webgpu").ConstNode<"float", number>>>;
|
|
250
|
+
/** Linear lamp radiance of a room (its lit lamps) — the night tint of its window panes. */
|
|
251
|
+
roomGlowNode: import("three/tsl").FnNode<[any], import("three/webgpu").Node<"vec3">>;
|
|
252
|
+
}
|
|
253
|
+
export declare function pointInPolygon(x: number, z: number, poly: [number, number][]): boolean;
|
|
254
|
+
export declare function polygonPerimeter(poly: [number, number][]): number;
|
|
255
|
+
export declare function polygonArea(poly: [number, number][]): number;
|
|
256
|
+
export declare function polygonCentroid(poly: [number, number][]): Vector2;
|
|
257
|
+
/** Tanner Helland blackbody fit → linear RGB. */
|
|
258
|
+
export declare function kelvin(k: number): Color;
|
|
259
|
+
/** The daylight look's room system (one viewer canvas at a time). */
|
|
260
|
+
export declare const daylightRooms: RoomLighting;
|
|
261
|
+
export {};
|
|
262
|
+
//# sourceMappingURL=rooms.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rooms.d.ts","sourceRoot":"","sources":["../../../src/lib/daylight/rooms.ts"],"names":[],"mappings":"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"}
|