@pascal-app/viewer 0.8.0 → 0.9.2

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 (284) hide show
  1. package/dist/components/error-boundary.d.ts +6 -0
  2. package/dist/components/error-boundary.d.ts.map +1 -1
  3. package/dist/components/error-boundary.js +6 -0
  4. package/dist/components/renderers/node-renderer.d.ts +6 -2
  5. package/dist/components/renderers/node-renderer.d.ts.map +1 -1
  6. package/dist/components/renderers/node-renderer.js +41 -22
  7. package/dist/components/renderers/parametric-node-renderer.d.ts +5 -0
  8. package/dist/components/renderers/parametric-node-renderer.d.ts.map +1 -0
  9. package/dist/components/renderers/parametric-node-renderer.js +37 -0
  10. package/dist/components/renderers/scene-renderer.d.ts +1 -1
  11. package/dist/components/renderers/scene-renderer.d.ts.map +1 -1
  12. package/dist/components/viewer/bvh-ecctrl.d.ts +77 -0
  13. package/dist/components/viewer/bvh-ecctrl.d.ts.map +1 -0
  14. package/dist/components/viewer/bvh-ecctrl.js +552 -0
  15. package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
  16. package/dist/components/viewer/frame-limiter.js +39 -4
  17. package/dist/components/viewer/glb-interactive.d.ts +41 -0
  18. package/dist/components/viewer/glb-interactive.d.ts.map +1 -0
  19. package/dist/components/viewer/glb-interactive.js +394 -0
  20. package/dist/components/viewer/glb-reference-nodes.d.ts +29 -0
  21. package/dist/components/viewer/glb-reference-nodes.d.ts.map +1 -0
  22. package/dist/components/viewer/glb-reference-nodes.js +80 -0
  23. package/dist/components/viewer/glb-replace-instances.d.ts +18 -0
  24. package/dist/components/viewer/glb-replace-instances.d.ts.map +1 -0
  25. package/dist/components/viewer/glb-replace-instances.js +61 -0
  26. package/dist/components/viewer/glb-scene.d.ts +54 -0
  27. package/dist/components/viewer/glb-scene.d.ts.map +1 -0
  28. package/dist/components/viewer/glb-scene.js +920 -0
  29. package/dist/components/viewer/glb-walkthrough-controller.d.ts +12 -0
  30. package/dist/components/viewer/glb-walkthrough-controller.d.ts.map +1 -0
  31. package/dist/components/viewer/glb-walkthrough-controller.js +357 -0
  32. package/dist/components/viewer/index.d.ts +56 -3
  33. package/dist/components/viewer/index.d.ts.map +1 -1
  34. package/dist/components/viewer/index.js +280 -65
  35. package/dist/components/viewer/lights.d.ts +1 -1
  36. package/dist/components/viewer/lights.d.ts.map +1 -1
  37. package/dist/components/viewer/lights.js +201 -43
  38. package/dist/components/viewer/perf-monitor.d.ts +1 -1
  39. package/dist/components/viewer/perf-monitor.d.ts.map +1 -1
  40. package/dist/components/viewer/perf-monitor.js +115 -12
  41. package/dist/components/viewer/post-processing.d.ts +7 -1
  42. package/dist/components/viewer/post-processing.d.ts.map +1 -1
  43. package/dist/components/viewer/post-processing.js +395 -63
  44. package/dist/components/viewer/registered-systems.d.ts +12 -0
  45. package/dist/components/viewer/registered-systems.d.ts.map +1 -0
  46. package/dist/components/viewer/registered-systems.js +49 -0
  47. package/dist/components/viewer/scene-bvh.d.ts +18 -0
  48. package/dist/components/viewer/scene-bvh.d.ts.map +1 -0
  49. package/dist/components/viewer/scene-bvh.js +97 -0
  50. package/dist/components/viewer/scene-bvh.test.d.ts +2 -0
  51. package/dist/components/viewer/scene-bvh.test.d.ts.map +1 -0
  52. package/dist/components/viewer/scene-bvh.test.js +13 -0
  53. package/dist/components/viewer/scene-environment.d.ts +2 -0
  54. package/dist/components/viewer/scene-environment.d.ts.map +1 -0
  55. package/dist/components/viewer/scene-environment.js +79 -0
  56. package/dist/components/viewer/selection-manager.d.ts +1 -1
  57. package/dist/components/viewer/selection-manager.d.ts.map +1 -1
  58. package/dist/components/viewer/selection-manager.js +37 -14
  59. package/dist/components/viewer/viewer-camera.d.ts +1 -1
  60. package/dist/components/viewer/viewer-camera.d.ts.map +1 -1
  61. package/dist/components/viewer/walkthrough-controls.d.ts +2 -1
  62. package/dist/components/viewer/walkthrough-controls.d.ts.map +1 -1
  63. package/dist/components/viewer/walkthrough-controls.js +20 -0
  64. package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
  65. package/dist/hooks/use-gltf-ktx2.js +3 -22
  66. package/dist/hooks/use-node-events.d.ts +6 -74
  67. package/dist/hooks/use-node-events.d.ts.map +1 -1
  68. package/dist/hooks/use-node-events.js +28 -8
  69. package/dist/index.d.ts +48 -5
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +82 -4
  72. package/dist/lib/backdrop.d.ts +33 -0
  73. package/dist/lib/backdrop.d.ts.map +1 -0
  74. package/dist/lib/backdrop.js +92 -0
  75. package/dist/lib/box-uv.d.ts +13 -0
  76. package/dist/lib/box-uv.d.ts.map +1 -0
  77. package/dist/lib/box-uv.js +32 -0
  78. package/dist/lib/csg-utils.d.ts +10 -0
  79. package/dist/lib/csg-utils.d.ts.map +1 -0
  80. package/dist/lib/csg-utils.js +93 -0
  81. package/dist/lib/csg-utils.test.d.ts +2 -0
  82. package/dist/lib/csg-utils.test.d.ts.map +1 -0
  83. package/dist/lib/csg-utils.test.js +45 -0
  84. package/dist/lib/drawable-geometry.d.ts +21 -0
  85. package/dist/lib/drawable-geometry.d.ts.map +1 -0
  86. package/dist/lib/drawable-geometry.js +22 -0
  87. package/dist/lib/edge-style.d.ts +4 -0
  88. package/dist/lib/edge-style.d.ts.map +1 -0
  89. package/dist/lib/edge-style.js +31 -0
  90. package/dist/lib/gpu-perf.d.ts +4 -0
  91. package/dist/lib/gpu-perf.d.ts.map +1 -0
  92. package/dist/lib/gpu-perf.js +23 -0
  93. package/dist/lib/ink-edges.d.ts +9 -0
  94. package/dist/lib/ink-edges.d.ts.map +1 -0
  95. package/dist/lib/ink-edges.js +49 -0
  96. package/dist/lib/isolation.d.ts +39 -0
  97. package/dist/lib/isolation.d.ts.map +1 -0
  98. package/dist/lib/isolation.js +103 -0
  99. package/dist/lib/ktx2-loader.d.ts +43 -0
  100. package/dist/lib/ktx2-loader.d.ts.map +1 -0
  101. package/dist/lib/ktx2-loader.js +136 -0
  102. package/dist/lib/layers.d.ts +28 -0
  103. package/dist/lib/layers.d.ts.map +1 -1
  104. package/dist/lib/layers.js +28 -0
  105. package/dist/lib/materials.d.ts +38 -15
  106. package/dist/lib/materials.d.ts.map +1 -1
  107. package/dist/lib/materials.js +364 -72
  108. package/dist/lib/merged-outline-node.d.ts.map +1 -1
  109. package/dist/lib/merged-outline-node.js +7 -0
  110. package/dist/lib/polygon-union.d.ts +4 -0
  111. package/dist/lib/polygon-union.d.ts.map +1 -0
  112. package/dist/lib/polygon-union.js +289 -0
  113. package/dist/lib/polygon-union.test.d.ts +2 -0
  114. package/dist/lib/polygon-union.test.d.ts.map +1 -0
  115. package/dist/lib/polygon-union.test.js +105 -0
  116. package/dist/lib/scene-themes.d.ts +30 -0
  117. package/dist/lib/scene-themes.d.ts.map +1 -0
  118. package/dist/lib/scene-themes.js +202 -0
  119. package/dist/lib/shadow-only.d.ts +4 -0
  120. package/dist/lib/shadow-only.d.ts.map +1 -0
  121. package/dist/lib/shadow-only.js +36 -0
  122. package/dist/lib/tsl-compat.d.ts +12 -0
  123. package/dist/lib/tsl-compat.d.ts.map +1 -0
  124. package/dist/lib/tsl-compat.js +13 -0
  125. package/dist/store/use-viewer.d.ts +92 -12
  126. package/dist/store/use-viewer.d.ts.map +1 -1
  127. package/dist/store/use-viewer.js +201 -6
  128. package/dist/store/use-viewer.test.d.ts +2 -0
  129. package/dist/store/use-viewer.test.d.ts.map +1 -0
  130. package/dist/store/use-viewer.test.js +42 -0
  131. package/dist/systems/ceiling/ceiling-system.d.ts +6 -2
  132. package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
  133. package/dist/systems/ceiling/ceiling-system.js +66 -13
  134. package/dist/systems/door/door-animation.test.d.ts +2 -0
  135. package/dist/systems/door/door-animation.test.d.ts.map +1 -0
  136. package/dist/systems/door/door-animation.test.js +51 -0
  137. package/dist/systems/door/door-floor-alignment.test.d.ts +2 -0
  138. package/dist/systems/door/door-floor-alignment.test.d.ts.map +1 -0
  139. package/dist/systems/door/door-floor-alignment.test.js +50 -0
  140. package/dist/systems/door/door-system.d.ts +24 -0
  141. package/dist/systems/door/door-system.d.ts.map +1 -1
  142. package/dist/systems/door/door-system.js +864 -180
  143. package/dist/systems/elevator/elevator-interaction-system.d.ts +2 -0
  144. package/dist/systems/elevator/elevator-interaction-system.d.ts.map +1 -0
  145. package/dist/systems/elevator/elevator-interaction-system.js +62 -0
  146. package/dist/systems/fence/fence-system.d.ts +20 -0
  147. package/dist/systems/fence/fence-system.d.ts.map +1 -1
  148. package/dist/systems/fence/fence-system.js +229 -57
  149. package/dist/systems/floor-elevation/floor-elevation-system.d.ts +20 -0
  150. package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -0
  151. package/dist/systems/floor-elevation/floor-elevation-system.js +78 -0
  152. package/dist/systems/geometry/geometry-system.d.ts +41 -0
  153. package/dist/systems/geometry/geometry-system.d.ts.map +1 -0
  154. package/dist/systems/geometry/geometry-system.js +348 -0
  155. package/dist/systems/geometry/geometry-system.test.d.ts +2 -0
  156. package/dist/systems/geometry/geometry-system.test.d.ts.map +1 -0
  157. package/dist/systems/geometry/geometry-system.test.js +30 -0
  158. package/dist/systems/interactive/control-widget.d.ts +10 -0
  159. package/dist/systems/interactive/control-widget.d.ts.map +1 -0
  160. package/dist/systems/interactive/control-widget.js +35 -0
  161. package/dist/systems/interactive/interactive-system.d.ts +1 -1
  162. package/dist/systems/interactive/interactive-system.d.ts.map +1 -1
  163. package/dist/systems/interactive/interactive-system.js +55 -56
  164. package/dist/systems/item/item-system.d.ts +7 -0
  165. package/dist/systems/item/item-system.d.ts.map +1 -1
  166. package/dist/systems/item/item-system.js +28 -17
  167. package/dist/systems/item-light/item-light-system.d.ts +1 -1
  168. package/dist/systems/item-light/item-light-system.d.ts.map +1 -1
  169. package/dist/systems/item-light/item-light-system.js +18 -5
  170. package/dist/systems/level/level-system.d.ts.map +1 -1
  171. package/dist/systems/level/level-system.js +28 -4
  172. package/dist/systems/level/level-utils.d.ts +0 -3
  173. package/dist/systems/level/level-utils.d.ts.map +1 -1
  174. package/dist/systems/level/level-utils.js +2 -41
  175. package/dist/systems/roof/roof-materials.d.ts +3 -2
  176. package/dist/systems/roof/roof-materials.d.ts.map +1 -1
  177. package/dist/systems/roof/roof-materials.js +59 -14
  178. package/dist/systems/roof/roof-system.d.ts +46 -6
  179. package/dist/systems/roof/roof-system.d.ts.map +1 -1
  180. package/dist/systems/roof/roof-system.js +1365 -170
  181. package/dist/systems/slab/slab-system.d.ts +5 -3
  182. package/dist/systems/slab/slab-system.d.ts.map +1 -1
  183. package/dist/systems/slab/slab-system.js +204 -68
  184. package/dist/systems/slab/slab-system.test.d.ts +2 -0
  185. package/dist/systems/slab/slab-system.test.d.ts.map +1 -0
  186. package/dist/systems/slab/slab-system.test.js +63 -0
  187. package/dist/systems/stair/stair-materials.d.ts +4 -3
  188. package/dist/systems/stair/stair-materials.d.ts.map +1 -1
  189. package/dist/systems/stair/stair-materials.js +30 -15
  190. package/dist/systems/stair/stair-system.d.ts.map +1 -1
  191. package/dist/systems/stair/stair-system.js +55 -63
  192. package/dist/systems/surface-hole-geometry.d.ts +3 -0
  193. package/dist/systems/surface-hole-geometry.d.ts.map +1 -0
  194. package/dist/systems/surface-hole-geometry.js +4 -0
  195. package/dist/systems/wall/opening-cutout-geometry.d.ts +31 -0
  196. package/dist/systems/wall/opening-cutout-geometry.d.ts.map +1 -0
  197. package/dist/systems/wall/opening-cutout-geometry.js +167 -0
  198. package/dist/systems/wall/opening-cutout-geometry.test.d.ts +2 -0
  199. package/dist/systems/wall/opening-cutout-geometry.test.d.ts.map +1 -0
  200. package/dist/systems/wall/opening-cutout-geometry.test.js +172 -0
  201. package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
  202. package/dist/systems/wall/wall-cutout.js +62 -17
  203. package/dist/systems/wall/wall-materials.d.ts +12 -7
  204. package/dist/systems/wall/wall-materials.d.ts.map +1 -1
  205. package/dist/systems/wall/wall-materials.js +279 -46
  206. package/dist/systems/wall/wall-opening-cutout.test.d.ts +2 -0
  207. package/dist/systems/wall/wall-opening-cutout.test.d.ts.map +1 -0
  208. package/dist/systems/wall/wall-opening-cutout.test.js +59 -0
  209. package/dist/systems/wall/wall-support-extension.test.d.ts +2 -0
  210. package/dist/systems/wall/wall-support-extension.test.d.ts.map +1 -0
  211. package/dist/systems/wall/wall-support-extension.test.js +48 -0
  212. package/dist/systems/wall/wall-system.d.ts +2 -2
  213. package/dist/systems/wall/wall-system.d.ts.map +1 -1
  214. package/dist/systems/wall/wall-system.js +493 -193
  215. package/dist/systems/window/window-animation-system.d.ts +12 -0
  216. package/dist/systems/window/window-animation-system.d.ts.map +1 -1
  217. package/dist/systems/window/window-animation-system.js +16 -5
  218. package/dist/systems/window/window-system.d.ts +7 -0
  219. package/dist/systems/window/window-system.d.ts.map +1 -1
  220. package/dist/systems/window/window-system.js +308 -18
  221. package/package.json +5 -6
  222. package/dist/components/renderers/building/building-renderer.d.ts +0 -5
  223. package/dist/components/renderers/building/building-renderer.d.ts.map +0 -1
  224. package/dist/components/renderers/building/building-renderer.js +0 -11
  225. package/dist/components/renderers/ceiling/ceiling-renderer.d.ts +0 -5
  226. package/dist/components/renderers/ceiling/ceiling-renderer.d.ts.map +0 -1
  227. package/dist/components/renderers/ceiling/ceiling-renderer.js +0 -58
  228. package/dist/components/renderers/column/column-renderer.d.ts +0 -5
  229. package/dist/components/renderers/column/column-renderer.d.ts.map +0 -1
  230. package/dist/components/renderers/column/column-renderer.js +0 -597
  231. package/dist/components/renderers/door/door-renderer.d.ts +0 -5
  232. package/dist/components/renderers/door/door-renderer.d.ts.map +0 -1
  233. package/dist/components/renderers/door/door-renderer.js +0 -16
  234. package/dist/components/renderers/fence/fence-renderer.d.ts +0 -5
  235. package/dist/components/renderers/fence/fence-renderer.d.ts.map +0 -1
  236. package/dist/components/renderers/fence/fence-renderer.js +0 -15
  237. package/dist/components/renderers/guide/guide-renderer.d.ts +0 -5
  238. package/dist/components/renderers/guide/guide-renderer.d.ts.map +0 -1
  239. package/dist/components/renderers/guide/guide-renderer.js +0 -38
  240. package/dist/components/renderers/item/item-renderer.d.ts +0 -5
  241. package/dist/components/renderers/item/item-renderer.d.ts.map +0 -1
  242. package/dist/components/renderers/item/item-renderer.js +0 -172
  243. package/dist/components/renderers/level/level-renderer.d.ts +0 -5
  244. package/dist/components/renderers/level/level-renderer.d.ts.map +0 -1
  245. package/dist/components/renderers/level/level-renderer.js +0 -11
  246. package/dist/components/renderers/roof/roof-materials.d.ts +0 -4
  247. package/dist/components/renderers/roof/roof-materials.d.ts.map +0 -1
  248. package/dist/components/renderers/roof/roof-materials.js +0 -16
  249. package/dist/components/renderers/roof/roof-renderer.d.ts +0 -5
  250. package/dist/components/renderers/roof/roof-renderer.d.ts.map +0 -1
  251. package/dist/components/renderers/roof/roof-renderer.js +0 -32
  252. package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts +0 -5
  253. package/dist/components/renderers/roof-segment/roof-segment-renderer.d.ts.map +0 -1
  254. package/dist/components/renderers/roof-segment/roof-segment-renderer.js +0 -40
  255. package/dist/components/renderers/scan/scan-renderer.d.ts +0 -5
  256. package/dist/components/renderers/scan/scan-renderer.d.ts.map +0 -1
  257. package/dist/components/renderers/scan/scan-renderer.js +0 -54
  258. package/dist/components/renderers/site/site-renderer.d.ts +0 -5
  259. package/dist/components/renderers/site/site-renderer.d.ts.map +0 -1
  260. package/dist/components/renderers/site/site-renderer.js +0 -106
  261. package/dist/components/renderers/slab/slab-renderer.d.ts +0 -5
  262. package/dist/components/renderers/slab/slab-renderer.d.ts.map +0 -1
  263. package/dist/components/renderers/slab/slab-renderer.js +0 -58
  264. package/dist/components/renderers/spawn/spawn-renderer.d.ts +0 -5
  265. package/dist/components/renderers/spawn/spawn-renderer.d.ts.map +0 -1
  266. package/dist/components/renderers/spawn/spawn-renderer.js +0 -32
  267. package/dist/components/renderers/stair/stair-renderer.d.ts +0 -5
  268. package/dist/components/renderers/stair/stair-renderer.d.ts.map +0 -1
  269. package/dist/components/renderers/stair/stair-renderer.js +0 -603
  270. package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts +0 -5
  271. package/dist/components/renderers/stair-segment/stair-segment-renderer.d.ts.map +0 -1
  272. package/dist/components/renderers/stair-segment/stair-segment-renderer.js +0 -51
  273. package/dist/components/renderers/wall/wall-renderer.d.ts +0 -5
  274. package/dist/components/renderers/wall/wall-renderer.d.ts.map +0 -1
  275. package/dist/components/renderers/wall/wall-renderer.js +0 -16
  276. package/dist/components/renderers/window/window-renderer.d.ts +0 -5
  277. package/dist/components/renderers/window/window-renderer.d.ts.map +0 -1
  278. package/dist/components/renderers/window/window-renderer.js +0 -21
  279. package/dist/components/renderers/zone/zone-renderer.d.ts +0 -5
  280. package/dist/components/renderers/zone/zone-renderer.d.ts.map +0 -1
  281. package/dist/components/renderers/zone/zone-renderer.js +0 -181
  282. package/dist/components/viewer/ground-occluder.d.ts +0 -2
  283. package/dist/components/viewer/ground-occluder.d.ts.map +0 -1
  284. package/dist/components/viewer/ground-occluder.js +0 -79
@@ -0,0 +1,12 @@
1
+ /**
2
+ * First-person walkthrough controller for the baked GLB. Reuses the editor's
3
+ * `BVHEcctrl` capsule character controller (gravity, jump, sprint, ground-float,
4
+ * mesh collision) fed a collider built from the artifact's own geometry — so the
5
+ * baked viewer walks the building with the same physics as the editor, without
6
+ * the parametric scene. Pointer-lock drives look; WASD moves; Space jumps; Shift
7
+ * sprints. Door/window interaction stays in `GlbScene` (its centre-ray HUD).
8
+ */
9
+ export declare function GlbWalkthroughController({ url }: {
10
+ url: string;
11
+ }): import("react").JSX.Element | null;
12
+ //# sourceMappingURL=glb-walkthrough-controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glb-walkthrough-controller.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/glb-walkthrough-controller.tsx"],"names":[],"mappings":"AAgOA;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,EAAE,GAAG,EAAE,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,sCAyLhE"}
@@ -0,0 +1,357 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { KeyboardControls } from '@react-three/drei';
4
+ import { useFrame, useThree } from '@react-three/fiber';
5
+ import { useCallback, useEffect, useRef, useState } from 'react';
6
+ import { Box3, BoxGeometry, BufferGeometry, Euler, Float32BufferAttribute, Matrix4, Mesh, MeshBasicMaterial, Quaternion, Vector3, } from 'three';
7
+ import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
8
+ import { acceleratedRaycast, computeBoundsTree, disposeBoundsTree } from 'three-mesh-bvh';
9
+ import { useGLTFKTX2 } from '../../hooks/use-gltf-ktx2';
10
+ import { SCENE_LAYER } from '../../lib/layers';
11
+ import useViewer from '../../store/use-viewer';
12
+ import BVHEcctrl, {} from './bvh-ecctrl';
13
+ import { WALKTHROUGH_FOV } from './walkthrough-controls';
14
+ // Eye/capsule geometry mirrors the editor's first-person controller so the
15
+ // baked walkthrough feels identical. The capsule centre sits below the eye; the
16
+ // camera rides the capsule with a small offset and the controller floats it to
17
+ // the ground.
18
+ const CAMERA_EYE_OFFSET = 0.45;
19
+ const CONTROLLER_CENTER_FROM_EYE = 0.85;
20
+ const SPAWN_EYE_HEIGHT = 1.65;
21
+ const LOOK_SENSITIVITY = 0.002;
22
+ const VOID_FALL_RESPAWN_DEPTH = 12;
23
+ // Kinds that must not block the player: room helpers, the spawn marker, the
24
+ // ceiling/roof shell (you walk under them), and door/window leaves — excluding
25
+ // the latter lets you pass any doorway whether the leaf is open or shut (the
26
+ // wall already has the opening cut into its baked geometry).
27
+ const COLLIDER_EXCLUDED_KINDS = new Set(['zone', 'spawn', 'ceiling', 'roof', 'door', 'window']);
28
+ const colliderMaterial = new MeshBasicMaterial({ visible: false });
29
+ const keyboardMap = [
30
+ { name: 'forward', keys: ['ArrowUp', 'KeyW'] },
31
+ { name: 'backward', keys: ['ArrowDown', 'KeyS'] },
32
+ { name: 'leftward', keys: ['ArrowLeft', 'KeyA'] },
33
+ { name: 'rightward', keys: ['ArrowRight', 'KeyD'] },
34
+ { name: 'jump', keys: ['Space'] },
35
+ { name: 'run', keys: ['ShiftLeft', 'ShiftRight'] },
36
+ ];
37
+ const cameraOffset = new Vector3(0, CAMERA_EYE_OFFSET, 0);
38
+ const cameraEuler = new Euler(0, 0, 0, 'YXZ');
39
+ const spawnQuat = new Quaternion();
40
+ const spawnEuler = new Euler(0, 0, 0, 'YXZ');
41
+ const spawnPos = new Vector3();
42
+ /** Effective visibility — an invisible ancestor hides the whole subtree. */
43
+ function isEffectivelyVisible(object) {
44
+ let current = object;
45
+ while (current) {
46
+ if (!current.visible)
47
+ return false;
48
+ current = current.parent;
49
+ }
50
+ return true;
51
+ }
52
+ function kindOf(object) {
53
+ let current = object;
54
+ while (current) {
55
+ const kind = current.userData.kind;
56
+ if (kind)
57
+ return kind;
58
+ current = current.parent;
59
+ }
60
+ return undefined;
61
+ }
62
+ // Coerce any position attribute (quantized/interleaved) to a plain Float32 one
63
+ // so mergeGeometries can combine geometries that don't share an array type.
64
+ function toFloat32Position(source) {
65
+ const array = new Float32Array(source.count * 3);
66
+ for (let i = 0; i < source.count; i++) {
67
+ array[i * 3] = source.getX(i);
68
+ array[i * 3 + 1] = source.getY(i);
69
+ array[i * 3 + 2] = source.getZ(i);
70
+ }
71
+ return new Float32BufferAttribute(array, 3);
72
+ }
73
+ const FALLBACK_THICKNESS = 0.08;
74
+ const GROUND_MIN = 2000;
75
+ /** A thin position-only floor box whose top face sits at `topY`. */
76
+ function boxFloorGeometry(cx, topY, cz, width, depth) {
77
+ const box = new BoxGeometry(width, FALLBACK_THICKNESS, depth).toNonIndexed();
78
+ const geometry = new BufferGeometry();
79
+ geometry.setAttribute('position', box.getAttribute('position').clone());
80
+ box.dispose();
81
+ geometry.applyMatrix4(new Matrix4().makeTranslation(cx, topY - FALLBACK_THICKNESS / 2, cz));
82
+ return geometry;
83
+ }
84
+ // Fallback ground so the player never falls into the void: a single large
85
+ // ground plane at the lowest level (level 0). Upper levels rely on their own
86
+ // baked slabs — a slab-less upper floor lets you fall down to the ground, which
87
+ // the ground plane catches. (Mirrors the editor walkthrough's site ground.)
88
+ function addFallbackFloors(scene, geometries) {
89
+ const sceneBounds = new Box3();
90
+ for (const geometry of geometries) {
91
+ geometry.computeBoundingBox();
92
+ if (geometry.boundingBox)
93
+ sceneBounds.union(geometry.boundingBox);
94
+ }
95
+ const center = new Vector3();
96
+ const levelPos = new Vector3();
97
+ let lowestLevelY = Number.POSITIVE_INFINITY;
98
+ scene.traverse((object) => {
99
+ if (object.userData.kind !== 'level')
100
+ return;
101
+ object.updateWorldMatrix(true, false);
102
+ object.getWorldPosition(levelPos);
103
+ lowestLevelY = Math.min(lowestLevelY, levelPos.y);
104
+ });
105
+ if (sceneBounds.isEmpty())
106
+ return;
107
+ sceneBounds.getCenter(center);
108
+ const groundY = Number.isFinite(lowestLevelY) ? lowestLevelY : sceneBounds.min.y;
109
+ geometries.push(boxFloorGeometry(center.x, groundY, center.z, GROUND_MIN, GROUND_MIN));
110
+ }
111
+ /** Merge the baked GLB's walkable/blocking meshes into one BVH collider. */
112
+ function buildGlbColliderWorld(scene) {
113
+ scene.updateWorldMatrix(true, true);
114
+ const geometries = [];
115
+ scene.traverse((object) => {
116
+ const mesh = object;
117
+ if (!mesh.isMesh)
118
+ return;
119
+ // Zone fills live on a separate layer (and never collide).
120
+ if (!mesh.layers.isEnabled(SCENE_LAYER))
121
+ return;
122
+ if (!isEffectivelyVisible(mesh))
123
+ return;
124
+ const kind = kindOf(mesh);
125
+ if (kind && COLLIDER_EXCLUDED_KINDS.has(kind))
126
+ return;
127
+ const position = mesh.geometry?.getAttribute('position');
128
+ if (!position || position.count < 3)
129
+ return;
130
+ const geometry = new BufferGeometry();
131
+ const source = mesh.geometry.index ? mesh.geometry.toNonIndexed() : mesh.geometry;
132
+ geometry.setAttribute('position', toFloat32Position(source.getAttribute('position')));
133
+ if (mesh.geometry.index)
134
+ source.dispose();
135
+ geometry.applyMatrix4(mesh.matrixWorld);
136
+ geometries.push(geometry);
137
+ });
138
+ if (geometries.length === 0)
139
+ return null;
140
+ addFallbackFloors(scene, geometries);
141
+ const merged = mergeGeometries(geometries, false);
142
+ for (const geometry of geometries)
143
+ geometry.dispose();
144
+ if (!merged || merged.getAttribute('position') == null) {
145
+ merged?.dispose();
146
+ return null;
147
+ }
148
+ ;
149
+ merged.computeBoundsTree = computeBoundsTree;
150
+ merged.disposeBoundsTree = disposeBoundsTree;
151
+ merged.computeBoundsTree({ maxLeafSize: 12, strategy: 0 });
152
+ merged.computeBoundingBox();
153
+ const mesh = new Mesh(merged, colliderMaterial);
154
+ mesh.raycast = acceleratedRaycast;
155
+ mesh.visible = true;
156
+ mesh.userData = {
157
+ type: 'STATIC',
158
+ friction: 0.8,
159
+ restitution: 0.05,
160
+ excludeFloatHit: false,
161
+ excludeCollisionCheck: false,
162
+ };
163
+ mesh.updateMatrixWorld(true);
164
+ return {
165
+ mesh,
166
+ minY: merged.boundingBox?.min.y ?? 0,
167
+ dispose: () => {
168
+ ;
169
+ merged.disposeBoundsTree?.();
170
+ merged.dispose();
171
+ },
172
+ };
173
+ }
174
+ /** The baked spawn marker's eye position + yaw, if the artifact carries one. */
175
+ function resolveGlbSpawn(scene) {
176
+ let spawn = null;
177
+ scene.traverse((object) => {
178
+ if (object.userData.kind === 'spawn')
179
+ spawn = object;
180
+ });
181
+ if (!spawn)
182
+ return null;
183
+ const node = spawn;
184
+ node.updateWorldMatrix(true, false);
185
+ node.getWorldPosition(spawnPos);
186
+ node.getWorldQuaternion(spawnQuat);
187
+ spawnEuler.setFromQuaternion(spawnQuat, 'YXZ');
188
+ return {
189
+ position: [spawnPos.x, spawnPos.y + SPAWN_EYE_HEIGHT, spawnPos.z],
190
+ yaw: spawnEuler.y,
191
+ };
192
+ }
193
+ /**
194
+ * First-person walkthrough controller for the baked GLB. Reuses the editor's
195
+ * `BVHEcctrl` capsule character controller (gravity, jump, sprint, ground-float,
196
+ * mesh collision) fed a collider built from the artifact's own geometry — so the
197
+ * baked viewer walks the building with the same physics as the editor, without
198
+ * the parametric scene. Pointer-lock drives look; WASD moves; Space jumps; Shift
199
+ * sprints. Door/window interaction stays in `GlbScene` (its centre-ray HUD).
200
+ */
201
+ export function GlbWalkthroughController({ url }) {
202
+ const { camera, gl } = useThree();
203
+ const gltf = useGLTFKTX2(url);
204
+ const worldRef = useRef(null);
205
+ const controllerRef = useRef(null);
206
+ const yawRef = useRef(0);
207
+ const pitchRef = useRef(0);
208
+ const [start, setStart] = useState(null);
209
+ const [world, setWorld] = useState(null);
210
+ // Build the collider on the first frame (priority after GlbScene's level loop,
211
+ // which snaps the floors to their stacked world positions in walkthrough) so it
212
+ // matches the rendered building rather than a mid-lerp / exploded layout.
213
+ const builtRef = useRef(false);
214
+ useFrame(() => {
215
+ if (builtRef.current)
216
+ return;
217
+ builtRef.current = true;
218
+ setWorld(buildGlbColliderWorld(gltf.scene));
219
+ }, 6);
220
+ // First-person needs a perspective camera — an orthographic projection has no
221
+ // foreshortening and makes the walkthrough unusable. Force perspective while
222
+ // walking and restore the prior projection on exit.
223
+ useEffect(() => {
224
+ const prevMode = useViewer.getState().cameraMode;
225
+ if (prevMode === 'orthographic')
226
+ useViewer.getState().setCameraMode('perspective');
227
+ return () => {
228
+ if (prevMode === 'orthographic')
229
+ useViewer.getState().setCameraMode('orthographic');
230
+ };
231
+ }, []);
232
+ // Widen FOV while walking; the baked walkthrough rides the default 50° orbit
233
+ // camera, which feels cramped on foot. Keyed on `camera` so it re-applies if
234
+ // the instance swaps (e.g. the ortho→perspective switch above), restoring the
235
+ // prior FOV on exit.
236
+ useEffect(() => {
237
+ const cam = camera;
238
+ if (!cam.isPerspectiveCamera)
239
+ return;
240
+ const prevFov = cam.fov;
241
+ cam.fov = WALKTHROUGH_FOV;
242
+ cam.updateProjectionMatrix();
243
+ return () => {
244
+ cam.fov = prevFov;
245
+ cam.updateProjectionMatrix();
246
+ };
247
+ }, [camera]);
248
+ useEffect(() => {
249
+ worldRef.current = world;
250
+ if (world) {
251
+ const triangles = world.mesh.geometry.getAttribute('position').count / 3;
252
+ console.warn('[glb-walkthrough] collider built', {
253
+ triangles,
254
+ minY: world.minY,
255
+ hasBoundsTree: !!world.mesh.geometry.boundsTree,
256
+ spawn: resolveGlbSpawn(gltf.scene),
257
+ });
258
+ }
259
+ else {
260
+ console.warn('[glb-walkthrough] NO collider world (no eligible meshes)');
261
+ }
262
+ return () => {
263
+ world?.dispose();
264
+ worldRef.current = null;
265
+ };
266
+ }, [world, gltf.scene]);
267
+ // Resolve the spawn once the collider exists; the capsule centre sits below
268
+ // the eye, then the controller floats it onto the ground.
269
+ useEffect(() => {
270
+ if (!world || start)
271
+ return;
272
+ const spawn = resolveGlbSpawn(gltf.scene);
273
+ const eye = spawn?.position ?? [0, SPAWN_EYE_HEIGHT, 0];
274
+ yawRef.current = spawn?.yaw ?? 0;
275
+ pitchRef.current = 0;
276
+ setStart({ position: [eye[0], eye[1] - CONTROLLER_CENTER_FROM_EYE, eye[2]] });
277
+ }, [world, start, gltf.scene]);
278
+ // Pointer-lock look + click-to-lock fallback + Esc/unlock to exit. Once the
279
+ // pointer has been locked, releasing it (Esc — the browser swallows that
280
+ // keydown, so we can't rely on it; or any other unlock) leaves the walkthrough
281
+ // in a single press rather than just freeing the cursor.
282
+ useEffect(() => {
283
+ const canvas = gl.domElement;
284
+ let wasLocked = false;
285
+ const onMouseMove = (event) => {
286
+ if (document.pointerLockElement !== canvas)
287
+ return;
288
+ yawRef.current -= event.movementX * LOOK_SENSITIVITY;
289
+ pitchRef.current = Math.max(-(Math.PI / 2 - 0.05), Math.min(Math.PI / 2 - 0.05, pitchRef.current - event.movementY * LOOK_SENSITIVITY));
290
+ };
291
+ const onClick = () => {
292
+ if (document.pointerLockElement !== canvas)
293
+ canvas.requestPointerLock?.();
294
+ };
295
+ const onKeyDown = (event) => {
296
+ // When locked, the browser intercepts Esc to release the pointer and the
297
+ // pointerlockchange handler below exits; this only covers Esc while the
298
+ // pointer is already free (e.g. lock never engaged).
299
+ if (event.code === 'Escape' && document.pointerLockElement !== canvas) {
300
+ useViewer.getState().setWalkthroughMode(false);
301
+ }
302
+ };
303
+ const onPointerLockChange = () => {
304
+ if (document.pointerLockElement === canvas)
305
+ wasLocked = true;
306
+ else if (wasLocked)
307
+ useViewer.getState().setWalkthroughMode(false);
308
+ };
309
+ document.addEventListener('mousemove', onMouseMove);
310
+ canvas.addEventListener('click', onClick);
311
+ document.addEventListener('keydown', onKeyDown);
312
+ document.addEventListener('pointerlockchange', onPointerLockChange);
313
+ return () => {
314
+ document.removeEventListener('mousemove', onMouseMove);
315
+ canvas.removeEventListener('click', onClick);
316
+ document.removeEventListener('keydown', onKeyDown);
317
+ document.removeEventListener('pointerlockchange', onPointerLockChange);
318
+ if (document.pointerLockElement === canvas)
319
+ document.exitPointerLock();
320
+ };
321
+ }, [gl]);
322
+ // Lock the pointer the moment the walkthrough is ready, so the user doesn't
323
+ // have to click the canvas first. The walkthrough toggle is itself a user
324
+ // gesture; if the browser still rejects the request (no transient activation
325
+ // left), the click-to-lock fallback above covers it.
326
+ useEffect(() => {
327
+ if (!(world && start))
328
+ return;
329
+ const canvas = gl.domElement;
330
+ if (document.pointerLockElement === canvas)
331
+ return;
332
+ const result = canvas.requestPointerLock?.();
333
+ if (result && typeof result.catch === 'function')
334
+ result.catch(() => { });
335
+ }, [gl, world, start]);
336
+ const setControllerApi = useCallback((api) => {
337
+ controllerRef.current = api;
338
+ }, []);
339
+ // Drive the camera from the capsule each frame + respawn if it falls into void.
340
+ useFrame(() => {
341
+ const group = controllerRef.current?.group;
342
+ if (!group)
343
+ return;
344
+ if (start && world && group.position.y < world.minY - VOID_FALL_RESPAWN_DEPTH) {
345
+ group.position.set(start.position[0], start.position[1], start.position[2]);
346
+ controllerRef.current?.resetLinVel();
347
+ }
348
+ group.rotation.y = 0;
349
+ camera.position.copy(group.position).add(cameraOffset);
350
+ cameraEuler.set(pitchRef.current, yawRef.current, 0, 'YXZ');
351
+ camera.quaternion.setFromEuler(cameraEuler);
352
+ camera.updateMatrixWorld(true);
353
+ }, 2.5);
354
+ if (!(world && start))
355
+ return null;
356
+ return (_jsx(KeyboardControls, { map: keyboardMap, children: _jsx(BVHEcctrl, { acceleration: 26, airDragFactor: 0.3, colliderCapsuleArgs: [0.25, 0.8, 4, 8], colliderMeshes: [world.mesh], collisionCheckIteration: 3, collisionPushBackDamping: 0.1, collisionPushBackThreshold: 0.001, deceleration: 30, delay: 0, fallGravityFactor: 4, floatCheckType: "BOTH", floatDampingC: 36, floatHeight: 0.5, floatPullBackHeight: 0.35, floatSensorRadius: 0.15, floatSpringK: 1200, gravity: 9.81, jumpVel: 5, maxRunSpeed: 5, maxSlope: 1.2, maxWalkSpeed: 2, position: start.position, ref: setControllerApi }) }));
357
+ }
@@ -1,8 +1,12 @@
1
- import { type ThreeToJSXElements } from '@react-three/fiber';
1
+ import { type AnyNodeId } from '@pascal-app/core';
2
+ import { type ThreeElement } from '@react-three/fiber';
2
3
  import * as THREE from 'three/webgpu';
4
+ import type { ColorPreset, RenderShading } from '../../lib/materials';
5
+ import { type RenderContext } from '../../store/use-viewer';
3
6
  import { type HoverStyles } from './post-processing';
4
7
  declare module '@react-three/fiber' {
5
- interface ThreeElements extends ThreeToJSXElements<typeof THREE> {
8
+ interface ThreeElements {
9
+ lineBasicNodeMaterial: ThreeElement<typeof THREE.LineBasicNodeMaterial>;
6
10
  }
7
11
  }
8
12
  interface ViewerProps {
@@ -11,7 +15,56 @@ interface ViewerProps {
11
15
  selectionManager?: 'default' | 'custom';
12
16
  perf?: boolean;
13
17
  useBvh?: boolean;
18
+ renderContext?: RenderContext;
19
+ transparent?: boolean;
20
+ defaultRender?: {
21
+ shading?: RenderShading;
22
+ textures?: boolean;
23
+ colorPreset?: ColorPreset;
24
+ };
25
+ /**
26
+ * Visibility filter on the live canvas. When non-null, every registered
27
+ * node group whose id is not in `isolate` (or in the isolated set's
28
+ * ancestor / descendant closure) is hidden. Pass `null` (or omit) to
29
+ * clear. Powers the unified preset-capture flow (community modal sets
30
+ * this to the subtree it wants to thumbnail) and is the building block
31
+ * for a future focus-mode UX.
32
+ */
33
+ isolate?: AnyNodeId[] | null;
34
+ /**
35
+ * Host-controlled key for scene readiness. Change it whenever a new scene
36
+ * graph is being loaded; the viewer will report not-ready until the graph is
37
+ * mounted, build systems have had a frame to settle, and one rendered frame
38
+ * has presented the new content.
39
+ */
40
+ sceneReadyKey?: string | number | null;
41
+ onSceneReadyChange?: (ready: boolean) => void;
42
+ /**
43
+ * Wall-clock give-up cap for scene readiness, replacing the default
44
+ * frame-count cap. Set it on hosts whose frame cadence is decoupled from
45
+ * real time (the headless bake page's timer-driven loop runs the default
46
+ * 180-frame cap in ~3.6s — shorter than a cold item-model download).
47
+ */
48
+ sceneReadyMaxWaitMs?: number;
49
+ /**
50
+ * Skip the TSL post-processing pipeline (SSGI/denoise/ink/outline) and render
51
+ * the scene directly. For headless/capture surfaces (the bake page) where
52
+ * frame quality is irrelevant: on a software-rasterised worker the pipeline
53
+ * consumes the whole CPU budget and bakes time out. Equivalent to the
54
+ * `?disable=postFx` diagnostic URL flag, but host-controlled.
55
+ */
56
+ disablePostFx?: boolean;
14
57
  }
15
- declare const Viewer: React.FC<ViewerProps>;
58
+ /** Imperative handle exposed via `ref` on `<Viewer>`. */
59
+ export type ViewerHandle = {
60
+ /**
61
+ * Apply / clear the same visibility filter as the `isolate` prop. Useful
62
+ * for transient cases (a temporary hover-to-isolate UX) where holding
63
+ * the value in React state would be over-engineering. Passing `null`
64
+ * clears.
65
+ */
66
+ setIsolated(ids: AnyNodeId[] | null): void;
67
+ };
68
+ declare const Viewer: import("react").ForwardRefExoticComponent<ViewerProps & import("react").RefAttributes<ViewerHandle>>;
16
69
  export default Viewer;
17
70
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAkB,KAAK,kBAAkB,EAAsB,MAAM,oBAAoB,CAAA;AAEhG,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAwBrC,OAAuB,EAAwB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAA;AA+B1F,OAAO,QAAQ,oBAAoB,CAAC;IAClC,UAAU,aAAc,SAAQ,kBAAkB,CAAC,OAAO,KAAK,CAAC;KAAG;CACpE;AAkFD,UAAU,WAAW;IACnB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC1B,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,gBAAgB,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;IACvC,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,QAAA,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CAwGjC,CAAA;AASD,eAAe,MAAM,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/index.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,SAAS,EAKf,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAkB,KAAK,YAAY,EAAsB,MAAM,oBAAoB,CAAA;AAS1F,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAKrC,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAErE,OAAkB,EAAE,KAAK,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAQtE,OAAuB,EAAwB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAM1F,OAAO,QAAQ,oBAAoB,CAAC;IAOlC,UAAU,aAAa;QACrB,qBAAqB,EAAE,YAAY,CAAC,OAAO,KAAK,CAAC,qBAAqB,CAAC,CAAA;KACxE;CACF;AAuSD,UAAU,WAAW;IACnB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC1B,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,gBAAgB,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;IACvC,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,aAAa,CAAC,EAAE,aAAa,CAAA;IAC7B,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,aAAa,CAAC,EAAE;QACd,OAAO,CAAC,EAAE,aAAa,CAAA;QACvB,QAAQ,CAAC,EAAE,OAAO,CAAA;QAClB,WAAW,CAAC,EAAE,WAAW,CAAA;KAC1B,CAAA;IACD;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,CAAA;IAC5B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IACtC,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IAC7C;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,yDAAyD;AACzD,MAAM,MAAM,YAAY,GAAG;IACzB;;;;;OAKG;IACH,WAAW,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,IAAI,GAAG,IAAI,CAAA;CAC3C,CAAA;AAED,QAAA,MAAM,MAAM,sGAoNV,CAAA;AAkBF,eAAe,MAAM,CAAA"}