@pascal-app/viewer 0.9.2 → 1.0.0-beta.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 (79) hide show
  1. package/README.md +20 -5
  2. package/dist/components/viewer/frame-limiter.d.ts.map +1 -1
  3. package/dist/components/viewer/frame-limiter.js +25 -5
  4. package/dist/components/viewer/glb-scene.js +2 -2
  5. package/dist/components/viewer/glb-walkthrough-controller.d.ts +10 -0
  6. package/dist/components/viewer/glb-walkthrough-controller.d.ts.map +1 -1
  7. package/dist/components/viewer/glb-walkthrough-controller.js +96 -8
  8. package/dist/components/viewer/index.d.ts.map +1 -1
  9. package/dist/components/viewer/index.js +9 -0
  10. package/dist/components/viewer/post-processing.d.ts.map +1 -1
  11. package/dist/components/viewer/post-processing.js +1 -0
  12. package/dist/components/viewer/selection-manager.js +5 -2
  13. package/dist/hooks/use-gltf-ktx2.d.ts.map +1 -1
  14. package/dist/hooks/use-gltf-ktx2.js +2 -5
  15. package/dist/index.d.ts +8 -5
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +10 -8
  18. package/dist/lib/hero-pose.d.ts +47 -0
  19. package/dist/lib/hero-pose.d.ts.map +1 -0
  20. package/dist/lib/hero-pose.js +227 -0
  21. package/dist/lib/ktx2-loader.d.ts +3 -0
  22. package/dist/lib/ktx2-loader.d.ts.map +1 -1
  23. package/dist/lib/ktx2-loader.js +6 -0
  24. package/dist/lib/materials.d.ts +2 -0
  25. package/dist/lib/materials.d.ts.map +1 -1
  26. package/dist/lib/materials.js +56 -9
  27. package/dist/lib/materials.test.d.ts +2 -0
  28. package/dist/lib/materials.test.d.ts.map +1 -0
  29. package/dist/lib/materials.test.js +25 -0
  30. package/dist/lib/merged-outline-node.test.d.ts +2 -0
  31. package/dist/lib/merged-outline-node.test.d.ts.map +1 -0
  32. package/dist/lib/merged-outline-node.test.js +22 -0
  33. package/dist/lib/snapshot-pipeline.d.ts +42 -0
  34. package/dist/lib/snapshot-pipeline.d.ts.map +1 -0
  35. package/dist/lib/snapshot-pipeline.js +254 -0
  36. package/dist/lib/terrain-perimeter-fill.d.ts +7 -0
  37. package/dist/lib/terrain-perimeter-fill.d.ts.map +1 -0
  38. package/dist/lib/terrain-perimeter-fill.js +75 -0
  39. package/dist/lib/texture-node-guard.d.ts +13 -0
  40. package/dist/lib/texture-node-guard.d.ts.map +1 -0
  41. package/dist/lib/texture-node-guard.js +39 -0
  42. package/dist/lib/texture-reference.d.ts +31 -0
  43. package/dist/lib/texture-reference.d.ts.map +1 -0
  44. package/dist/lib/texture-reference.js +139 -0
  45. package/dist/lib/texture-reference.test.d.ts +2 -0
  46. package/dist/lib/texture-reference.test.d.ts.map +1 -0
  47. package/dist/lib/texture-reference.test.js +86 -0
  48. package/dist/store/use-viewer.d.ts +13 -0
  49. package/dist/store/use-viewer.d.ts.map +1 -1
  50. package/dist/store/use-viewer.js +16 -1
  51. package/dist/store/use-viewer.test.js +19 -0
  52. package/dist/systems/ceiling/ceiling-system.d.ts.map +1 -1
  53. package/dist/systems/ceiling/ceiling-system.js +8 -4
  54. package/dist/systems/floor-elevation/floor-elevation-system.d.ts.map +1 -1
  55. package/dist/systems/floor-elevation/floor-elevation-system.js +30 -7
  56. package/dist/systems/geometry/geometry-system.d.ts.map +1 -1
  57. package/dist/systems/geometry/geometry-system.js +18 -2
  58. package/dist/systems/level/level-system.d.ts.map +1 -1
  59. package/dist/systems/level/level-system.js +8 -7
  60. package/dist/systems/level/level-utils.d.ts.map +1 -1
  61. package/dist/systems/level/level-utils.js +7 -6
  62. package/dist/systems/slab/slab-system.d.ts +5 -4
  63. package/dist/systems/slab/slab-system.d.ts.map +1 -1
  64. package/dist/systems/slab/slab-system.js +24 -94
  65. package/dist/systems/slab/slab-system.test.js +51 -12
  66. package/dist/systems/stair/stair-system.js +1 -1
  67. package/dist/systems/wall/wall-cutout.d.ts.map +1 -1
  68. package/dist/systems/wall/wall-cutout.js +5 -2
  69. package/dist/systems/wall/wall-support-extension.test.js +69 -1
  70. package/dist/systems/wall/wall-system.d.ts +3 -7
  71. package/dist/systems/wall/wall-system.d.ts.map +1 -1
  72. package/dist/systems/wall/wall-system.js +87 -22
  73. package/dist/systems/wall/wall-terrain-bottom.test.d.ts +2 -0
  74. package/dist/systems/wall/wall-terrain-bottom.test.d.ts.map +1 -0
  75. package/dist/systems/wall/wall-terrain-bottom.test.js +34 -0
  76. package/package.json +2 -2
  77. package/dist/components/viewer/walkthrough-controls.d.ts +0 -3
  78. package/dist/components/viewer/walkthrough-controls.d.ts.map +0 -1
  79. package/dist/components/viewer/walkthrough-controls.js +0 -151
package/README.md CHANGED
@@ -5,19 +5,19 @@
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
- npm install @pascal-app/viewer @pascal-app/core
8
+ npm install @pascal-app/core @pascal-app/viewer @pascal-app/editor @pascal-app/nodes
9
9
  ```
10
10
 
11
11
  ## Peer Dependencies
12
12
 
13
13
  ```bash
14
- npm install react three @react-three/fiber @react-three/drei
14
+ npm install next react react-dom three @react-three/fiber @react-three/drei lucide-react zustand
15
15
  ```
16
16
 
17
17
  ## What's Included
18
18
 
19
19
  - **Viewer Component** - WebGPU-powered 3D viewer with camera controls
20
- - **Node Renderers** - React Three Fiber components for all node types
20
+ - **Node Rendering Runtime** - Registry-driven dispatch for node renderers supplied by `@pascal-app/nodes`
21
21
  - **Post-Processing** - SSGI (ambient occlusion + global illumination), TRAA (anti-aliasing), outline effects
22
22
  - **Level System** - Level visibility and positioning (stacked/exploded/solo modes)
23
23
  - **Wall Cutout System** - Dynamic wall hiding based on camera position
@@ -26,10 +26,22 @@ npm install react three @react-three/fiber @react-three/drei
26
26
  ## Usage
27
27
 
28
28
  ```typescript
29
- import { Viewer, useViewer } from '@pascal-app/viewer'
30
- import { useScene } from '@pascal-app/core'
29
+ import { loadPlugin } from '@pascal-app/core'
30
+ import { builtinPlugin } from '@pascal-app/nodes'
31
+ import { Viewer } from '@pascal-app/viewer'
32
+ import { useEffect, useState } from 'react'
33
+
34
+ const registryReady = loadPlugin(builtinPlugin)
31
35
 
32
36
  function App() {
37
+ const [ready, setReady] = useState(false)
38
+
39
+ useEffect(() => {
40
+ void registryReady.then(() => setReady(true))
41
+ }, [])
42
+
43
+ if (!ready) return null
44
+
33
45
  return (
34
46
  <div style={{ width: '100vw', height: '100vh' }}>
35
47
  <Viewer />
@@ -38,6 +50,9 @@ function App() {
38
50
  }
39
51
  ```
40
52
 
53
+ Load the built-in plugin once, before mounting any viewer. Without it, the registry has no node
54
+ definitions and scene nodes cannot render. Host-provided plugins use the same `loadPlugin` API.
55
+
41
56
  ## Custom Camera Controls
42
57
 
43
58
  ```typescript
@@ -1 +1 @@
1
- {"version":3,"file":"frame-limiter.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/frame-limiter.tsx"],"names":[],"mappings":"AAIA,KAAK,iBAAiB,GAAG;IACvB,GAAG,CAAC,EAAE,MAAM,CAAA;CACb,CAAA;AAgBD,QAAA,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAuD7C,CAAA;AAED,eAAe,YAAY,CAAA"}
1
+ {"version":3,"file":"frame-limiter.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/frame-limiter.tsx"],"names":[],"mappings":"AAIA,KAAK,iBAAiB,GAAG;IACvB,GAAG,CAAC,EAAE,MAAM,CAAA;CACb,CAAA;AAgBD,QAAA,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAyE7C,CAAA;AAED,eAAe,YAAY,CAAA"}
@@ -28,13 +28,16 @@ const FrameLimiter = ({ fps = 50 }) => {
28
28
  let timer = null;
29
29
  let sizeSynced = false;
30
30
  const interval = 1000 / fps;
31
+ function syncSize() {
32
+ if (sizeSynced)
33
+ return;
34
+ renderer.setPixelRatio(dpr);
35
+ renderer.setSize(size.width, size.height, false);
36
+ sizeSynced = true;
37
+ }
31
38
  function tick(t) {
32
39
  raf = requestAnimationFrame(tick);
33
- if (!sizeSynced) {
34
- renderer.setPixelRatio(dpr);
35
- renderer.setSize(size.width, size.height, false);
36
- sizeSynced = true;
37
- }
40
+ syncSize();
38
41
  elapsed = t - then;
39
42
  if (elapsed > interval) {
40
43
  advance(i);
@@ -42,6 +45,15 @@ const FrameLimiter = ({ fps = 50 }) => {
42
45
  then = t - (elapsed % interval);
43
46
  }
44
47
  }
48
+ function kick() {
49
+ syncSize();
50
+ i += 1 / 1000;
51
+ advance(i);
52
+ }
53
+ function onVisibilityChange() {
54
+ if (document.visibilityState === 'visible')
55
+ kick();
56
+ }
45
57
  // Set frameloop to never, it will shut down the default render loop
46
58
  set({ frameloop: 'never' });
47
59
  if (DRAW_DISABLED) {
@@ -53,6 +65,11 @@ const FrameLimiter = ({ fps = 50 }) => {
53
65
  else {
54
66
  // Kick off custom render loop
55
67
  raf = requestAnimationFrame(tick);
68
+ // rAF can stall while a tab is hidden, unfocused, or occluded. With the
69
+ // default loop disabled, force one current frame as soon as it resumes.
70
+ document.addEventListener('visibilitychange', onVisibilityChange);
71
+ window.addEventListener('focus', kick);
72
+ window.addEventListener('pageshow', kick);
56
73
  }
57
74
  // Restore initial setting
58
75
  return () => {
@@ -62,6 +79,9 @@ const FrameLimiter = ({ fps = 50 }) => {
62
79
  if (timer) {
63
80
  clearInterval(timer);
64
81
  }
82
+ document.removeEventListener('visibilitychange', onVisibilityChange);
83
+ window.removeEventListener('focus', kick);
84
+ window.removeEventListener('pageshow', kick);
65
85
  set({ frameloop: initFrameloop });
66
86
  };
67
87
  }, [advance, dpr, fps, initFrameloop, renderPaused, renderer, set, size.height, size.width]);
@@ -421,7 +421,7 @@ export function GlbScene({ url, interactiveItems, referenceNodes, replaceNodes,
421
421
  }
422
422
  else {
423
423
  bookmarkNode = rootNode;
424
- _camBox.setFromObject(gltf.scene);
424
+ _camBox.setFromObject(rootNode ?? gltf.scene);
425
425
  }
426
426
  const bookmark = bookmarkNode?.userData?.camera;
427
427
  if (bookmark) {
@@ -781,7 +781,7 @@ export function GlbScene({ url, interactiveItems, referenceNodes, replaceNodes,
781
781
  }
782
782
  });
783
783
  // E or click activates the openable in view. The click also re-locks the
784
- // pointer via WalkthroughControls — harmless overlap; no selection happens.
784
+ // pointer through the walkthrough controller; no selection happens.
785
785
  const activateWalkDoor = useCallback(() => {
786
786
  if (walkDoorRef.current)
787
787
  toggleOpenable(walkDoorRef.current);
@@ -1,3 +1,13 @@
1
+ export declare const WALKTHROUGH_FOV = 60;
2
+ export declare const STAND_CAPSULE: [number, number, number, number];
3
+ export declare const CROUCH_CAPSULE: [number, number, number, number];
4
+ export declare const STAND_FLOAT_HEIGHT = 0.5;
5
+ export declare const CROUCH_FLOAT_HEIGHT = 0.25;
6
+ export declare const CROUCH_EYE_OFFSET = 0.1;
7
+ export declare const CROUCH_WALK_SPEED = 1;
8
+ export declare const CROUCH_RUN_SPEED = 1.4;
9
+ export declare const STAND_CLEARANCE = 1.25;
10
+ export declare const EYE_LERP_SPEED = 12;
1
11
  /**
2
12
  * First-person walkthrough controller for the baked GLB. Reuses the editor's
3
13
  * `BVHEcctrl` capsule character controller (gravity, jump, sprint, ground-float,
@@ -1 +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"}
1
+ {"version":3,"file":"glb-walkthrough-controller.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/glb-walkthrough-controller.tsx"],"names":[],"mappings":"AAiCA,eAAO,MAAM,eAAe,KAAK,CAAA;AAkBjC,eAAO,MAAM,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAqB,CAAA;AAChF,eAAO,MAAM,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAqB,CAAA;AACjF,eAAO,MAAM,kBAAkB,MAAM,CAAA;AACrC,eAAO,MAAM,mBAAmB,OAAO,CAAA;AACvC,eAAO,MAAM,iBAAiB,MAAM,CAAA;AACpC,eAAO,MAAM,iBAAiB,IAAI,CAAA;AAClC,eAAO,MAAM,gBAAgB,MAAM,CAAA;AAInC,eAAO,MAAM,eAAe,OAAO,CAAA;AACnC,eAAO,MAAM,cAAc,KAAK,CAAA;AA8LhC;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CAAC,EAAE,GAAG,EAAE,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,sCAqPhE"}
@@ -3,14 +3,18 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { KeyboardControls } from '@react-three/drei';
4
4
  import { useFrame, useThree } from '@react-three/fiber';
5
5
  import { useCallback, useEffect, useRef, useState } from 'react';
6
- import { Box3, BoxGeometry, BufferGeometry, Euler, Float32BufferAttribute, Matrix4, Mesh, MeshBasicMaterial, Quaternion, Vector3, } from 'three';
6
+ import { Box3, BoxGeometry, BufferGeometry, Euler, Float32BufferAttribute, Matrix4, Mesh, MeshBasicMaterial, Quaternion, Raycaster, Vector3, } from 'three';
7
7
  import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
8
8
  import { acceleratedRaycast, computeBoundsTree, disposeBoundsTree } from 'three-mesh-bvh';
9
9
  import { useGLTFKTX2 } from '../../hooks/use-gltf-ktx2';
10
10
  import { SCENE_LAYER } from '../../lib/layers';
11
11
  import useViewer from '../../store/use-viewer';
12
12
  import BVHEcctrl, {} from './bvh-ecctrl';
13
- import { WALKTHROUGH_FOV } from './walkthrough-controls';
13
+ // First-person FOV. The orbit camera is 50° (set on the Canvas), which feels
14
+ // cramped on foot; ~60° vertical (~90° horizontal at 16:9) restores peripheral
15
+ // awareness without wide-angle distortion. Applied only while walking — both
16
+ // walkthrough controllers read this and restore the orbit FOV on exit.
17
+ export const WALKTHROUGH_FOV = 60;
14
18
  // Eye/capsule geometry mirrors the editor's first-person controller so the
15
19
  // baked walkthrough feels identical. The capsule centre sits below the eye; the
16
20
  // camera rides the capsule with a small offset and the controller floats it to
@@ -20,6 +24,26 @@ const CONTROLLER_CENTER_FROM_EYE = 0.85;
20
24
  const SPAWN_EYE_HEIGHT = 1.65;
21
25
  const LOOK_SENSITIVITY = 0.002;
22
26
  const VOID_FALL_RESPAWN_DEPTH = 12;
27
+ // Crouch (hold Ctrl): swap to a short capsule — it shrinks around the centre,
28
+ // so a crouch mid-jump also lowers the head AND raises the feet, letting the
29
+ // player thread window openings. Standing back up is gated on headroom.
30
+ // The float gap counts toward the effective obstacle height (the capsule rides
31
+ // floatHeight above the ground), so crouching also lowers it: crouched span is
32
+ // CROUCH_FLOAT_HEIGHT + capsule = 0.25 + 0.7 = 0.95 m — fits a 1 m opening.
33
+ export const STAND_CAPSULE = [0.25, 0.8, 4, 8];
34
+ export const CROUCH_CAPSULE = [0.25, 0.2, 4, 8];
35
+ export const STAND_FLOAT_HEIGHT = 0.5;
36
+ export const CROUCH_FLOAT_HEIGHT = 0.25;
37
+ export const CROUCH_EYE_OFFSET = 0.1;
38
+ export const CROUCH_WALK_SPEED = 1;
39
+ export const CROUCH_RUN_SPEED = 1.4;
40
+ // Headroom (from the capsule centre, upward) required before uncrouching:
41
+ // standing raises the centre by half the length delta plus the float delta,
42
+ // and the standing capsule top sits standLength/2 + radius above the centre.
43
+ export const STAND_CLEARANCE = 1.25;
44
+ export const EYE_LERP_SPEED = 12;
45
+ const standClearanceRaycaster = new Raycaster();
46
+ const UP = new Vector3(0, 1, 0);
23
47
  // Kinds that must not block the player: room helpers, the spawn marker, the
24
48
  // ceiling/roof shell (you walk under them), and door/window leaves — excluding
25
49
  // the latter lets you pass any doorway whether the leaf is open or shut (the
@@ -34,7 +58,7 @@ const keyboardMap = [
34
58
  { name: 'jump', keys: ['Space'] },
35
59
  { name: 'run', keys: ['ShiftLeft', 'ShiftRight'] },
36
60
  ];
37
- const cameraOffset = new Vector3(0, CAMERA_EYE_OFFSET, 0);
61
+ const cameraOffset = new Vector3();
38
62
  const cameraEuler = new Euler(0, 0, 0, 'YXZ');
39
63
  const spawnQuat = new Quaternion();
40
64
  const spawnEuler = new Euler(0, 0, 0, 'YXZ');
@@ -205,6 +229,10 @@ export function GlbWalkthroughController({ url }) {
205
229
  const controllerRef = useRef(null);
206
230
  const yawRef = useRef(0);
207
231
  const pitchRef = useRef(0);
232
+ const crouchKeyRef = useRef(false);
233
+ const suspendRef = useRef(false);
234
+ const eyeOffsetRef = useRef(CAMERA_EYE_OFFSET);
235
+ const [crouched, setCrouched] = useState(false);
208
236
  const [start, setStart] = useState(null);
209
237
  const [world, setWorld] = useState(null);
210
238
  // Build the collider on the first frame (priority after GlbScene's level loop,
@@ -299,22 +327,63 @@ export function GlbWalkthroughController({ url }) {
299
327
  if (event.code === 'Escape' && document.pointerLockElement !== canvas) {
300
328
  useViewer.getState().setWalkthroughMode(false);
301
329
  }
330
+ // P toggles a cursor pause (advertised in the HUD): frees the pointer
331
+ // without leaving the walkthrough — e.g. for an OS screenshot, which
332
+ // needs a movable cursor — and click or P resumes.
333
+ if (event.code === 'KeyP') {
334
+ if (document.pointerLockElement === canvas) {
335
+ suspendRef.current = true;
336
+ useViewer.getState().setWalkthroughSuspended(true);
337
+ document.exitPointerLock();
338
+ }
339
+ else if (suspendRef.current) {
340
+ const result = canvas.requestPointerLock?.();
341
+ if (result && typeof result.catch === 'function')
342
+ result.catch(() => { });
343
+ }
344
+ }
345
+ // While paused (P), crouch is frozen as-is — ⌃⇧⌘4 (clipboard screenshot)
346
+ // must not toggle it under the user.
347
+ if ((event.code === 'ControlLeft' || event.code === 'ControlRight') && !suspendRef.current) {
348
+ crouchKeyRef.current = true;
349
+ }
350
+ };
351
+ const onKeyUp = (event) => {
352
+ if ((event.code === 'ControlLeft' || event.code === 'ControlRight') && !suspendRef.current) {
353
+ crouchKeyRef.current = false;
354
+ }
355
+ };
356
+ const onBlur = () => {
357
+ if (!suspendRef.current)
358
+ crouchKeyRef.current = false;
302
359
  };
303
360
  const onPointerLockChange = () => {
304
- if (document.pointerLockElement === canvas)
361
+ if (document.pointerLockElement === canvas) {
305
362
  wasLocked = true;
306
- else if (wasLocked)
363
+ suspendRef.current = false;
364
+ useViewer.getState().setWalkthroughSuspended(false);
365
+ }
366
+ else if (suspendRef.current) {
367
+ // Deliberately released (screenshot pause) — stay in walkthrough.
368
+ }
369
+ else if (wasLocked) {
307
370
  useViewer.getState().setWalkthroughMode(false);
371
+ }
308
372
  };
309
373
  document.addEventListener('mousemove', onMouseMove);
310
374
  canvas.addEventListener('click', onClick);
311
375
  document.addEventListener('keydown', onKeyDown);
376
+ document.addEventListener('keyup', onKeyUp);
377
+ window.addEventListener('blur', onBlur);
312
378
  document.addEventListener('pointerlockchange', onPointerLockChange);
313
379
  return () => {
314
380
  document.removeEventListener('mousemove', onMouseMove);
315
381
  canvas.removeEventListener('click', onClick);
316
382
  document.removeEventListener('keydown', onKeyDown);
383
+ document.removeEventListener('keyup', onKeyUp);
384
+ window.removeEventListener('blur', onBlur);
317
385
  document.removeEventListener('pointerlockchange', onPointerLockChange);
386
+ useViewer.getState().setWalkthroughSuspended(false);
318
387
  if (document.pointerLockElement === canvas)
319
388
  document.exitPointerLock();
320
389
  };
@@ -336,8 +405,16 @@ export function GlbWalkthroughController({ url }) {
336
405
  const setControllerApi = useCallback((api) => {
337
406
  controllerRef.current = api;
338
407
  }, []);
408
+ const hasStandingClearance = useCallback((position) => {
409
+ const mesh = worldRef.current?.mesh;
410
+ if (!mesh)
411
+ return true;
412
+ standClearanceRaycaster.set(position, UP);
413
+ standClearanceRaycaster.far = STAND_CLEARANCE;
414
+ return standClearanceRaycaster.intersectObject(mesh, false).length === 0;
415
+ }, []);
339
416
  // Drive the camera from the capsule each frame + respawn if it falls into void.
340
- useFrame(() => {
417
+ useFrame((_, delta) => {
341
418
  const group = controllerRef.current?.group;
342
419
  if (!group)
343
420
  return;
@@ -345,13 +422,24 @@ export function GlbWalkthroughController({ url }) {
345
422
  group.position.set(start.position[0], start.position[1], start.position[2]);
346
423
  controllerRef.current?.resetLinVel();
347
424
  }
425
+ // Crouch follows the held key; standing back up waits for headroom.
426
+ // Frozen while the cursor pause is active.
427
+ if (!suspendRef.current && crouchKeyRef.current !== crouched) {
428
+ if (crouchKeyRef.current)
429
+ setCrouched(true);
430
+ else if (hasStandingClearance(group.position))
431
+ setCrouched(false);
432
+ }
433
+ const targetEyeOffset = crouched ? CROUCH_EYE_OFFSET : CAMERA_EYE_OFFSET;
434
+ eyeOffsetRef.current +=
435
+ (targetEyeOffset - eyeOffsetRef.current) * Math.min(1, delta * EYE_LERP_SPEED);
348
436
  group.rotation.y = 0;
349
- camera.position.copy(group.position).add(cameraOffset);
437
+ camera.position.copy(group.position).add(cameraOffset.set(0, eyeOffsetRef.current, 0));
350
438
  cameraEuler.set(pitchRef.current, yawRef.current, 0, 'YXZ');
351
439
  camera.quaternion.setFromEuler(cameraEuler);
352
440
  camera.updateMatrixWorld(true);
353
441
  }, 2.5);
354
442
  if (!(world && start))
355
443
  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 }) }));
444
+ return (_jsx(KeyboardControls, { map: keyboardMap, children: _jsx(BVHEcctrl, { acceleration: 26, airDragFactor: 0.3, colliderCapsuleArgs: crouched ? CROUCH_CAPSULE : STAND_CAPSULE, colliderMeshes: [world.mesh], collisionCheckIteration: 3, collisionPushBackDamping: 0.1, collisionPushBackThreshold: 0.001, deceleration: 30, delay: 0, fallGravityFactor: 4, floatCheckType: "BOTH", floatDampingC: 36, floatHeight: crouched ? CROUCH_FLOAT_HEIGHT : STAND_FLOAT_HEIGHT, floatPullBackHeight: 0.35, floatSensorRadius: 0.15, floatSpringK: 1200, gravity: 9.81, jumpVel: 5, maxRunSpeed: crouched ? CROUCH_RUN_SPEED : 5, maxSlope: 1.2, maxWalkSpeed: crouched ? CROUCH_WALK_SPEED : 2, position: start.position, ref: setControllerApi }) }));
357
445
  }
@@ -1 +1 @@
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"}
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;AAGrE,OAAkB,EAAE,KAAK,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAQtE,OAAuB,EAAwB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAU1F,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,sGA4NV,CAAA;AAkBF,eAAe,MAAM,CAAA"}
@@ -9,6 +9,7 @@ import { PERF_OVERLAY_ENABLED, pushGpuSample } from '../../lib/gpu-perf';
9
9
  import { applyIsolation, clearIsolation } from '../../lib/isolation';
10
10
  import { ensureKtx2Support } from '../../lib/ktx2-loader';
11
11
  import { getSceneTheme } from '../../lib/scene-themes';
12
+ import { installTextureNodeNullGuard } from '../../lib/texture-node-guard';
12
13
  import useViewer, {} from '../../store/use-viewer';
13
14
  import { FloorElevationSystem } from '../../systems/floor-elevation/floor-elevation-system';
14
15
  import { GeometrySystem } from '../../systems/geometry/geometry-system';
@@ -22,6 +23,9 @@ import { RegisteredSystems } from './registered-systems';
22
23
  import { SceneBvh } from './scene-bvh';
23
24
  import { SelectionManager } from './selection-manager';
24
25
  import { ViewerCamera } from './viewer-camera';
26
+ // Must be in place before any node material builds — a null texture pulled by
27
+ // a shared override-material pass otherwise kills the render pass outright.
28
+ installTextureNodeNullGuard();
25
29
  extend(THREE);
26
30
  // R3F's <Canvas> useLayoutEffect has no deps, so any re-render (theme switch,
27
31
  // parent re-render, StrictMode double-mount) re-invokes `configure()`. With a
@@ -227,6 +231,11 @@ function SceneReadyTracker({ onSceneReadyChange, sceneReadyKey, sceneReadyMaxWai
227
231
  return null;
228
232
  }
229
233
  const Viewer = forwardRef(function Viewer({ children, hoverStyles = DEFAULT_HOVER_STYLES, selectionManager = 'default', perf = false, useBvh = true, renderContext = 'editor', transparent, defaultRender, isolate, sceneReadyKey, onSceneReadyChange, sceneReadyMaxWaitMs, disablePostFx = false, }, ref) {
234
+ useEffect(() => {
235
+ if (nodeRegistry.size === 0) {
236
+ console.warn('[viewer] Node registry is empty. Install @pascal-app/nodes and call await loadPlugin(builtinPlugin) before mounting <Viewer>.');
237
+ }
238
+ }, []);
230
239
  useImperativeHandle(ref, () => ({
231
240
  setIsolated: (ids) => applyIsolation(ids),
232
241
  }), []);
@@ -1 +1 @@
1
- {"version":3,"file":"post-processing.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/post-processing.tsx"],"names":[],"mappings":"AAwCA,eAAO,MAAM,YAAY;;;CAGxB,CAAA;AAGD,eAAO,MAAM,WAAW;;;;;;;;;;;;;CAavB,CAAA;AA0DD,MAAM,MAAM,UAAU,GAAG;IACvB,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,OAAO,CAAA;CACf,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,UAAU,CAAA;CACpB,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;AAS9B,eAAO,MAAM,oBAAoB,EAAE,WAElC,CAAA;AAiBD,QAAA,MAAM,oBAAoB,GAAI,iCAG3B;IACD,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB,SA8mBA,CAAA;AAED,eAAe,oBAAoB,CAAA"}
1
+ {"version":3,"file":"post-processing.d.ts","sourceRoot":"","sources":["../../../src/components/viewer/post-processing.tsx"],"names":[],"mappings":"AAwCA,eAAO,MAAM,YAAY;;;CAGxB,CAAA;AAGD,eAAO,MAAM,WAAW;;;;;;;;;;;;;CAavB,CAAA;AA0DD,MAAM,MAAM,UAAU,GAAG;IACvB,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,OAAO,CAAA;CACf,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,UAAU,CAAA;CACpB,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;AAS9B,eAAO,MAAM,oBAAoB,EAAE,WAElC,CAAA;AAiBD,QAAA,MAAM,oBAAoB,GAAI,iCAG3B;IACD,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB,SAgnBA,CAAA;AAED,eAAe,oBAAoB,CAAA"}
@@ -618,6 +618,7 @@ const PostProcessingPasses = ({ hoverStyles = DEFAULT_HOVER_STYLES, disablePostF
618
618
  }
619
619
  catch (error) {
620
620
  hasPipelineErrorRef.current = true;
621
+ renderer.setRenderTarget?.(null);
621
622
  console.error('[viewer/post-processing] Render pass failed.', {
622
623
  retryCount: retryCountRef.current,
623
624
  rendererCtor: renderer.constructor?.name,
@@ -353,13 +353,16 @@ const PointerMissedHandler = ({ clickHandledRef, }) => {
353
353
  };
354
354
  const OutlinerSync = () => {
355
355
  const selection = useViewer((s) => s.selection);
356
+ const externalSelectedIds = useViewer((s) => s.externalSelectedIds);
356
357
  const hoveredId = useViewer((s) => s.hoveredId);
357
358
  const outliner = useViewer((s) => s.outliner);
359
+ const geometryRevision = useViewer((s) => s.geometryRevision);
358
360
  const nodes = useScene((s) => s.nodes);
359
361
  useEffect(() => {
362
+ void geometryRevision;
360
363
  // Sync selected objects
361
364
  outliner.selectedObjects.length = 0;
362
- for (const id of selection.selectedIds) {
365
+ for (const id of new Set([...selection.selectedIds, ...externalSelectedIds])) {
363
366
  const node = nodes[id];
364
367
  if (node?.type === 'slab')
365
368
  continue;
@@ -377,6 +380,6 @@ const OutlinerSync = () => {
377
380
  if (obj)
378
381
  outliner.hoveredObjects.push(obj);
379
382
  }
380
- }, [selection, hoveredId, outliner, nodes]);
383
+ }, [selection, externalSelectedIds, hoveredId, outliner, nodes, geometryRevision]);
381
384
  return null;
382
385
  };
@@ -1 +1 @@
1
- {"version":3,"file":"use-gltf-ktx2.d.ts","sourceRoot":"","sources":["../../src/hooks/use-gltf-ktx2.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAK3C,QAAA,MAAM,WAAW,GAAI,MAAM,MAAM,KAAG,UAAU,CAAC,OAAO,OAAO,CAU5D,CAAA;AAED,OAAO,EAAE,WAAW,EAAE,CAAA"}
1
+ {"version":3,"file":"use-gltf-ktx2.d.ts","sourceRoot":"","sources":["../../src/hooks/use-gltf-ktx2.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAK3C,QAAA,MAAM,WAAW,GAAI,MAAM,MAAM,KAAG,UAAU,CAAC,OAAO,OAAO,CAO5D,CAAA;AAED,OAAO,EAAE,WAAW,EAAE,CAAA"}
@@ -1,14 +1,11 @@
1
1
  import { useGLTF } from '@react-three/drei';
2
2
  import { useThree } from '@react-three/fiber';
3
3
  import { MeshoptDecoder } from 'three/examples/jsm/libs/meshopt_decoder.module.js';
4
- import { ensureKtx2Support, ktx2Loader } from '../lib/ktx2-loader';
4
+ import { configureKtx2Support } from '../lib/ktx2-loader';
5
5
  const useGLTFKTX2 = (path) => {
6
6
  const gl = useThree((state) => state.gl);
7
7
  return useGLTF(path, true, true, (loader) => {
8
- if (ensureKtx2Support(gl)) {
9
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
- loader.setKTX2Loader(ktx2Loader);
11
- }
8
+ configureKtx2Support(loader, gl);
12
9
  loader.setMeshoptDecoder(MeshoptDecoder);
13
10
  });
14
11
  };
package/dist/index.d.ts CHANGED
@@ -6,11 +6,10 @@ export { type BVHEcctrlApi, default as BVHEcctrl, type MovementInput, } from './
6
6
  export { buildGlbInteractiveItems, GlbInteractive, type GlbInteractiveItem, } from './components/viewer/glb-interactive';
7
7
  export { buildGlbReferenceNodes, buildGlbReplaceNodes, } from './components/viewer/glb-reference-nodes';
8
8
  export { type GlbHover, type GlbIdentity, type GlbLevel, GlbScene, type GlbWalkthrough, } from './components/viewer/glb-scene';
9
- export { GlbWalkthroughController } from './components/viewer/glb-walkthrough-controller';
9
+ export { CROUCH_CAPSULE, CROUCH_EYE_OFFSET, CROUCH_FLOAT_HEIGHT, CROUCH_RUN_SPEED, CROUCH_WALK_SPEED, EYE_LERP_SPEED, GlbWalkthroughController, STAND_CAPSULE, STAND_CLEARANCE, STAND_FLOAT_HEIGHT, WALKTHROUGH_FOV, } from './components/viewer/glb-walkthrough-controller';
10
10
  export type { HoverStyle, HoverStyles } from './components/viewer/post-processing';
11
11
  export { DEFAULT_HOVER_STYLES, SSGI_PARAMS, } from './components/viewer/post-processing';
12
12
  export { SceneEnvironment } from './components/viewer/scene-environment';
13
- export { WalkthroughControls } from './components/viewer/walkthrough-controls';
14
13
  export { useAssetUrl } from './hooks/use-asset-url';
15
14
  export { useGLTFKTX2 } from './hooks/use-gltf-ktx2';
16
15
  export { useNodeEvents } from './hooks/use-node-events';
@@ -19,16 +18,20 @@ export { backdropGradient, deepSkyColor, horizonHazeColor } from './lib/backdrop
19
18
  export { applyWorldScaleBoxUVs } from './lib/box-uv';
20
19
  export { ADDITION, Brush, computeGeometryBoundsTree, csgEvaluator, csgGeometry, csgMaterials, INTERSECTION, prepareBrushForCSG, SUBTRACTION, } from './lib/csg-utils';
21
20
  export type { EdgeMode } from './lib/edge-style';
21
+ export { computeHeroFraming, DEFAULT_FRAMING_EXCLUDED_TYPES, type HeroFraming, heroCameraPose, temporarilyHideNodeTypes, unionRegisteredNodeBounds, } from './lib/hero-pose';
22
22
  export { applyIsolation, clearIsolation, collectIsolationSubtree, isIsolationActive, } from './lib/isolation';
23
- export { ensureKtx2Support } from './lib/ktx2-loader';
23
+ export { configureKtx2Support, ensureKtx2Support } from './lib/ktx2-loader';
24
24
  export { GRID_LAYER, OVERLAY_LAYER, SCENE_LAYER, ZONE_LAYER } from './lib/layers';
25
25
  export { applyMaterialPresetToMaterials, BLUEPRINT_PALETTE, baseMaterial, CLAY_PALETTE, type ColorPreset, clearMaterialCache, createDefaultMaterial, createMaterial, createMaterialFromPresetRef, createSurfaceRoleMaterial, DEFAULT_CEILING_MATERIAL, DEFAULT_DOOR_MATERIAL, DEFAULT_ROOF_MATERIAL, DEFAULT_SHELF_MATERIAL, DEFAULT_SLAB_MATERIAL, DEFAULT_STAIR_MATERIAL, DEFAULT_WALL_MATERIAL, DEFAULT_WINDOW_MATERIAL, disposeMaterial, glassMaterial, MONO_PALETTE, PRESET_PALETTES, type RenderShading, resolveMaterialRef, resolveSlotDefaultMaterial, resolveSurfaceColor, WHITE_PALETTE, } from './lib/materials';
26
26
  export { mergedOutline } from './lib/merged-outline-node';
27
27
  export { unionPolygons } from './lib/polygon-union';
28
28
  export { getSceneTheme, SCENE_THEME_IDS, SCENE_THEMES, type SceneTheme, } from './lib/scene-themes';
29
+ export { createSnapshotPipeline, type SnapshotCaptureMode, type SnapshotCaptureResult, type SnapshotCropRegion, type SnapshotPipeline, type SnapshotSize, THUMBNAIL_HEIGHT, THUMBNAIL_WIDTH, } from './lib/snapshot-pipeline';
30
+ export { buildTerrainPerimeterFillGeometry, type TerrainPerimeterPoint, } from './lib/terrain-perimeter-fill';
31
+ export { getPascalTextureRef, type PascalTextureColorSpace, type PascalTextureMap, type PascalTextureRef, stampPascalTextureRef, textureMapForSlot, } from './lib/texture-reference';
29
32
  export { packNormalToRGB, unpackRGBToNormal } from './lib/tsl-compat';
30
33
  export { useItemLightPool } from './store/use-item-light-pool';
31
- export { applyCountryUnitDefault, default as useViewer } from './store/use-viewer';
34
+ export { applyCountryUnitDefault, default as useViewer, type MetricNotation, } from './store/use-viewer';
32
35
  export { CeilingSystem } from './systems/ceiling/ceiling-system';
33
36
  export { createColumnBoxGeometry, createColumnCylinderGeometry, createColumnSphereGeometry, createColumnTorusGeometry, } from './systems/column/column-geometry';
34
37
  export { DoorAnimationSystem } from './systems/door/door-animation-system';
@@ -45,7 +48,7 @@ export { snapLevelsToTruePositions } from './systems/level/level-utils';
45
48
  export { getRoofMaterialArray } from './systems/roof/roof-materials';
46
49
  export { clipGeometryBySegmentTrim, generateRoofSegmentGeometry, getRoofOuterSurfaceFrameAtPoint, getRoofSegmentBrushes, mapRoofGroupMaterialIndex, ROOF_MATERIAL_SLOT_COUNT, RoofSystem, remapRoofShellFaces, roofCsgDummyMats, type SurfaceFrame, } from './systems/roof/roof-system';
47
50
  export { ScanSystem } from './systems/scan/scan-system';
48
- export { generateSlabGeometry, SlabSystem } from './systems/slab/slab-system';
51
+ export { generateSlabGeometry } from './systems/slab/slab-system';
49
52
  export { getStairBodyMaterials, getStairRailingMaterial, getStraightStairSegmentBodyMaterials, type StairBodyMaterials, } from './systems/stair/stair-materials';
50
53
  export { StairSystem } from './systems/stair/stair-system';
51
54
  export { buildOpeningCutoutGeometry, getOpeningCutoutBottomPadding, hasFlatOpeningCutoutBottom, } from './systems/wall/opening-cutout-geometry';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -15,10 +15,9 @@ export { default as BVHEcctrl, } from './components/viewer/bvh-ecctrl';
15
15
  export { buildGlbInteractiveItems, GlbInteractive, } from './components/viewer/glb-interactive';
16
16
  export { buildGlbReferenceNodes, buildGlbReplaceNodes, } from './components/viewer/glb-reference-nodes';
17
17
  export { GlbScene, } from './components/viewer/glb-scene';
18
- export { GlbWalkthroughController } from './components/viewer/glb-walkthrough-controller';
18
+ export { CROUCH_CAPSULE, CROUCH_EYE_OFFSET, CROUCH_FLOAT_HEIGHT, CROUCH_RUN_SPEED, CROUCH_WALK_SPEED, EYE_LERP_SPEED, GlbWalkthroughController, STAND_CAPSULE, STAND_CLEARANCE, STAND_FLOAT_HEIGHT, WALKTHROUGH_FOV, } from './components/viewer/glb-walkthrough-controller';
19
19
  export { DEFAULT_HOVER_STYLES, SSGI_PARAMS, } from './components/viewer/post-processing';
20
20
  export { SceneEnvironment } from './components/viewer/scene-environment';
21
- export { WalkthroughControls } from './components/viewer/walkthrough-controls';
22
21
  export { useAssetUrl } from './hooks/use-asset-url';
23
22
  export { useGLTFKTX2 } from './hooks/use-gltf-ktx2';
24
23
  export { useNodeEvents } from './hooks/use-node-events';
@@ -29,16 +28,20 @@ export { applyWorldScaleBoxUVs } from './lib/box-uv';
29
28
  // geometry subtracts pieces against their host. Lives in viewer
30
29
  // because three-bvh-csg / three-mesh-bvh are viewer-only deps.
31
30
  export { ADDITION, Brush, computeGeometryBoundsTree, csgEvaluator, csgGeometry, csgMaterials, INTERSECTION, prepareBrushForCSG, SUBTRACTION, } from './lib/csg-utils';
31
+ export { computeHeroFraming, DEFAULT_FRAMING_EXCLUDED_TYPES, heroCameraPose, temporarilyHideNodeTypes, unionRegisteredNodeBounds, } from './lib/hero-pose';
32
32
  export { applyIsolation, clearIsolation, collectIsolationSubtree, isIsolationActive, } from './lib/isolation';
33
- export { ensureKtx2Support } from './lib/ktx2-loader';
33
+ export { configureKtx2Support, ensureKtx2Support } from './lib/ktx2-loader';
34
34
  export { GRID_LAYER, OVERLAY_LAYER, SCENE_LAYER, ZONE_LAYER } from './lib/layers';
35
35
  export { applyMaterialPresetToMaterials, BLUEPRINT_PALETTE, baseMaterial, CLAY_PALETTE, clearMaterialCache, createDefaultMaterial, createMaterial, createMaterialFromPresetRef, createSurfaceRoleMaterial, DEFAULT_CEILING_MATERIAL, DEFAULT_DOOR_MATERIAL, DEFAULT_ROOF_MATERIAL, DEFAULT_SHELF_MATERIAL, DEFAULT_SLAB_MATERIAL, DEFAULT_STAIR_MATERIAL, DEFAULT_WALL_MATERIAL, DEFAULT_WINDOW_MATERIAL, disposeMaterial, glassMaterial, MONO_PALETTE, PRESET_PALETTES, resolveMaterialRef, resolveSlotDefaultMaterial, resolveSurfaceColor, WHITE_PALETTE, } from './lib/materials';
36
36
  export { mergedOutline } from './lib/merged-outline-node';
37
37
  export { unionPolygons } from './lib/polygon-union';
38
38
  export { getSceneTheme, SCENE_THEME_IDS, SCENE_THEMES, } from './lib/scene-themes';
39
+ export { createSnapshotPipeline, THUMBNAIL_HEIGHT, THUMBNAIL_WIDTH, } from './lib/snapshot-pipeline';
40
+ export { buildTerrainPerimeterFillGeometry, } from './lib/terrain-perimeter-fill';
41
+ export { getPascalTextureRef, stampPascalTextureRef, textureMapForSlot, } from './lib/texture-reference';
39
42
  export { packNormalToRGB, unpackRGBToNormal } from './lib/tsl-compat';
40
43
  export { useItemLightPool } from './store/use-item-light-pool';
41
- export { applyCountryUnitDefault, default as useViewer } from './store/use-viewer';
44
+ export { applyCountryUnitDefault, default as useViewer, } from './store/use-viewer';
42
45
  export { CeilingSystem } from './systems/ceiling/ceiling-system';
43
46
  export { createColumnBoxGeometry, createColumnCylinderGeometry, createColumnSphereGeometry, createColumnTorusGeometry, } from './systems/column/column-geometry';
44
47
  export { DoorAnimationSystem } from './systems/door/door-animation-system';
@@ -69,10 +72,9 @@ export { getRoofMaterialArray } from './systems/roof/roof-materials';
69
72
  // belong here — those live in `@pascal-app/nodes/<kind>/`.
70
73
  export { clipGeometryBySegmentTrim, generateRoofSegmentGeometry, getRoofOuterSurfaceFrameAtPoint, getRoofSegmentBrushes, mapRoofGroupMaterialIndex, ROOF_MATERIAL_SLOT_COUNT, RoofSystem, remapRoofShellFaces, roofCsgDummyMats, } from './systems/roof/roof-system';
71
74
  export { ScanSystem } from './systems/scan/scan-system';
72
- // Slab system follows the wall + fence re-export pattern — composed into
73
- // the registry-driven slab definition's `def.system`. Removed in Phase 6
74
- // alongside the legacy slab mount point.
75
- export { generateSlabGeometry, SlabSystem } from './systems/slab/slab-system';
75
+ // Pure slab geometry generator — composed into the registry-driven slab
76
+ // definition's `def.geometry` in `@pascal-app/nodes`.
77
+ export { generateSlabGeometry } from './systems/slab/slab-system';
76
78
  export { getStairBodyMaterials, getStairRailingMaterial, getStraightStairSegmentBodyMaterials, } from './systems/stair/stair-materials';
77
79
  export { StairSystem } from './systems/stair/stair-system';
78
80
  // Pure opening-cutout profile math shared by the wall CSG pipeline and
@@ -0,0 +1,47 @@
1
+ import { Box3 } from 'three';
2
+ export declare const DEFAULT_FRAMING_EXCLUDED_TYPES: readonly ["site", "scan", "guide", "spawn"];
3
+ export declare function heroCameraPose({ boxes, aspect, aim, fovDeg, azimuthRad, elevationRad, padding, minDistance, frameShift, }: {
4
+ boxes: Box3 | readonly Box3[];
5
+ aspect: number;
6
+ /** Where the camera looks (frame center). Defaults to the union-box center;
7
+ * pass the building's center to keep it dead-center while outlying boxes
8
+ * (lot plate, far palms) simply take asymmetric margin — the corner fit
9
+ * still guarantees every box stays in frame. */
10
+ aim?: [number, number, number];
11
+ fovDeg?: number;
12
+ azimuthRad?: number;
13
+ elevationRad?: number;
14
+ padding?: number;
15
+ minDistance?: number;
16
+ /** Fraction of the frustum half-height to drop the aim by. 0 keeps the aim
17
+ * (the building center) exactly at frame center. */
18
+ frameShift?: number;
19
+ }): {
20
+ position: [number, number, number];
21
+ target: [number, number, number];
22
+ };
23
+ export declare function unionRegisteredNodeBounds({ excludeTypes, }: {
24
+ excludeTypes: readonly string[];
25
+ }): Box3 | null;
26
+ export type HeroFraming = {
27
+ /** Fit constraints — everything that must stay in frame. */
28
+ boxes: Box3[];
29
+ /** Frame center: plate/structure center on XZ, building center on Y. */
30
+ aim: [number, number, number];
31
+ /** 45° to the dominant facade (falls back to world 45°). */
32
+ azimuthRad: number;
33
+ };
34
+ /**
35
+ * Framing for a scene hero shot. The base set is the built structure (walls,
36
+ * roofs, slabs, …) plus any intentionally-shaped site plate; an item joins
37
+ * only when it sits near that base — one palm at the far lot corner must not
38
+ * shrink the building. `building`/`level` container groups are skipped (their
39
+ * Object3D holds the whole subtree). The aim keeps the building dead-center:
40
+ * XZ from the plate+structure union, Y from the structure alone so the
41
+ * building — not the ground — is vertically centered. Scenes with no
42
+ * structure (pure furniture arrangements) fall back to framing every
43
+ * non-helper node.
44
+ */
45
+ export declare function computeHeroFraming(): HeroFraming | null;
46
+ export declare function temporarilyHideNodeTypes(types: readonly string[]): () => void;
47
+ //# sourceMappingURL=hero-pose.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hero-pose.d.ts","sourceRoot":"","sources":["../../src/lib/hero-pose.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAA0B,MAAM,OAAO,CAAA;AAEpD,eAAO,MAAM,8BAA8B,6CAA8C,CAAA;AAEzF,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,MAAM,EACN,GAAG,EACH,MAAW,EACX,UAAwB,EACxB,YAAmC,EACnC,OAAc,EACd,WAAe,EACf,UAAc,GACf,EAAE;IACD,KAAK,EAAE,IAAI,GAAG,SAAS,IAAI,EAAE,CAAA;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd;;;qDAGiD;IACjD,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;yDACqD;IACrD,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,GAAG;IACF,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;CACjC,CA2DA;AAED,wBAAgB,yBAAyB,CAAC,EACxC,YAAY,GACb,EAAE;IACD,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;CAChC,GAAG,IAAI,GAAG,IAAI,CAgBd;AAiFD,MAAM,MAAM,WAAW,GAAG;IACxB,4DAA4D;IAC5D,KAAK,EAAE,IAAI,EAAE,CAAA;IACb,wEAAwE;IACxE,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,4DAA4D;IAC5D,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,IAAI,WAAW,GAAG,IAAI,CAuCvD;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,IAAI,CAiB7E"}