@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.
Files changed (199) hide show
  1. package/dist/components/viewer/daylight-atmosphere.d.ts +9 -0
  2. package/dist/components/viewer/daylight-atmosphere.d.ts.map +1 -0
  3. package/dist/components/viewer/daylight-atmosphere.js +315 -0
  4. package/dist/components/viewer/drone-controller.d.ts +15 -0
  5. package/dist/components/viewer/drone-controller.d.ts.map +1 -0
  6. package/dist/components/viewer/drone-controller.js +194 -0
  7. package/dist/components/viewer/glb-interactive.d.ts +4 -18
  8. package/dist/components/viewer/glb-interactive.d.ts.map +1 -1
  9. package/dist/components/viewer/glb-interactive.js +32 -268
  10. package/dist/components/viewer/glb-scene-motion.d.ts +8 -0
  11. package/dist/components/viewer/glb-scene-motion.d.ts.map +1 -0
  12. package/dist/components/viewer/glb-scene-motion.js +57 -0
  13. package/dist/components/viewer/glb-scene.d.ts +3 -1
  14. package/dist/components/viewer/glb-scene.d.ts.map +1 -1
  15. package/dist/components/viewer/glb-scene.js +289 -49
  16. package/dist/components/viewer/glb-selection-callout.d.ts +17 -0
  17. package/dist/components/viewer/glb-selection-callout.d.ts.map +1 -0
  18. package/dist/components/viewer/glb-selection-callout.js +54 -0
  19. package/dist/components/viewer/glb-walkthrough-controller.d.ts.map +1 -1
  20. package/dist/components/viewer/glb-walkthrough-controller.js +6 -3
  21. package/dist/components/viewer/index.d.ts +5 -0
  22. package/dist/components/viewer/index.d.ts.map +1 -1
  23. package/dist/components/viewer/index.js +30 -6
  24. package/dist/components/viewer/lights.d.ts.map +1 -1
  25. package/dist/components/viewer/lights.js +192 -5
  26. package/dist/components/viewer/post-processing.d.ts.map +1 -1
  27. package/dist/components/viewer/post-processing.js +150 -94
  28. package/dist/components/viewer/scene-environment.js +4 -5
  29. package/dist/components/viewer/selection-callout.d.ts +51 -0
  30. package/dist/components/viewer/selection-callout.d.ts.map +1 -0
  31. package/dist/components/viewer/selection-callout.js +197 -0
  32. package/dist/components/viewer/use-daylight-rooms.d.ts +13 -0
  33. package/dist/components/viewer/use-daylight-rooms.d.ts.map +1 -0
  34. package/dist/components/viewer/use-daylight-rooms.js +236 -0
  35. package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
  36. package/dist/components/viewer/viewer-camera.js +12 -1
  37. package/dist/index.d.ts +21 -7
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +23 -7
  40. package/dist/lib/daylight/ground.d.ts +21 -0
  41. package/dist/lib/daylight/ground.d.ts.map +1 -0
  42. package/dist/lib/daylight/ground.js +53 -0
  43. package/dist/lib/daylight/location.d.ts +32 -0
  44. package/dist/lib/daylight/location.d.ts.map +1 -0
  45. package/dist/lib/daylight/location.js +74 -0
  46. package/dist/lib/daylight/look.d.ts +33 -0
  47. package/dist/lib/daylight/look.d.ts.map +1 -0
  48. package/dist/lib/daylight/look.js +13 -0
  49. package/dist/lib/daylight/material-hooks.d.ts +2 -0
  50. package/dist/lib/daylight/material-hooks.d.ts.map +1 -0
  51. package/dist/lib/daylight/material-hooks.js +150 -0
  52. package/dist/lib/daylight/room-context.d.ts +12 -0
  53. package/dist/lib/daylight/room-context.d.ts.map +1 -0
  54. package/dist/lib/daylight/room-context.js +53 -0
  55. package/dist/lib/daylight/room-scene.d.ts +54 -0
  56. package/dist/lib/daylight/room-scene.d.ts.map +1 -0
  57. package/dist/lib/daylight/room-scene.js +356 -0
  58. package/dist/lib/daylight/rooms.d.ts +262 -0
  59. package/dist/lib/daylight/rooms.d.ts.map +1 -0
  60. package/dist/lib/daylight/rooms.js +1223 -0
  61. package/dist/lib/daylight/runtime.d.ts +33 -0
  62. package/dist/lib/daylight/runtime.d.ts.map +1 -0
  63. package/dist/lib/daylight/runtime.js +42 -0
  64. package/dist/lib/daylight/sky.d.ts +26 -0
  65. package/dist/lib/daylight/sky.d.ts.map +1 -0
  66. package/dist/lib/daylight/sky.js +217 -0
  67. package/dist/lib/daylight/solar.d.ts +46 -0
  68. package/dist/lib/daylight/solar.d.ts.map +1 -0
  69. package/dist/lib/daylight/solar.js +156 -0
  70. package/dist/lib/device-tier.d.ts +29 -0
  71. package/dist/lib/device-tier.d.ts.map +1 -0
  72. package/dist/lib/device-tier.js +71 -0
  73. package/dist/lib/dispose-object3d.d.ts.map +1 -1
  74. package/dist/lib/dispose-object3d.js +4 -2
  75. package/dist/lib/drone-flight.d.ts +44 -0
  76. package/dist/lib/drone-flight.d.ts.map +1 -0
  77. package/dist/lib/drone-flight.js +121 -0
  78. package/dist/lib/editor-highlight.d.ts +16 -0
  79. package/dist/lib/editor-highlight.d.ts.map +1 -0
  80. package/dist/lib/editor-highlight.js +33 -0
  81. package/dist/lib/foundation-materials.d.ts.map +1 -1
  82. package/dist/lib/foundation-materials.js +2 -4
  83. package/dist/lib/glb-merge.d.ts +23 -0
  84. package/dist/lib/glb-merge.d.ts.map +1 -0
  85. package/dist/lib/glb-merge.js +184 -0
  86. package/dist/lib/hardware-geometry.d.ts +21 -0
  87. package/dist/lib/hardware-geometry.d.ts.map +1 -0
  88. package/dist/lib/hardware-geometry.js +283 -0
  89. package/dist/lib/layer-pass.d.ts +2 -0
  90. package/dist/lib/layer-pass.d.ts.map +1 -1
  91. package/dist/lib/layer-pass.js +20 -0
  92. package/dist/lib/materials.d.ts.map +1 -1
  93. package/dist/lib/materials.js +50 -23
  94. package/dist/lib/merged-outline-node.d.ts.map +1 -1
  95. package/dist/lib/merged-outline-node.js +7 -1
  96. package/dist/lib/occlusion-culler.d.ts +34 -0
  97. package/dist/lib/occlusion-culler.d.ts.map +1 -0
  98. package/dist/lib/occlusion-culler.js +698 -0
  99. package/dist/lib/post-processing-resources.d.ts +4 -0
  100. package/dist/lib/post-processing-resources.d.ts.map +1 -1
  101. package/dist/lib/post-processing-resources.js +5 -0
  102. package/dist/lib/readback-rows.d.ts +14 -0
  103. package/dist/lib/readback-rows.d.ts.map +1 -0
  104. package/dist/lib/readback-rows.js +31 -0
  105. package/dist/lib/reveal-pose.d.ts.map +1 -1
  106. package/dist/lib/reveal-pose.js +3 -0
  107. package/dist/lib/scene-matrices.d.ts +12 -0
  108. package/dist/lib/scene-matrices.d.ts.map +1 -0
  109. package/dist/lib/scene-matrices.js +287 -0
  110. package/dist/lib/scene-pass-mrt.d.ts +5 -2
  111. package/dist/lib/scene-pass-mrt.d.ts.map +1 -1
  112. package/dist/lib/scene-pass-mrt.js +7 -2
  113. package/dist/lib/scene-themes.d.ts +0 -2
  114. package/dist/lib/scene-themes.d.ts.map +1 -1
  115. package/dist/lib/scene-themes.js +0 -2
  116. package/dist/lib/scene-visibility.d.ts +2 -0
  117. package/dist/lib/scene-visibility.d.ts.map +1 -1
  118. package/dist/lib/scene-visibility.js +12 -3
  119. package/dist/lib/site-surfaces.d.ts +1 -8
  120. package/dist/lib/site-surfaces.d.ts.map +1 -1
  121. package/dist/lib/site-surfaces.js +0 -19
  122. package/dist/lib/snapshot-pipeline.d.ts.map +1 -1
  123. package/dist/lib/snapshot-pipeline.js +24 -40
  124. package/dist/lib/texture-reference.d.ts.map +1 -1
  125. package/dist/lib/texture-reference.js +10 -8
  126. package/dist/lib/underground-ambiance.d.ts +0 -2
  127. package/dist/lib/underground-ambiance.d.ts.map +1 -1
  128. package/dist/lib/underground-ambiance.js +0 -2
  129. package/dist/store/use-item-light-pool.d.ts +3 -6
  130. package/dist/store/use-item-light-pool.d.ts.map +1 -1
  131. package/dist/store/use-item-light-pool.js +12 -0
  132. package/dist/store/use-viewer.d.ts +72 -15
  133. package/dist/store/use-viewer.d.ts.map +1 -1
  134. package/dist/store/use-viewer.js +103 -18
  135. package/dist/systems/ceiling/ceiling-system.d.ts +5 -0
  136. package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
  137. package/dist/systems/ceiling/ceiling-system.js +11 -1
  138. package/dist/systems/door/door-system.d.ts.map +1 -1
  139. package/dist/systems/door/door-system.js +501 -145
  140. package/dist/systems/door/leaf-profile.d.ts +11 -0
  141. package/dist/systems/door/leaf-profile.d.ts.map +1 -0
  142. package/dist/systems/door/leaf-profile.js +140 -0
  143. package/dist/systems/interactive/callout-actions.d.ts +72 -0
  144. package/dist/systems/interactive/callout-actions.d.ts.map +1 -0
  145. package/dist/systems/interactive/callout-actions.js +212 -0
  146. package/dist/systems/interactive/interactive-system.d.ts +5 -1
  147. package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
  148. package/dist/systems/interactive/interactive-system.js +44 -121
  149. package/dist/systems/interactive/opening-settled.d.ts +16 -0
  150. package/dist/systems/interactive/opening-settled.d.ts.map +1 -0
  151. package/dist/systems/interactive/opening-settled.js +53 -0
  152. package/dist/systems/item-light/item-light-system.d.ts.map +1 -1
  153. package/dist/systems/item-light/item-light-system.js +22 -214
  154. package/dist/systems/item-light/pooled-lights.d.ts +70 -0
  155. package/dist/systems/item-light/pooled-lights.d.ts.map +1 -0
  156. package/dist/systems/item-light/pooled-lights.js +449 -0
  157. package/dist/systems/level/level-system.d.ts.map +1 -1
  158. package/dist/systems/level/level-system.js +19 -1
  159. package/dist/systems/roof/roof-material-slots.d.ts +14 -0
  160. package/dist/systems/roof/roof-material-slots.d.ts.map +1 -0
  161. package/dist/systems/roof/roof-material-slots.js +13 -0
  162. package/dist/systems/roof/roof-materials.d.ts +25 -2
  163. package/dist/systems/roof/roof-materials.d.ts.map +1 -1
  164. package/dist/systems/roof/roof-materials.js +85 -26
  165. package/dist/systems/roof/roof-system.d.ts +34 -1
  166. package/dist/systems/roof/roof-system.d.ts.map +1 -1
  167. package/dist/systems/roof/roof-system.js +477 -105
  168. package/dist/systems/wall/opening-casing-geometry.d.ts +14 -0
  169. package/dist/systems/wall/opening-casing-geometry.d.ts.map +1 -0
  170. package/dist/systems/wall/opening-casing-geometry.js +102 -0
  171. package/dist/systems/wall/opening-cutout-geometry.d.ts +9 -0
  172. package/dist/systems/wall/opening-cutout-geometry.d.ts.map +1 -1
  173. package/dist/systems/wall/opening-cutout-geometry.js +7 -3
  174. package/dist/systems/wall/wall-materials.d.ts.map +1 -1
  175. package/dist/systems/wall/wall-materials.js +28 -35
  176. package/dist/systems/wall/wall-system.d.ts.map +1 -1
  177. package/dist/systems/wall/wall-system.js +34 -19
  178. package/dist/systems/wall/wall-t-junctions.d.ts +15 -0
  179. package/dist/systems/wall/wall-t-junctions.d.ts.map +1 -0
  180. package/dist/systems/wall/wall-t-junctions.js +149 -0
  181. package/dist/systems/window/window-animation-system.d.ts +3 -12
  182. package/dist/systems/window/window-animation-system.d.ts.map +1 -1
  183. package/dist/systems/window/window-animation-system.js +37 -122
  184. package/dist/systems/window/window-pose.d.ts +31 -0
  185. package/dist/systems/window/window-pose.d.ts.map +1 -0
  186. package/dist/systems/window/window-pose.js +152 -0
  187. package/dist/systems/window/window-roller-shutter.d.ts +8 -0
  188. package/dist/systems/window/window-roller-shutter.d.ts.map +1 -0
  189. package/dist/systems/window/window-roller-shutter.js +64 -0
  190. package/dist/systems/window/window-system.d.ts +2 -11
  191. package/dist/systems/window/window-system.d.ts.map +1 -1
  192. package/dist/systems/window/window-system.js +470 -303
  193. package/package.json +6 -6
  194. package/dist/systems/interactive/control-widget.d.ts +0 -10
  195. package/dist/systems/interactive/control-widget.d.ts.map +0 -1
  196. package/dist/systems/interactive/control-widget.js +0 -35
  197. package/dist/systems/interactive/procedural-controls.d.ts +0 -13
  198. package/dist/systems/interactive/procedural-controls.d.ts.map +0 -1
  199. package/dist/systems/interactive/procedural-controls.js +0 -17
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The colored appearance's built-in sun and sky: real sun position for the site (or a fallback
3
+ * location), a time-of-day preset and a weather preset. One shadowed key (the sun, or a cool moon
4
+ * at night), the sky as a PMREM environment, metered exposure and eye-like white balance.
5
+ * Installed through the generic `SceneAtmosphere` seam, so a plugin atmosphere mounted later
6
+ * still takes precedence.
7
+ */
8
+ export declare function DaylightAtmosphere(): import("react").JSX.Element;
9
+ //# sourceMappingURL=daylight-atmosphere.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daylight-atmosphere.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/daylight-atmosphere.tsx"],"names":[],"mappings":"AAgFA;;;;;;GAMG;AACH,wBAAgB,kBAAkB,gCAySjC"}
@@ -0,0 +1,315 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useInteractive, useScene } from '@pascal-app/core';
4
+ import { useFrame, useThree } from '@react-three/fiber';
5
+ import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
6
+ import { BackSide, Color, MathUtils, Mesh, Scene, SphereGeometry, Vector3 } from 'three';
7
+ import { pmremTexture, positionWorldDirection, vec3 } from 'three/tsl';
8
+ import { MeshBasicNodeMaterial, PMREMGenerator, } from 'three/webgpu';
9
+ import { resolveLocation } from '../../lib/daylight/location';
10
+ import { installDaylightMaterialHooks } from '../../lib/daylight/material-hooks';
11
+ import { restoreLampEmitters } from '../../lib/daylight/room-scene';
12
+ import { daylightRooms } from '../../lib/daylight/rooms';
13
+ import { daylightUniforms, setDaylightRenderer } from '../../lib/daylight/runtime';
14
+ import { createDaylightSky, WEATHER_COVER } from '../../lib/daylight/sky';
15
+ import { presetInstant, solarClockInstant, solarLight, } from '../../lib/daylight/solar';
16
+ import useViewer, {} from '../../store/use-viewer';
17
+ import { SceneAtmosphere } from './scene-atmosphere';
18
+ import { useDaylightRooms } from './use-daylight-rooms';
19
+ /** Irradiance that renders at exposure 1 (≈ a clear afternoon exterior). */
20
+ const METER_REF = 3.2;
21
+ /** Image-based lighting gain on the sky environment. */
22
+ const ENV_INTENSITY = 1.15;
23
+ const MOON_COLOR = new Color('#9fb4ff');
24
+ /** Ground fog range (m), capped by the camera's far plane (the grounds add a horizon haze). */
25
+ const FOG_START = 250;
26
+ const FOG_END = 2600;
27
+ /** Eye-like chromatic adaptation strength (0 = off, 1 = full neutralisation). */
28
+ const WHITE_BALANCE = 0.75;
29
+ /**
30
+ * How much of its extra exposure lamp light seen from outside gives back, relative to the rooms'
31
+ * indoor exposure (0 none … 1 all; a little is kept, so lit windows still read as glowing).
32
+ */
33
+ const LAMP_VIEW_COMP = 0.8;
34
+ /** Maximum metered exposure boost with the camera inside a room. */
35
+ const INTERIOR_EV = 2.4;
36
+ /**
37
+ * Indoors the eye opens up a little past the meter's partial adaptation (EV): rooms read airy
38
+ * rather than dim, by day and by lamplight alike.
39
+ */
40
+ const INTERIOR_BIAS_EV = 0.35;
41
+ /** Sun shadow map per quality tier. */
42
+ const SHADOW_MAP = { high: 4096, medium: 2048, low: 1024 };
43
+ /** EON rough diffuse per quality tier. */
44
+ const EON = { high: 0.6, medium: 0.6, low: 0 };
45
+ const luminance = (c) => 0.2126 * c.r + 0.7152 * c.g + 0.0722 * c.b;
46
+ /** Cosine-weighted hemisphere probes for the sky irradiance colour. */
47
+ const HEMISPHERE = [
48
+ new Vector3(0, 1, 0),
49
+ ...Array.from({ length: 6 }, (_, i) => new Vector3(Math.cos((i * Math.PI) / 3), 0.6, Math.sin((i * Math.PI) / 3)).normalize()),
50
+ ...Array.from({ length: 6 }, (_, i) => new Vector3(Math.cos((i * Math.PI) / 3 + 0.5), 0.15, Math.sin((i * Math.PI) / 3 + 0.5)).normalize()),
51
+ ];
52
+ const UP = new Vector3(0, 1, 0);
53
+ /**
54
+ * The colored appearance's built-in sun and sky: real sun position for the site (or a fallback
55
+ * location), a time-of-day preset and a weather preset. One shadowed key (the sun, or a cool moon
56
+ * at night), the sky as a PMREM environment, metered exposure and eye-like white balance.
57
+ * Installed through the generic `SceneAtmosphere` seam, so a plugin atmosphere mounted later
58
+ * still takes precedence.
59
+ */
60
+ export function DaylightAtmosphere() {
61
+ const renderer = useThree((s) => s.gl);
62
+ const timeOfDay = useViewer((s) => s.timeOfDay);
63
+ const timeOfDayMinutes = useViewer((s) => s.timeOfDayMinutes);
64
+ const skyLive = useViewer((s) => s.skyLive);
65
+ const weather = useViewer((s) => s.weather);
66
+ const deviceLocation = useViewer((s) => s.deviceLocation);
67
+ const transparent = useViewer((s) => s.transparentBackground);
68
+ const quality = useViewer((s) => s.renderQuality);
69
+ const site = useScene((state) => {
70
+ for (const id of state.rootNodeIds) {
71
+ const node = state.nodes[id];
72
+ if (node?.type === 'site')
73
+ return node;
74
+ }
75
+ return undefined;
76
+ });
77
+ const sky = useMemo(() => createDaylightSky(), []);
78
+ // The environment: a PMREM of the visible sky (clouds and aureole, no sun disc — the key light
79
+ // is the sun), re-captured into the same target when the sun or the weather changes, so the
80
+ // environment node installed once stays valid.
81
+ const environment = useMemo(() => {
82
+ const scene = new Scene();
83
+ const material = new MeshBasicNodeMaterial({ side: BackSide, depthWrite: false });
84
+ material.colorNode = sky.skyRadiance(positionWorldDirection, { disc: false });
85
+ // Radiance, not an unlit surface: it keeps the exposure (see the material hooks).
86
+ material.userData.daylightSky = true;
87
+ const dome = new Mesh(new SphereGeometry(10, 48, 24), material);
88
+ scene.add(dome);
89
+ // The viewer mounts its scene once the GPU renderer is initialized; anything else (a test
90
+ // double) renders without the sky environment.
91
+ const ready = renderer.hasInitialized?.() === true;
92
+ if (!ready)
93
+ return { dome, pmrem: null, target: null, capture: () => { } };
94
+ const pmrem = new PMREMGenerator(renderer);
95
+ const capture = (into) => pmrem.fromScene(scene, 0, 0.1, 100, {
96
+ size: 256,
97
+ renderTarget: into,
98
+ });
99
+ const target = capture(null);
100
+ return { dome, pmrem, target, capture: () => capture(target) };
101
+ }, [renderer, sky]);
102
+ const source = useMemo(() => ({
103
+ skyRadiance: (dir) => sky.skyRadiance(dir),
104
+ reflectionRadiance: (dir) => sky.skyRadiance(dir, { disc: false }).mul(ENV_INTENSITY),
105
+ // Just above the horizon in the view's heading: what the ground fog and the backdrop meet at.
106
+ fogRadiance: (dir) => sky.horizonRadiance(vec3(dir.x, 0.004, dir.z)),
107
+ environmentNode: (environment.target
108
+ ? pmremTexture(environment.target.texture).mul(ENV_INTENSITY)
109
+ : vec3(0)),
110
+ sunDirection: new Vector3(0, 1, 0),
111
+ sunColor: new Color(1, 1, 1),
112
+ sunIntensity: 3,
113
+ moonDirection: new Vector3(0, 1, 0),
114
+ moonColor: MOON_COLOR.clone(),
115
+ moonIntensity: 0,
116
+ skyColor: new Color(1, 1, 1),
117
+ groundColor: new Color(0.5, 0.5, 0.5),
118
+ hemisphereIntensity: 0,
119
+ ambientIntensity: 0,
120
+ exposure: 1,
121
+ fogStart: FOG_START,
122
+ fogEnd: FOG_END,
123
+ whiteBalance: new Vector3(1, 1, 1),
124
+ shadow: { mapSize: SHADOW_MAP.high, bias: -0.0002, normalBias: 0.035, radius: 2 },
125
+ elevation: 45,
126
+ }), [environment, sky]);
127
+ const solar = useRef(null);
128
+ const meter = useRef({ ev: 0, keyE: 0, night: 0, skyLum: 0, outdoor: new Color(1, 1, 1) });
129
+ const wbTarget = useMemo(() => new Vector3(1, 1, 1), []);
130
+ useDaylightRooms();
131
+ // Materials attach their daylight hooks for this renderer only (thumbnail canvases elsewhere
132
+ // keep their plain look); the uniforms go back to neutral when the look leaves.
133
+ useLayoutEffect(() => {
134
+ installDaylightMaterialHooks();
135
+ setDaylightRenderer(renderer, true);
136
+ daylightUniforms.albedoClamp.value = 1;
137
+ return () => {
138
+ restoreLampEmitters();
139
+ setDaylightRenderer(renderer, false);
140
+ daylightUniforms.albedoClamp.value = 0;
141
+ daylightUniforms.eon.value = 0;
142
+ daylightUniforms.lampLevel.value = 0;
143
+ daylightUniforms.glowComp.value = 1;
144
+ daylightUniforms.emitterExpInv.value = 1;
145
+ daylightRooms.lampView.value = 1;
146
+ };
147
+ }, [renderer]);
148
+ useEffect(() => {
149
+ daylightUniforms.eon.value = EON[quality];
150
+ source.shadow.mapSize = SHADOW_MAP[quality];
151
+ // The Low tier drops the soft room map (openings) and the sky patch.
152
+ daylightRooms.softRooms.value = quality === 'low' ? 0 : 1;
153
+ daylightRooms.skyPatch.value = quality === 'low' ? 0 : 3;
154
+ daylightRooms.refresh();
155
+ }, [quality, source]);
156
+ // The live sky moves on with the clock, a minute at a time.
157
+ const [clockTick, setClockTick] = useState(0);
158
+ useEffect(() => {
159
+ if (!skyLive || transparent)
160
+ return;
161
+ const timer = setInterval(() => setClockTick((tick) => tick + 1), 60_000);
162
+ return () => clearInterval(timer);
163
+ }, [skyLive, transparent]);
164
+ // Lamp switches follow the sun only when it crosses into or out of the evening, so the
165
+ // person's own switches survive weather changes and the live clock's ticks.
166
+ const lampsLit = useRef(null);
167
+ // Sun, sky and lights follow the time of day: the live clock, an exact time, or a preset
168
+ // (pinned to a neutral fair midday for transparent captures).
169
+ // biome-ignore lint/correctness/useExhaustiveDependencies: clockTick re-runs the live sky.
170
+ useEffect(() => {
171
+ const cover = transparent ? WEATHER_COVER.fair : WEATHER_COVER[weather];
172
+ const { location } = resolveLocation(site, deviceLocation);
173
+ const today = new Date();
174
+ const when = transparent
175
+ ? presetInstant(today, location.lat, location.lng, 'midday')
176
+ : skyLive
177
+ ? today
178
+ : timeOfDayMinutes !== null
179
+ ? solarClockInstant(today, location.lng, timeOfDayMinutes)
180
+ : presetInstant(today, location.lat, location.lng, timeOfDay);
181
+ const sun = solarLight(when, location.lat, location.lng, site?.northRotation ?? 0, solar.current ?? undefined);
182
+ solar.current = sun;
183
+ sky.update(sun, cover);
184
+ source.elevation = sun.elevation;
185
+ // One key light: the sun, or below 1° a cool, dim moon high on the other side of the sky.
186
+ // Clouds dim the sun statistically (no cloud shadows) and soften its shadows.
187
+ if (sun.elevation < 1) {
188
+ source.sunDirection
189
+ .set(-sun.direction.x, Math.max(0.5, Math.abs(sun.direction.y)), -sun.direction.z)
190
+ .normalize();
191
+ source.sunColor.copy(MOON_COLOR);
192
+ source.sunIntensity = 0.18 * sun.night;
193
+ }
194
+ else {
195
+ source.sunDirection.copy(sun.direction);
196
+ source.sunColor.copy(sun.color);
197
+ source.sunIntensity = 3.4 * sun.strength * (1 - 0.5 * cover ** 1.6);
198
+ }
199
+ source.moonIntensity = 0;
200
+ source.shadow.radius = 1.6 + 2.2 * (1 - MathUtils.smoothstep(sun.elevation, 2, 30)) + cover * 3;
201
+ meter.current.keyE = source.sunIntensity * Math.max(0.15, sun.direction.y);
202
+ meter.current.night = sun.night;
203
+ // The outdoor illuminant (sky irradiance + the key light), for the white balance.
204
+ const skyIrr = new Color(0, 0, 0);
205
+ let weight = 0;
206
+ for (const d of HEMISPHERE) {
207
+ skyIrr.add(sky.radianceCpu(d).multiplyScalar(d.y));
208
+ weight += d.y;
209
+ }
210
+ skyIrr.multiplyScalar((Math.PI / weight) * ENV_INTENSITY);
211
+ source.skyColor.copy(skyIrr).multiplyScalar(1 / Math.PI);
212
+ meter.current.skyLum = luminance(skyIrr);
213
+ meter.current.outdoor
214
+ .copy(skyIrr)
215
+ .add(source.sunColor.clone().multiplyScalar(meter.current.keyE));
216
+ // Windows let this sky (and sun) into the rooms.
217
+ daylightRooms.updateSky((dir) => sky.radianceCpu(dir).multiplyScalar(ENV_INTENSITY), sun.direction, sun.color, source.sunIntensity);
218
+ // Lamps follow the light: a smooth level for the room lamps, on/off for the lamp switches.
219
+ daylightUniforms.lampLevel.value = 1 - MathUtils.smoothstep(sun.elevation, -5, 6);
220
+ // Blue hour and night (the presets aim at −4° and −14°; golden hour at +6° stays unlit).
221
+ const lit = sun.elevation < -2;
222
+ if (lit !== lampsLit.current) {
223
+ lampsLit.current = lit;
224
+ useInteractive.getState().setLampDefault(lit, true);
225
+ }
226
+ // Re-capture the sky environment.
227
+ environment.capture();
228
+ }, [
229
+ clockTick,
230
+ deviceLocation,
231
+ environment,
232
+ site,
233
+ skyLive,
234
+ sky,
235
+ source,
236
+ timeOfDay,
237
+ timeOfDayMinutes,
238
+ transparent,
239
+ weather,
240
+ ]);
241
+ // Lamps loaded after the preset applied (items mount late, and an authored "on" default would
242
+ // light them by day) join the time of day; the person's own switches are left alone after.
243
+ useEffect(() => {
244
+ const known = new Set(Object.keys(useInteractive.getState().lampItems));
245
+ return useInteractive.subscribe((state) => {
246
+ // A lamp that leaves (its item unmounts) is new again when it comes back.
247
+ for (const id of known)
248
+ if (!(id in state.lampItems))
249
+ known.delete(id);
250
+ for (const [id, indices] of Object.entries(state.lampItems)) {
251
+ if (known.has(id))
252
+ continue;
253
+ known.add(id);
254
+ for (const index of indices) {
255
+ if (state.items[id]?.controlValues[index] !== state.lampDefault)
256
+ state.setControlValue(id, index, state.lampDefault);
257
+ }
258
+ }
259
+ });
260
+ }, []);
261
+ useEffect(() => () => {
262
+ environment.target?.dispose();
263
+ environment.pmrem?.dispose();
264
+ environment.dome.geometry.dispose();
265
+ environment.dome.material.dispose();
266
+ }, [environment]);
267
+ useFrame(({ camera }, delta) => {
268
+ const dt = Math.min(delta, 0.1);
269
+ // The ground must be fully fogged before the far plane clips it (no hard horizon line).
270
+ source.fogEnd = Math.min(FOG_END, camera.far * 0.9);
271
+ source.fogStart = Math.min(FOG_START, source.fogEnd * 0.1);
272
+ sky.cloudTime.value += dt;
273
+ // Metered exposure: the irradiance where the camera stands — outside: sun + sky; inside a
274
+ // room: its windows + bounce + lamps — exposed towards mid-grey with partial adaptation (dark
275
+ // scenes stay a bit dark), up to +2 EV indoors.
276
+ const { room, inside } = daylightRooms.isEnclosedAt(camera.position);
277
+ daylightUniforms.cameraOutside.value = inside ? 0 : 1;
278
+ const skyE = Math.PI * 0.45 * luminance(sky.radianceCpu(UP)) * ENV_INTENSITY;
279
+ // Outside, the lit rooms in view count too: a cutaway of lamplit rooms exposes for them.
280
+ const viewed = inside
281
+ ? { sceneE: daylightRooms.meter(room, skyE), roomsE: 0, illuminant: null }
282
+ : daylightRooms.viewedMeter(camera, meter.current.keyE + skyE, skyE, meter.current.outdoor, meter.current.skyLum);
283
+ const meterEV = (E, max, bias = 0) => MathUtils.clamp(Math.log2(METER_REF / Math.max(E, 1e-4)) * 0.6 + bias, -0.5, max);
284
+ const indoorEV = (E) => meterEV(E, INTERIOR_EV, INTERIOR_BIAS_EV);
285
+ const target = inside ? indoorEV(viewed.sceneE) : meterEV(viewed.sceneE, 2.2);
286
+ meter.current.ev += (target - meter.current.ev) * Math.min(1, dt * 3);
287
+ source.exposure = 2 ** meter.current.ev;
288
+ // Glows (window panes, lamp shades) stay out of the night exposure lift.
289
+ daylightUniforms.glowComp.value = 1 / 2 ** Math.max(0, meter.current.ev - 0.5);
290
+ daylightUniforms.emitterExpInv.value = 1 / source.exposure;
291
+ // Lamp light seen from outside gives back most of the exposure it gets above what those rooms
292
+ // meter at from inside (back to 1 indoors), so lit rooms look from out there as they do in them.
293
+ const lampView = inside || viewed.roomsE <= 0
294
+ ? 1
295
+ : 2 ** (-LAMP_VIEW_COMP * Math.max(0, meter.current.ev - indoorEV(viewed.roomsE)));
296
+ const shown = daylightRooms.lampView.value;
297
+ daylightRooms.lampView.value = shown + (lampView - shown) * Math.min(1, dt * 3);
298
+ // Eye-like chromatic adaptation (von Kries): divide out the illuminant where the camera is,
299
+ // partially (golden hour and lamplight keep some warmth), luminance-preserving; a bit less
300
+ // at night (moonlight stays blue-ish).
301
+ // Outside, the eye adapts towards the lit rooms in view too (by their pull on the meter).
302
+ const illum = (inside
303
+ ? daylightRooms.roomIlluminant(room, meter.current.outdoor, meter.current.skyLum)
304
+ : viewed.illuminant) ?? meter.current.outdoor;
305
+ const L = luminance(illum);
306
+ if (L > 1e-5) {
307
+ const k = WHITE_BALANCE * (1 - 0.35 * meter.current.night);
308
+ wbTarget.set(MathUtils.lerp(1, L / Math.max(illum.r, 1e-4), k), MathUtils.lerp(1, L / Math.max(illum.g, 1e-4), k), MathUtils.lerp(1, L / Math.max(illum.b, 1e-4), k));
309
+ wbTarget.clampScalar(0.5, 2);
310
+ wbTarget.divideScalar(0.2126 * wbTarget.x + 0.7152 * wbTarget.y + 0.0722 * wbTarget.z);
311
+ }
312
+ source.whiteBalance.lerp(wbTarget, Math.min(1, dt * 2));
313
+ }, -2);
314
+ return _jsx(SceneAtmosphere, { source: source });
315
+ }
@@ -0,0 +1,15 @@
1
+ export type DroneControllerProps = {
2
+ /** Freezes flight and look, e.g. while the host renders a still. */
3
+ paused?: boolean;
4
+ /** Leaves the drone: Esc, or losing the pointer lock it held. Ends the walkthrough by default. */
5
+ onExit?: () => void;
6
+ };
7
+ /**
8
+ * The free-flying drone camera for any scene, parametric or baked: no gravity,
9
+ * floor or collision, so it needs no collider. Picks up from the current camera
10
+ * pose. Pointer lock drives look (click the canvas to lock); WASD / arrows move
11
+ * along the view, Space or E rises, Q or Ctrl sinks, Shift boosts, Alt slows,
12
+ * P frees the cursor (click or P resumes) and Esc leaves.
13
+ */
14
+ export declare function DroneController({ paused, onExit }: DroneControllerProps): null;
15
+ //# sourceMappingURL=drone-controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drone-controller.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/drone-controller.tsx"],"names":[],"mappings":"AAsCA,MAAM,MAAM,oBAAoB,GAAG;IACjC,oEAAoE;IACpE,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,kGAAkG;IAClG,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;CACpB,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,EAAE,MAAc,EAAE,MAAM,EAAE,EAAE,oBAAoB,QAyK/E"}
@@ -0,0 +1,194 @@
1
+ 'use client';
2
+ import { useFrame, useThree } from '@react-three/fiber';
3
+ import { useEffect, useRef } from 'react';
4
+ import { Vector3 } from 'three';
5
+ import { createDroneInput, DRONE_SLOW_MULTIPLIER, droneInputForKey, readDroneInput, seedDroneLook, stepDroneFlight, turnFirstPersonLook, } from '../../lib/drone-flight';
6
+ import useViewer from '../../store/use-viewer';
7
+ import { WALKTHROUGH_FOV } from './glb-walkthrough-controller';
8
+ import { applyWalkthroughCameraClipping } from './viewer-camera';
9
+ const TYPING_TARGET = 'input, textarea, select, [contenteditable]:not([contenteditable="false"]), [role="combobox"], [role="textbox"], [role="dialog"], [role="alertdialog"]';
10
+ function isTypingTarget(target) {
11
+ const element = target;
12
+ return !!element && (element.isContentEditable === true || !!element.closest?.(TYPING_TARGET));
13
+ }
14
+ function requestLock(canvas) {
15
+ // The request can reject asynchronously (the browser's re-lock cooldown);
16
+ // clicking the canvas asks again.
17
+ const result = canvas.requestPointerLock?.();
18
+ if (result && typeof result.catch === 'function')
19
+ result.catch(() => { });
20
+ }
21
+ function exitWalkthrough() {
22
+ useViewer.getState().setWalkthroughMode(false);
23
+ }
24
+ /**
25
+ * The free-flying drone camera for any scene, parametric or baked: no gravity,
26
+ * floor or collision, so it needs no collider. Picks up from the current camera
27
+ * pose. Pointer lock drives look (click the canvas to lock); WASD / arrows move
28
+ * along the view, Space or E rises, Q or Ctrl sinks, Shift boosts, Alt slows,
29
+ * P frees the cursor (click or P resumes) and Esc leaves.
30
+ */
31
+ export function DroneController({ paused = false, onExit }) {
32
+ const camera = useThree((state) => state.camera);
33
+ const gl = useThree((state) => state.gl);
34
+ const lookRef = useRef({ yaw: 0, pitch: 0 });
35
+ const heldKeysRef = useRef(new Set());
36
+ const inputRef = useRef(createDroneInput());
37
+ const velocityRef = useRef(new Vector3());
38
+ const suspendRef = useRef(false);
39
+ const pausedRef = useRef(paused);
40
+ const onExitRef = useRef(onExit);
41
+ const entryPoseRef = useRef(null);
42
+ useEffect(() => {
43
+ pausedRef.current = paused;
44
+ }, [paused]);
45
+ useEffect(() => {
46
+ onExitRef.current = onExit;
47
+ }, [onExit]);
48
+ // First person needs foreshortening: fly a perspective camera, and give the
49
+ // projection back on exit.
50
+ useEffect(() => {
51
+ const previousMode = useViewer.getState().cameraMode;
52
+ if (previousMode === 'orthographic')
53
+ useViewer.getState().setCameraMode('perspective');
54
+ return () => {
55
+ if (previousMode === 'orthographic')
56
+ useViewer.getState().setCameraMode('orthographic');
57
+ };
58
+ }, []);
59
+ // Take off from the pose the camera had on entry. Leaving orthographic swaps
60
+ // in a fresh perspective camera, which inherits that pose.
61
+ useEffect(() => {
62
+ const entry = entryPoseRef.current;
63
+ if (!entry) {
64
+ entryPoseRef.current = {
65
+ camera,
66
+ position: camera.position.clone(),
67
+ quaternion: camera.quaternion.clone(),
68
+ };
69
+ }
70
+ else if (entry.camera !== camera) {
71
+ camera.position.copy(entry.position);
72
+ camera.quaternion.copy(entry.quaternion);
73
+ camera.updateMatrixWorld(true);
74
+ }
75
+ seedDroneLook(lookRef.current, camera.quaternion);
76
+ velocityRef.current.set(0, 0, 0);
77
+ }, [camera]);
78
+ useEffect(() => {
79
+ const perspective = camera;
80
+ if (!perspective.isPerspectiveCamera)
81
+ return;
82
+ const previousFov = perspective.fov;
83
+ perspective.fov = WALKTHROUGH_FOV;
84
+ perspective.updateProjectionMatrix();
85
+ const restoreClipping = applyWalkthroughCameraClipping(perspective);
86
+ return () => {
87
+ restoreClipping();
88
+ perspective.fov = previousFov;
89
+ perspective.updateProjectionMatrix();
90
+ };
91
+ }, [camera]);
92
+ useEffect(() => {
93
+ const canvas = gl.domElement;
94
+ const heldKeys = heldKeysRef.current;
95
+ let wasLocked = false;
96
+ const exit = () => (onExitRef.current ?? exitWalkthrough)();
97
+ // Keyboard focus may still sit on the button that started the flight, where
98
+ // Space would press it again.
99
+ const activeElement = document.activeElement;
100
+ if (activeElement instanceof HTMLElement && !canvas.contains(activeElement)) {
101
+ activeElement.blur();
102
+ }
103
+ const onMouseMove = (event) => {
104
+ if (document.pointerLockElement !== canvas || pausedRef.current)
105
+ return;
106
+ turnFirstPersonLook(lookRef.current, event.movementX, event.movementY, heldKeys.has('AltLeft') || heldKeys.has('AltRight') ? DRONE_SLOW_MULTIPLIER : 1);
107
+ };
108
+ const onClick = () => {
109
+ if (document.pointerLockElement !== canvas)
110
+ requestLock(canvas);
111
+ };
112
+ const onKeyDown = (event) => {
113
+ if (isTypingTarget(event.target))
114
+ return;
115
+ if (event.code === 'Escape') {
116
+ event.preventDefault();
117
+ event.stopPropagation();
118
+ exit();
119
+ return;
120
+ }
121
+ // P toggles a cursor pause: frees the pointer without leaving the drone —
122
+ // e.g. for an OS screenshot, which needs a movable cursor.
123
+ if (event.code === 'KeyP') {
124
+ event.preventDefault();
125
+ event.stopPropagation();
126
+ if (document.pointerLockElement === canvas) {
127
+ suspendRef.current = true;
128
+ heldKeys.clear();
129
+ useViewer.getState().setWalkthroughSuspended(true);
130
+ document.exitPointerLock();
131
+ }
132
+ else if (suspendRef.current) {
133
+ requestLock(canvas);
134
+ }
135
+ return;
136
+ }
137
+ if (!droneInputForKey(event.code))
138
+ return;
139
+ event.preventDefault();
140
+ event.stopPropagation();
141
+ // Paused, or a system shortcut (⌘⇧4 screenshot): the drone holds still.
142
+ if (suspendRef.current || event.metaKey)
143
+ return;
144
+ heldKeys.add(event.code);
145
+ };
146
+ const onKeyUp = (event) => {
147
+ heldKeys.delete(event.code);
148
+ };
149
+ const onBlur = () => heldKeys.clear();
150
+ const onPointerLockChange = () => {
151
+ if (document.pointerLockElement === canvas) {
152
+ wasLocked = true;
153
+ suspendRef.current = false;
154
+ useViewer.getState().setWalkthroughSuspended(false);
155
+ }
156
+ else if (suspendRef.current) {
157
+ // Deliberately released (P) — keep flying once resumed.
158
+ }
159
+ else if (wasLocked) {
160
+ // Esc while locked: the browser releases the pointer and swallows the key.
161
+ exit();
162
+ }
163
+ };
164
+ document.addEventListener('mousemove', onMouseMove);
165
+ canvas.addEventListener('click', onClick);
166
+ document.addEventListener('keydown', onKeyDown, true);
167
+ document.addEventListener('keyup', onKeyUp, true);
168
+ window.addEventListener('blur', onBlur);
169
+ document.addEventListener('pointerlockchange', onPointerLockChange);
170
+ // The toggle that mounted the drone is a user gesture, so the lock is
171
+ // usually granted straight away; the canvas click covers a refusal.
172
+ if (document.pointerLockElement !== canvas)
173
+ requestLock(canvas);
174
+ return () => {
175
+ document.removeEventListener('mousemove', onMouseMove);
176
+ canvas.removeEventListener('click', onClick);
177
+ document.removeEventListener('keydown', onKeyDown, true);
178
+ document.removeEventListener('keyup', onKeyUp, true);
179
+ window.removeEventListener('blur', onBlur);
180
+ document.removeEventListener('pointerlockchange', onPointerLockChange);
181
+ heldKeys.clear();
182
+ useViewer.getState().setWalkthroughSuspended(false);
183
+ if (document.pointerLockElement === canvas)
184
+ document.exitPointerLock();
185
+ };
186
+ }, [gl]);
187
+ useFrame((_, delta) => {
188
+ if (pausedRef.current)
189
+ return;
190
+ readDroneInput(heldKeysRef.current, inputRef.current);
191
+ stepDroneFlight(camera, lookRef.current, inputRef.current, velocityRef.current, delta);
192
+ }, 2.5);
193
+ return null;
194
+ }
@@ -1,6 +1,7 @@
1
1
  import { type AnyNodeId, type Interactive, type SceneGraph } from '@pascal-app/core';
2
2
  import { type EvaluatedLight, type ProceduralItemNode } from '@pascal-app/core/procedural-items';
3
- import { type AnimationAction, type Object3D, Vector3 } from 'three';
3
+ import { type AnimationAction, type Object3D } from 'three';
4
+ import { type PooledLight } from '../../systems/item-light/pooled-lights';
4
5
  /** An interactive item recovered from the scene graph so the baked GLB can be
5
6
  * re-lit / re-animated by joining on `pascalId`. The GLB carries the geometry
6
7
  * + identity; the effects + controls live in the DB scene graph (no sidecar). */
@@ -21,13 +22,6 @@ export type GlbInteractiveItem = {
21
22
  recipe?: Pick<ProceduralItemNode['recipe'], 'parts' | 'joints'>;
22
23
  };
23
24
  };
24
- /** A baked zone's identity node + its local floor polygon (from `extras`). */
25
- export type GlbZoneRef = {
26
- id: string;
27
- node: Object3D;
28
- polygon: [number, number][];
29
- holes?: [number, number][][];
30
- };
31
25
  /** Pull the interactive items out of a scene graph. Only items that actually
32
26
  * carry effects (light / animation) are returned — everything else baked
33
27
  * faithfully and needs no runtime help. */
@@ -40,24 +34,16 @@ export declare function buildGlbLightRegs(items: GlbInteractiveItem[], identity:
40
34
  * baked Object3Ds (`identity`), joined on `pascalId`. Nothing is stamped into
41
35
  * the GLB itself, so the artifact stays integrator-clean.
42
36
  */
43
- export declare function GlbInteractive({ items, identity, zones, actions, levelOrder, }: {
37
+ export declare function GlbInteractive({ items, identity, actions, }: {
44
38
  items: GlbInteractiveItem[];
45
39
  identity: Map<string, Object3D>;
46
- zones: GlbZoneRef[];
47
40
  /** Baked animation actions keyed by clip name — ambient item loops play from
48
41
  * `<pascalId>: loop`. */
49
42
  actions: Record<string, AnimationAction | null>;
50
- /** Level pascalIds bottom-to-top, so the light pool can prefer ground-floor
51
- * lights when nothing is focused (mirrors the parametric level factor). */
52
- levelOrder: string[];
53
43
  }): import("react").JSX.Element;
54
- export type GlbLightReg = {
55
- key: string;
44
+ export type GlbLightReg = PooledLight & {
56
45
  nodeId: AnyNodeId;
57
46
  object: Object3D;
58
- color: string;
59
- distance: number;
60
- getWorldPosition: (out: Vector3) => void;
61
47
  getIntensity: () => number;
62
48
  isOn: () => boolean;
63
49
  levelId: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"glb-interactive.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/glb-interactive.tsx"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,SAAS,EAEd,KAAK,WAAW,EAGhB,KAAK,UAAU,EAOhB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,KAAK,cAAc,EAEnB,KAAK,kBAAkB,EACxB,MAAM,mCAAmC,CAAA;AAI1C,OAAO,EACL,KAAK,eAAe,EAGpB,KAAK,QAAQ,EAEb,OAAO,EACR,MAAM,OAAO,CAAA;AAcd;;kFAEkF;AAClF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,SAAS,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,WAAW,CAAA;IACxB;mEAC+D;IAC/D,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IACrC,UAAU,CAAC,EAAE;QACX,MAAM,EAAE,cAAc,EAAE,CAAA;QACxB,KAAK,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAA;QAC5C,+EAA+E;QAC/E,MAAM,CAAC,EAAE,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC,CAAA;KAChE,CAAA;CACF,CAAA;AAED,8EAA8E;AAC9E,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,QAAQ,CAAA;IACd,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAA;IAC3B,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,CAAA;CAC7B,CAAA;AAED;;4CAE4C;AAC5C,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,UAAU,GAAG,IAAI,GAAG,SAAS,GACxC,kBAAkB,EAAE,CAiEtB;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,kBAAkB,EAAE,EAC3B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,GAC9B,WAAW,EAAE,CAiFf;AAID;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,QAAQ,EACR,KAAK,EACL,OAAO,EACP,UAAU,GACX,EAAE;IACD,KAAK,EAAE,kBAAkB,EAAE,CAAA;IAC3B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IAC/B,KAAK,EAAE,UAAU,EAAE,CAAA;IACnB;8BAC0B;IAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC,CAAA;IAC/C;gFAC4E;IAC5E,UAAU,EAAE,MAAM,EAAE,CAAA;CACrB,+BA0FA;AA8CD,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,SAAS,CAAA;IACjB,MAAM,EAAE,QAAQ,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,gBAAgB,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;IACxC,YAAY,EAAE,MAAM,MAAM,CAAA;IAC1B,IAAI,EAAE,MAAM,OAAO,CAAA;IACnB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB,CAAA"}
1
+ {"version":3,"file":"glb-interactive.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/glb-interactive.tsx"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,WAAW,EAEhB,KAAK,UAAU,EAMhB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACL,KAAK,cAAc,EAEnB,KAAK,kBAAkB,EACxB,MAAM,mCAAmC,CAAA;AAG1C,OAAO,EACL,KAAK,eAAe,EAGpB,KAAK,QAAQ,EAGd,MAAM,OAAO,CAAA;AAWd,OAAO,EAAE,KAAK,WAAW,EAAgB,MAAM,wCAAwC,CAAA;AAEvF;;kFAEkF;AAClF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,SAAS,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,WAAW,CAAA;IACxB;mEAC+D;IAC/D,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IACrC,UAAU,CAAC,EAAE;QACX,MAAM,EAAE,cAAc,EAAE,CAAA;QACxB,KAAK,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAA;QAC5C,+EAA+E;QAC/E,MAAM,CAAC,EAAE,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC,CAAA;KAChE,CAAA;CACF,CAAA;AAED;;4CAE4C;AAC5C,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,UAAU,GAAG,IAAI,GAAG,SAAS,GACxC,kBAAkB,EAAE,CAiEtB;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,kBAAkB,EAAE,EAC3B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,GAC9B,WAAW,EAAE,CA2Ff;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,QAAQ,EACR,OAAO,GACR,EAAE;IACD,KAAK,EAAE,kBAAkB,EAAE,CAAA;IAC3B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IAC/B;8BAC0B;IAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC,CAAA;CAChD,+BAuDA;AAsCD,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG;IACtC,MAAM,EAAE,SAAS,CAAA;IACjB,MAAM,EAAE,QAAQ,CAAA;IAChB,YAAY,EAAE,MAAM,MAAM,CAAA;IAC1B,IAAI,EAAE,MAAM,OAAO,CAAA;IACnB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CACvB,CAAA"}