@3driseai/3drise-viewer 0.1.30 → 0.1.31

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 (91) hide show
  1. package/dist/animationsV2/ClipPlayer.d.ts.map +1 -1
  2. package/dist/animationsV2/ClipPlayer.js +14 -12
  3. package/dist/animationsV2/ClipsRuntime.d.ts +9 -0
  4. package/dist/animationsV2/ClipsRuntime.d.ts.map +1 -1
  5. package/dist/animationsV2/ClipsRuntime.js +54 -7
  6. package/dist/animationsV2/appliers.d.ts.map +1 -1
  7. package/dist/animationsV2/appliers.js +21 -2
  8. package/dist/animationsV2/clipUtils.d.ts.map +1 -1
  9. package/dist/animationsV2/clipUtils.js +4 -0
  10. package/dist/animationsV2/sequenceUtils.js +4 -5
  11. package/dist/animationsV2/sunSystemLive.d.ts +12 -0
  12. package/dist/animationsV2/sunSystemLive.d.ts.map +1 -0
  13. package/dist/animationsV2/sunSystemLive.js +8 -0
  14. package/dist/api/sceneHandle.d.ts +3 -1
  15. package/dist/api/sceneHandle.d.ts.map +1 -1
  16. package/dist/api/sceneHandle.js +2 -2
  17. package/dist/components/AdaptiveResolution.d.ts +9 -0
  18. package/dist/components/AdaptiveResolution.d.ts.map +1 -0
  19. package/dist/components/AdaptiveResolution.js +61 -0
  20. package/dist/components/CameraController.d.ts.map +1 -1
  21. package/dist/components/CameraController.js +24 -7
  22. package/dist/components/MoonDisc.d.ts.map +1 -1
  23. package/dist/components/MoonDisc.js +2 -1
  24. package/dist/components/OceanBuilder.d.ts +4 -1
  25. package/dist/components/OceanBuilder.d.ts.map +1 -1
  26. package/dist/components/OceanBuilder.js +2 -1
  27. package/dist/components/ParticlesGenerator.d.ts.map +1 -1
  28. package/dist/components/ParticlesGenerator.js +2 -0
  29. package/dist/components/SceneBuilder.d.ts.map +1 -1
  30. package/dist/components/SceneBuilder.js +5 -2
  31. package/dist/components/Skybox.d.ts.map +1 -1
  32. package/dist/components/Skybox.js +2 -1
  33. package/dist/components/SunLight.d.ts.map +1 -1
  34. package/dist/components/SunLight.js +3 -2
  35. package/dist/components/index.d.ts +1 -0
  36. package/dist/components/index.d.ts.map +1 -1
  37. package/dist/components/index.js +1 -0
  38. package/dist/components/lightBulb/LightBulb.d.ts.map +1 -1
  39. package/dist/components/lightBulb/LightBulb.js +7 -2
  40. package/dist/components/particles/FireworksParticles.d.ts +22 -0
  41. package/dist/components/particles/FireworksParticles.d.ts.map +1 -0
  42. package/dist/components/particles/FireworksParticles.js +1649 -0
  43. package/dist/components/particles/index.d.ts +2 -0
  44. package/dist/components/particles/index.d.ts.map +1 -1
  45. package/dist/components/particles/index.js +1 -0
  46. package/dist/components/terrain/TerrainSurface.d.ts.map +1 -1
  47. package/dist/components/terrain/TerrainSurface.js +26 -4
  48. package/dist/components/water/OceanSurface.d.ts.map +1 -1
  49. package/dist/components/water/OceanSurface.js +5 -1
  50. package/dist/components/water/types.d.ts +3 -1
  51. package/dist/components/water/types.d.ts.map +1 -1
  52. package/dist/constants.d.ts +1 -1
  53. package/dist/constants.d.ts.map +1 -1
  54. package/dist/constants.js +1 -1
  55. package/dist/hooks/useOceanMaterial.d.ts +6 -2
  56. package/dist/hooks/useOceanMaterial.d.ts.map +1 -1
  57. package/dist/hooks/useOceanMaterial.js +22 -2
  58. package/dist/hooks/useOrbitTargetFollow.d.ts +10 -0
  59. package/dist/hooks/useOrbitTargetFollow.d.ts.map +1 -0
  60. package/dist/hooks/useOrbitTargetFollow.js +194 -0
  61. package/dist/index.d.ts +1 -0
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +1 -0
  64. package/dist/shaders/glsl/terrainCore.glsl.d.ts +18 -3
  65. package/dist/shaders/glsl/terrainCore.glsl.d.ts.map +1 -1
  66. package/dist/shaders/glsl/terrainCore.glsl.js +42 -21
  67. package/dist/shaders/ocean/OceanSurfaceMaterial.d.ts +36 -11
  68. package/dist/shaders/ocean/OceanSurfaceMaterial.d.ts.map +1 -1
  69. package/dist/shaders/ocean/OceanSurfaceMaterial.js +96 -5
  70. package/dist/triggers/hosts/actions.d.ts.map +1 -1
  71. package/dist/triggers/hosts/actions.js +2 -0
  72. package/dist/triggers/hosts/timelineCues.js +1 -1
  73. package/dist/triggers/kinds/change.d.ts +4 -0
  74. package/dist/triggers/kinds/change.d.ts.map +1 -0
  75. package/dist/triggers/kinds/change.js +50 -0
  76. package/dist/triggers/kinds/index.d.ts +1 -0
  77. package/dist/triggers/kinds/index.d.ts.map +1 -1
  78. package/dist/triggers/kinds/index.js +1 -0
  79. package/dist/utils/CameraSingleton.d.ts +4 -0
  80. package/dist/utils/CameraSingleton.d.ts.map +1 -1
  81. package/dist/utils/CameraSingleton.js +4 -0
  82. package/dist/utils/deviceTier.d.ts +2 -0
  83. package/dist/utils/deviceTier.d.ts.map +1 -0
  84. package/dist/utils/deviceTier.js +16 -0
  85. package/dist/utils/environmentDrawOrder.d.ts +23 -0
  86. package/dist/utils/environmentDrawOrder.d.ts.map +1 -0
  87. package/dist/utils/environmentDrawOrder.js +22 -0
  88. package/dist/utils/terrainUniforms.d.ts +7 -0
  89. package/dist/utils/terrainUniforms.d.ts.map +1 -1
  90. package/dist/utils/terrainUniforms.js +15 -2
  91. package/package.json +2 -2
@@ -20,4 +20,6 @@ export { default as ShapeBurstParticles } from './ShapeBurstParticles';
20
20
  export type { ShapeBurstParticlesProps } from './ShapeBurstParticles';
21
21
  export { default as ShapeFlowParticles } from './ShapeFlowParticles';
22
22
  export type { ShapeFlowParticlesProps } from './ShapeFlowParticles';
23
+ export { default as FireworksParticles } from './FireworksParticles';
24
+ export type { FireworksParticlesProps } from './FireworksParticles';
23
25
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/particles/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACzE,YAAY,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAC3E,YAAY,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AACvF,YAAY,EAAE,gCAAgC,EAAE,MAAM,+BAA+B,CAAC;AACtF,OAAO,EAAE,OAAO,IAAI,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACnF,YAAY,EAAE,8BAA8B,EAAE,MAAM,6BAA6B,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAC7E,YAAY,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACzE,YAAY,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAC7E,YAAY,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACnE,YAAY,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAC/E,YAAY,EAAE,4BAA4B,EAAE,MAAM,2BAA2B,CAAC;AAC9E,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACvE,YAAY,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACrE,YAAY,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/particles/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACzE,YAAY,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAC3E,YAAY,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AACvF,YAAY,EAAE,gCAAgC,EAAE,MAAM,+BAA+B,CAAC;AACtF,OAAO,EAAE,OAAO,IAAI,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACnF,YAAY,EAAE,8BAA8B,EAAE,MAAM,6BAA6B,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAC7E,YAAY,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACzE,YAAY,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAC7E,YAAY,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACnE,YAAY,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAC/E,YAAY,EAAE,4BAA4B,EAAE,MAAM,2BAA2B,CAAC;AAC9E,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AACvE,YAAY,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACrE,YAAY,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACrE,YAAY,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -10,3 +10,4 @@ export { default as NeonRainParticles } from './NeonRainParticles.js';
10
10
  export { default as MagneticBottleParticles } from './MagneticBottleParticles.js';
11
11
  export { default as ShapeBurstParticles } from './ShapeBurstParticles.js';
12
12
  export { default as ShapeFlowParticles } from './ShapeFlowParticles.js';
13
+ export { default as FireworksParticles } from './FireworksParticles.js';
@@ -1 +1 @@
1
- {"version":3,"file":"TerrainSurface.d.ts","sourceRoot":"","sources":["../../../src/components/terrain/TerrainSurface.tsx"],"names":[],"mappings":"AACA,OAAO,KAAqC,MAAM,OAAO,CAAC;AAkB1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD;;;;;;;;;;;;;;;;GAgBG;AACH,QAAA,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAkI/C,CAAC;AAEF,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"TerrainSurface.d.ts","sourceRoot":"","sources":["../../../src/components/terrain/TerrainSurface.tsx"],"names":[],"mappings":"AACA,OAAO,KAAqC,MAAM,OAAO,CAAC;AAkB1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAkBjD;;;;;;;;;;;;;;;;GAgBG;AACH,QAAA,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA6I/C,CAAC;AAEF,eAAe,cAAc,CAAC"}
@@ -11,6 +11,21 @@ import { applyTerrainAmbient, applyTerrainTextures, applyTerrainUniforms } from
11
11
  import { useTerrainTextures } from '../../hooks/useTerrainTextures.js';
12
12
  import { getAnimatableProperties } from '@3driseai/3drise-core';
13
13
  import { applyConfigKeyframeAnimations, applyConfigMouseMove, } from '../../utils/configAnimations.js';
14
+ import { TERRAIN_DRAW_ORDER, ringDrawOrder } from '../../utils/environmentDrawOrder.js';
15
+ import { isMobileDevice } from '../../utils/deviceTier.js';
16
+ /**
17
+ * Phone / tablet tier, applied at render time only — the saved scene keeps its
18
+ * numbers, so the same project is full quality on a desktop. Measured on the
19
+ * desktop: textures within 100 m instead of 1500 saved ~2.8 ms, 5 octaves
20
+ * instead of 8 ~1.7 ms; the shadow march costs nothing measurable, so it stays.
21
+ * Fewer, coarser rings cut the vertex load by roughly half.
22
+ */
23
+ const MOBILE = {
24
+ maxSegments: 96,
25
+ maxLevels: 8,
26
+ maxOctaves: 5,
27
+ maxTextureDistance: 400,
28
+ };
14
29
  /**
15
30
  * The terrain renderer. One clipmap, one material, four landforms — they differ
16
31
  * by the shaping operator the height field applies, not by having separate
@@ -29,7 +44,8 @@ import { applyConfigKeyframeAnimations, applyConfigMouseMove, } from '../../util
29
44
  * the camera. Nothing here re-renders React.
30
45
  */
31
46
  const TerrainSurface = ({ id = '3drise-terrain', config, quality, skySettings, timeSettings, sunIntensity = 1, animations, mouseMove, onObjectReady, onObjectDispose, }) => {
32
- const clipmap = useTerrainClipmap(quality.levels, quality.segments, quality.cellSize);
47
+ const mobile = isMobileDevice();
48
+ const clipmap = useTerrainClipmap(mobile ? Math.min(quality.levels, MOBILE.maxLevels) : quality.levels, mobile ? Math.min(quality.segments, MOBILE.maxSegments) : quality.segments, quality.cellSize);
33
49
  const { state: sun, update: updateSun } = useCloudSun({ skySettings, timeSettings });
34
50
  const material = useMemo(() => new TerrainMaterial(), []);
35
51
  // The four ground textures, packed into arrays. Repacks only when a map URL
@@ -46,9 +62,9 @@ const TerrainSurface = ({ id = '3drise-terrain', config, quality, skySettings, t
46
62
  useEffect(() => { liveConfig.current = {}; }, [config]);
47
63
  useEffect(() => { liveQuality.current = {}; }, [quality]);
48
64
  // The `terrain` keyframe / pointer domains: config -> keyframes -> pointer ->
49
- // live drag, the clouds' order. Only the surface's lighting and colour knobs are
50
- // in the set — the registry marks every land-shape knob non-animatable, so the
51
- // ground itself never moves.
65
+ // live drag, the clouds' order. Every numeric / colour knob is in the set — shape
66
+ // and surface alike are shader uniforms, so the ground can rise or shift per frame;
67
+ // only the integer counts (octaves, strata) are structural in the registry.
52
68
  const animatable = useMemo(() => new Set(getAnimatableProperties({ type: 'terrain', config }, 'terrain').map(p => p.value)), [config.terrainType]);
53
69
  const override = useRef({});
54
70
  const startValues = useRef({});
@@ -91,6 +107,10 @@ const TerrainSurface = ({ id = '3drise-terrain', config, quality, skySettings, t
91
107
  applyConfigMouseMove(mouseMove.terrain.properties, config, o, state.pointer, animatable);
92
108
  }
93
109
  const cfg = { ...config, ...o, ...liveConfig.current };
110
+ if (mobile) {
111
+ cfg.octaves = Math.min(cfg.octaves, MOBILE.maxOctaves);
112
+ cfg.textureDistance = Math.min(cfg.textureDistance ?? 4000, MOBILE.maxTextureDistance);
113
+ }
94
114
  const qual = { ...quality, ...liveQuality.current };
95
115
  applyTerrainUniforms(material, cfg, qual);
96
116
  applyTerrainTextures(material, cfg, textures);
@@ -110,6 +130,8 @@ const TerrainSurface = ({ id = '3drise-terrain', config, quality, skySettings, t
110
130
  }
111
131
  });
112
132
  return (_jsx("group", { ref: rootRef, children: clipmap.levels.map((ring, i) => (_jsx("mesh", { ref: (m) => { meshRefs.current[i] = m; }, geometry: ring.geometry, material: material, receiveShadow: quality.receiveShadows,
133
+ // After the water, inner ring first: see environmentDrawOrder.
134
+ renderOrder: ringDrawOrder(TERRAIN_DRAW_ORDER, ring.level),
113
135
  // Displacement happens in the vertex shader, so the object-space
114
136
  // bounds describe a flat plane. Culling against them hides the
115
137
  // terrain the moment the camera tilts up.
@@ -1 +1 @@
1
- {"version":3,"file":"OceanSurface.d.ts","sourceRoot":"","sources":["../../../src/components/water/OceanSurface.tsx"],"names":[],"mappings":"AACA,OAAO,KAAiB,MAAM,OAAO,CAAC;AAItC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,QAAA,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA2D3C,CAAC;AAEF,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"OceanSurface.d.ts","sourceRoot":"","sources":["../../../src/components/water/OceanSurface.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAiB,MAAM,OAAO,CAAC;AAItC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,QAAA,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA+D3C,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { OCEAN_DRAW_ORDER, ringDrawOrder } from '../../utils/environmentDrawOrder.js';
2
3
  import { useRef } from 'react';
3
4
  import { useObjectReady } from '../../hooks/useObjectReady.js';
4
5
  import { useClipmap } from '../../hooks/useClipmap.js';
@@ -25,7 +26,7 @@ import { useOceanMaterial } from '../../hooks/useOceanMaterial.js';
25
26
  * a sea at 0. What connects them is computed — the shoreline is where the ground
26
27
  * crosses this surface — never configured.
27
28
  */
28
- const OceanSurface = ({ id = '3drise-water', config, quality, skySettings, timeSettings, sunIntensity = 1, animations, mouseMove, onObjectReady, onObjectDispose, }) => {
29
+ const OceanSurface = ({ id = '3drise-water', config, quality, skySettings, timeSettings, sunIntensity = 1, animations, mouseMove, terrain, onObjectReady, onObjectDispose, }) => {
29
30
  const clipmap = useClipmap(quality.levels, quality.segments, quality.cellSize);
30
31
  const meshRefs = useRef([]);
31
32
  const { material, rootRef } = useOceanMaterial({
@@ -37,6 +38,7 @@ const OceanSurface = ({ id = '3drise-water', config, quality, skySettings, timeS
37
38
  sunIntensity,
38
39
  animations,
39
40
  mouseMove,
41
+ terrain,
40
42
  onFrame: (cfg, _qual, state) => {
41
43
  // ONE origin for the whole pyramid, snapped to twice the finest cell.
42
44
  //
@@ -54,6 +56,8 @@ const OceanSurface = ({ id = '3drise-water', config, quality, skySettings, timeS
54
56
  });
55
57
  useObjectReady(rootRef, { id, type: 'ocean', config, quality }, onObjectReady, onObjectDispose);
56
58
  return (_jsx("group", { ref: rootRef, children: clipmap.levels.map((ring, i) => (_jsx("mesh", { ref: (m) => { meshRefs.current[i] = m; }, geometry: ring.geometry, material: material, receiveShadow: quality.receiveShadows,
59
+ // Before the terrain, inner ring first: see environmentDrawOrder.
60
+ renderOrder: ringDrawOrder(OCEAN_DRAW_ORDER, ring.level),
57
61
  // Displacement happens in the vertex shader, so the
58
62
  // object-space bounds describe a flat plane. Culling against
59
63
  // them hides the water the moment the camera tilts up.
@@ -1,6 +1,6 @@
1
1
  import type { SkySettings, TimeSettings } from '@3driseai/3drise-core';
2
2
  import type { ObjectReadyProps } from '../../types/objectReady';
3
- import type { OceanConfig, OceanMeshSettings, OceanQualityConfig, ObjectAnimations, MouseMoveInteractions } from '@3driseai/3drise-core';
3
+ import type { OceanConfig, OceanMeshSettings, OceanQualityConfig, ObjectAnimations, MouseMoveInteractions, TerrainConfig } from '@3driseai/3drise-core';
4
4
  /** Shared by every water type. The builder hands all four the same shape. */
5
5
  export interface OceanLayerProps extends ObjectReadyProps {
6
6
  /** Scene id, so a controller can findById() the water and drive it live. */
@@ -20,5 +20,7 @@ export interface OceanLayerProps extends ObjectReadyProps {
20
20
  animations?: ObjectAnimations;
21
21
  /** Pointer bindings; same `ocean` domain. */
22
22
  mouseMove?: MouseMoveInteractions;
23
+ /** The environment terrain, for the world ocean's shoreline and terrain shadow. */
24
+ terrain?: TerrainConfig | null;
23
25
  }
24
26
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/water/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAEzI,6EAA6E;AAC7E,MAAM,WAAW,eAAgB,SAAQ,gBAAgB;IACrD,4EAA4E;IAC5E,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,kBAAkB,CAAC;IAC5B;;4DAEwD;IACxD,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sDAAsD;IACtD,YAAY,CAAC,EAAE,iBAAiB,CAAC;IACjC,mEAAmE;IACnE,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,6CAA6C;IAC7C,SAAS,CAAC,EAAE,qBAAqB,CAAC;CACrC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/water/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAExJ,6EAA6E;AAC7E,MAAM,WAAW,eAAgB,SAAQ,gBAAgB;IACrD,4EAA4E;IAC5E,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,kBAAkB,CAAC;IAC5B;;4DAEwD;IACxD,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sDAAsD;IACtD,YAAY,CAAC,EAAE,iBAAiB,CAAC;IACjC,mEAAmE;IACnE,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,6CAA6C;IAC7C,SAAS,CAAC,EAAE,qBAAqB,CAAC;IAClC,mFAAmF;IACnF,OAAO,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;CAClC"}
@@ -1,3 +1,3 @@
1
- export declare const dataCenterServer = "https://3drise.ai/api/data/";
1
+ export declare const dataCenterServer = "http://localhost:3001/api/data/";
2
2
  export declare const SELF_MANAGED_MATERIAL: Set<string>;
3
3
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,gBAAgB,gCAAgC,CAAC;AAC9D,eAAO,MAAM,qBAAqB,aAqBhC,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,gBAAgB,oCAA2B,CAAC;AACzD,eAAO,MAAM,qBAAqB,aAqBhC,CAAC"}
package/dist/constants.js CHANGED
@@ -3,7 +3,7 @@ const PRODUCTION_DATA_CENTER_SERVER = 'https://3drise.ai/api/data/';
3
3
  const isLocalHostRuntime = typeof window !== 'undefined' &&
4
4
  (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1');
5
5
  // export const dataCenterServer = isLocalHostRuntime ? LOCAL_DATA_CENTER_SERVER : PRODUCTION_DATA_CENTER_SERVER;
6
- export const dataCenterServer = PRODUCTION_DATA_CENTER_SERVER;
6
+ export const dataCenterServer = LOCAL_DATA_CENTER_SERVER;
7
7
  export const SELF_MANAGED_MATERIAL = new Set([
8
8
  // The landscape owns its shader: splat arrays, height blending and the
9
9
  // heightmap displacement are the material. useMaterialApplication would
@@ -2,7 +2,7 @@ import * as THREE from 'three';
2
2
  import { type RootState } from '@react-three/fiber';
3
3
  import type { SkySettings, TimeSettings } from '@3driseai/3drise-core';
4
4
  import { OceanSurfaceMaterial } from '../shaders/ocean/OceanSurfaceMaterial';
5
- import type { OceanConfig, OceanQualityConfig, ObjectAnimations, MouseMoveInteractions } from '@3driseai/3drise-core';
5
+ import type { OceanConfig, OceanQualityConfig, ObjectAnimations, MouseMoveInteractions, TerrainConfig } from '@3driseai/3drise-core';
6
6
  /**
7
7
  * Everything the two water hosts share: the material, the live-edit handle, and
8
8
  * the per-frame uniform push.
@@ -27,6 +27,10 @@ export interface UseOceanMaterialOptions {
27
27
  animations?: ObjectAnimations;
28
28
  /** Pointer bindings; same `ocean` domain. */
29
29
  mouseMove?: MouseMoveInteractions;
30
+ /** The environment terrain's config, when it is on: the water reads the ground
31
+ * under it for the shoreline (waves shoal, shallow colour, shore foam) and
32
+ * marches the terrain's shadow. World host only. */
33
+ terrain?: TerrainConfig | null;
30
34
  /** Runs after the uniforms are pushed, with the live overrides already
31
35
  * merged in — where a host does its own per-frame work (snapping the
32
36
  * clipmap to the camera, say) with the config it is actually rendering. */
@@ -38,6 +42,6 @@ export interface OceanMaterialHandle {
38
42
  * handle a controller reaches through `findById`. */
39
43
  rootRef: React.RefObject<THREE.Group | null>;
40
44
  }
41
- export declare function useOceanMaterial({ id, config, quality, skySettings, timeSettings, sunIntensity, animations, mouseMove, onFrame, }: UseOceanMaterialOptions): OceanMaterialHandle;
45
+ export declare function useOceanMaterial({ id, config, quality, skySettings, timeSettings, sunIntensity, animations, mouseMove, terrain, onFrame, }: UseOceanMaterialOptions): OceanMaterialHandle;
42
46
  export default useOceanMaterial;
43
47
  //# sourceMappingURL=useOceanMaterial.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useOceanMaterial.d.ts","sourceRoot":"","sources":["../../src/hooks/useOceanMaterial.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAU7E,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAStH;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,uBAAuB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,6CAA6C;IAC7C,SAAS,CAAC,EAAE,qBAAqB,CAAC;IAClC;;gFAE4E;IAC5E,OAAO,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,kBAAkB,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;CACtF;AAED,MAAM,WAAW,mBAAmB;IAChC,QAAQ,EAAE,oBAAoB,CAAC;IAC/B;0DACsD;IACtD,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;CAChD;AAED,wBAAgB,gBAAgB,CAAC,EAC7B,EAAE,EACF,MAAM,EACN,OAAO,EACP,WAAW,EACX,YAAY,EACZ,YAAgB,EAChB,UAAU,EACV,SAAS,EACT,OAAO,GACV,EAAE,uBAAuB,GAAG,mBAAmB,CAuG/C;AAED,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"useOceanMaterial.d.ts","sourceRoot":"","sources":["../../src/hooks/useOceanMaterial.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAU7E,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AASrI;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,uBAAuB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,kBAAkB,CAAC;IAC5B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,6CAA6C;IAC7C,SAAS,CAAC,EAAE,qBAAqB,CAAC;IAClC;;yDAEqD;IACrD,OAAO,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IAC/B;;gFAE4E;IAC5E,OAAO,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,kBAAkB,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;CACtF;AAED,MAAM,WAAW,mBAAmB;IAChC,QAAQ,EAAE,oBAAoB,CAAC;IAC/B;0DACsD;IACtD,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;CAChD;AAED,wBAAgB,gBAAgB,CAAC,EAC7B,EAAE,EACF,MAAM,EACN,OAAO,EACP,WAAW,EACX,YAAY,EACZ,YAAgB,EAChB,UAAU,EACV,SAAS,EACT,OAAO,EACP,OAAO,GACV,EAAE,uBAAuB,GAAG,mBAAmB,CA0H/C;AAED,eAAe,gBAAgB,CAAC"}
@@ -5,7 +5,7 @@ import { useCloudSun } from './useCloudSun.js';
5
5
  import { OCEAN_LIVE_KEY } from './useOceanLive.js';
6
6
  import { OceanSurfaceMaterial } from '../shaders/ocean/OceanSurfaceMaterial.js';
7
7
  import { applySunUniforms } from '../utils/cloudUniforms.js';
8
- import { applyTerrainAmbient } from '../utils/terrainUniforms.js';
8
+ import { applyTerrainAmbient, applyTerrainFieldUniforms } from '../utils/terrainUniforms.js';
9
9
  import { applyOceanUniforms } from '../utils/oceanUniforms.js';
10
10
  import { resolveMoon, moonlightFactor, MOON_LIGHT_COLOR } from '../utils/skyMoon.js';
11
11
  /** Moonlight on the water relative to the sun's (sunIntensity 1). The key light
@@ -14,7 +14,7 @@ import { resolveMoon, moonlightFactor, MOON_LIGHT_COLOR } from '../utils/skyMoon
14
14
  const MOON_WATER_STRENGTH = 0.15;
15
15
  import { getAnimatableProperties } from '@3driseai/3drise-core';
16
16
  import { applyConfigKeyframeAnimations, applyConfigMouseMove, } from '../utils/configAnimations.js';
17
- export function useOceanMaterial({ id, config, quality, skySettings, timeSettings, sunIntensity = 1, animations, mouseMove, onFrame, }) {
17
+ export function useOceanMaterial({ id, config, quality, skySettings, timeSettings, sunIntensity = 1, animations, mouseMove, terrain, onFrame, }) {
18
18
  const { state: sun, update: updateSun } = useCloudSun({ skySettings, timeSettings });
19
19
  const moon = useRef({ direction: new THREE.Vector3(), illumination: 0 });
20
20
  const material = useMemo(() => new OceanSurfaceMaterial(), []);
@@ -82,6 +82,26 @@ export function useOceanMaterial({ id, config, quality, skySettings, timeSetting
82
82
  const cfg = { ...config, ...o, ...liveConfig.current };
83
83
  const qual = { ...quality, ...liveQuality.current };
84
84
  applyOceanUniforms(shader, cfg, qual);
85
+ // The shoreline. OCEAN_TERRAIN compiles the terrain field into the water;
86
+ // it flips only when terrain is switched on or off, never per frame.
87
+ const withTerrain = !!terrain?.enabled;
88
+ const defines = (shader.defines ??= {});
89
+ if (withTerrain !== ('OCEAN_TERRAIN' in defines)) {
90
+ if (withTerrain)
91
+ defines.OCEAN_TERRAIN = '';
92
+ else
93
+ delete defines.OCEAN_TERRAIN;
94
+ shader.needsUpdate = true;
95
+ }
96
+ const u = shader.uniforms;
97
+ u.u_shoreDepth.value = Math.max(cfg.shoreDepth ?? 3, 0.05);
98
+ u.u_shoreFoam.value = cfg.shoreFoam ?? 1;
99
+ if (withTerrain && terrain) {
100
+ applyTerrainFieldUniforms(shader, terrain);
101
+ u.u_sunShadow.value = cfg.terrainShadow ?? 1;
102
+ u.u_shadowSteps.value = Math.round(qual.terrainShadowSteps ?? 10);
103
+ u.u_shadowDistance.value = Math.max(qual.terrainShadowDistance ?? 4000, 1);
104
+ }
85
105
  shader.uniforms.u_time.value = state.clock.getElapsedTime();
86
106
  // World size of one pixel at unit distance. The detail fade and the
87
107
  // specular filtering are both screen-space decisions — whether a ripple
@@ -0,0 +1,10 @@
1
+ import type { ControlsConfig, OrbitTargetObject } from '@3driseai/3drise-core';
2
+ export declare function useOrbitTargetFollow(controlsRef: React.MutableRefObject<any>, targetObject: OrbitTargetObject | null | undefined, followObject: OrbitTargetObject | null | undefined,
3
+ /** True while something else owns the camera position (a camera animation). */
4
+ isCameraBusy: () => boolean,
5
+ /** False when the orbit controls are switched off: the camera keeps its own orientation. */
6
+ orbiting: boolean,
7
+ /** The authored controls — their limits are what the widening falls back to. */
8
+ authored: ControlsConfig): void;
9
+ export default useOrbitTargetFollow;
10
+ //# sourceMappingURL=useOrbitTargetFollow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useOrbitTargetFollow.d.ts","sourceRoot":"","sources":["../../src/hooks/useOrbitTargetFollow.ts"],"names":[],"mappings":"AA+CA,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAsE/E,wBAAgB,oBAAoB,CAChC,WAAW,EAAE,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,EACxC,YAAY,EAAE,iBAAiB,GAAG,IAAI,GAAG,SAAS,EAClD,YAAY,EAAE,iBAAiB,GAAG,IAAI,GAAG,SAAS;AAClD,+EAA+E;AAC/E,YAAY,EAAE,MAAM,OAAO;AAC3B,4FAA4F;AAC5F,QAAQ,EAAE,OAAO;AACjB,gFAAgF;AAChF,QAAQ,EAAE,cAAc,QAqE3B;AAED,eAAe,oBAAoB,CAAC"}
@@ -0,0 +1,194 @@
1
+ // =============================================================================
2
+ // useOrbitTargetFollow — the camera tracks scene objects
3
+ // =============================================================================
4
+ //
5
+ // Two independent settings on `controls`:
6
+ //
7
+ // targetObject ORBIT TARGET — the orbit point (what the camera looks at) sits on the
8
+ // object's centre every frame. The camera position is not touched; it
9
+ // only turns to keep looking at the object.
10
+ // followObject FOLLOW — the camera travels with the object: it (and the orbit point)
11
+ // moves by exactly as much as the object moved, so the distance and
12
+ // angle stay the same. Translation only — the object spinning in place
13
+ // does not swing the camera around.
14
+ //
15
+ // Both together: the camera rides along with one object and looks at another (or the
16
+ // same one).
17
+ //
18
+ // Smoothness — why this runs in scene.onBeforeRender and not in useFrame:
19
+ // clips, actions, effects and physics move objects in useFrame callbacks, some of them
20
+ // after this component's. Reading the object in useFrame would see last frame's position
21
+ // half the time, so the camera would trail the object by a frame and stutter. Three calls
22
+ // scene.onBeforeRender inside gl.render, after it has updated every matrixWorld and before
23
+ // it builds the view — so here every object is exactly where it is drawn this frame. We
24
+ // move the camera, re-aim it and update its matrix ourselves, and this frame's image is
25
+ // already right.
26
+ //
27
+ // The object's centre is its bounding-box centre, measured once in the object's own space
28
+ // when it is found and then carried by its matrixWorld every frame — exact under animation
29
+ // and cheap (no per-frame box).
30
+ //
31
+ // Limits vs a moving target: OrbitControls clamps the camera's distance and angles to
32
+ // the target on every update. When the TARGET moves (the plane climbs above a camera
33
+ // limited to 88°), that clamp would drag the camera along — the camera must not move.
34
+ // So while looking at an object, the limits are widened each frame just enough to hold
35
+ // where the camera is now: the object can take the view outside the authored range,
36
+ // the user still can't rotate / zoom further out of it, and moving back in tightens
37
+ // them to the authored values again. The authored values come from `controls`.
38
+ //
39
+ // Height limits (controls.minHeight / maxHeight) are world Y, not relative to the target,
40
+ // and are applied after everything else in the frame.
41
+ //
42
+ // While a camera animation (path / gsap) runs, follow does not move the camera — the
43
+ // animation owns its position; the follow just keeps counting, so nothing jumps after.
44
+ //
45
+ import { useEffect, useRef } from 'react';
46
+ import { useFrame, useThree } from '@react-three/fiber';
47
+ import { Box3, Vector3 } from 'three';
48
+ import { orbitAngleLimits } from '@3driseai/3drise-core';
49
+ import { findById } from '../utils/sceneQuery.js';
50
+ const RESOLVE_EVERY = 30; // frames between lookups while the object is missing
51
+ const _box = new Box3();
52
+ const _p = new Vector3();
53
+ const _d = new Vector3();
54
+ const _off = new Vector3();
55
+ const keyOf = (t) => (t?.id ? `${t.id}|${t.node ?? ''}` : '');
56
+ /** Finds one object and gives its centre in world space, every frame, without lag. */
57
+ class Tracker {
58
+ key = '';
59
+ obj = null;
60
+ local = new Vector3(); // bounding-box centre in the object's own space
61
+ missingSince = -1e9;
62
+ locked = false; // first point taken
63
+ last = new Vector3(); // point at the previous frame
64
+ sync(t) {
65
+ const key = keyOf(t);
66
+ if (key === this.key)
67
+ return;
68
+ this.key = key;
69
+ this.obj = null;
70
+ this.missingSince = -1e9;
71
+ this.locked = false;
72
+ }
73
+ /** Writes the object's centre into `out`; false while the object is not there. */
74
+ point(t, frame, out) {
75
+ if (!this.obj || !this.obj.parent) {
76
+ if (frame - this.missingSince < RESOLVE_EVERY)
77
+ return false;
78
+ const root = findById(t.id);
79
+ const obj = root && t.node ? root.getObjectByName(t.node) ?? null : root;
80
+ if (!obj) {
81
+ this.missingSince = frame;
82
+ this.obj = null;
83
+ return false;
84
+ }
85
+ this.obj = obj;
86
+ this.locked = false;
87
+ obj.updateWorldMatrix(true, true);
88
+ _box.setFromObject(obj);
89
+ if (_box.isEmpty())
90
+ this.local.set(0, 0, 0);
91
+ else
92
+ obj.worldToLocal(_box.getCenter(this.local));
93
+ }
94
+ out.copy(this.local).applyMatrix4(this.obj.matrixWorld);
95
+ return true;
96
+ }
97
+ }
98
+ /** Authored limits, widened to include the camera's current distance and angles to the target. */
99
+ function widenLimits(controls, cameraPos, auth) {
100
+ _off.copy(cameraPos).sub(controls.target);
101
+ const r = _off.length();
102
+ if (r < 1e-6)
103
+ return;
104
+ const phi = Math.acos(Math.min(1, Math.max(-1, _off.y / r)));
105
+ const theta = Math.atan2(_off.x, _off.z);
106
+ const lim = orbitAngleLimits(auth);
107
+ const minD = auth.minDistance ?? 0, maxD = auth.maxDistance ?? Infinity;
108
+ controls.minDistance = Math.min(minD, r);
109
+ controls.maxDistance = Math.max(maxD, r);
110
+ controls.minPolarAngle = Math.min(lim.minPolarAngle, phi);
111
+ controls.maxPolarAngle = Math.max(lim.maxPolarAngle, phi);
112
+ // Azimuth: only when it is limited (the full circle needs nothing).
113
+ if (lim.maxAzimuthAngle - lim.minAzimuthAngle < Math.PI * 2 - 1e-6) {
114
+ controls.minAzimuthAngle = Math.min(lim.minAzimuthAngle, theta);
115
+ controls.maxAzimuthAngle = Math.max(lim.maxAzimuthAngle, theta);
116
+ }
117
+ }
118
+ export function useOrbitTargetFollow(controlsRef, targetObject, followObject,
119
+ /** True while something else owns the camera position (a camera animation). */
120
+ isCameraBusy,
121
+ /** False when the orbit controls are switched off: the camera keeps its own orientation. */
122
+ orbiting,
123
+ /** The authored controls — their limits are what the widening falls back to. */
124
+ authored) {
125
+ const { scene, camera } = useThree();
126
+ const live = useRef({ targetObject, followObject, isCameraBusy, orbiting, authored });
127
+ live.current = { targetObject, followObject, isCameraBusy, orbiting, authored };
128
+ const frame = useRef(0);
129
+ const done = useRef(-1);
130
+ const aim = useRef(new Tracker());
131
+ const follow = useRef(new Tracker());
132
+ // A frame counter, so the render hook runs once per frame even if the scene is
133
+ // rendered more than once (reflections, previews).
134
+ useFrame(() => { frame.current++; });
135
+ useEffect(() => {
136
+ const prev = scene.onBeforeRender;
137
+ scene.onBeforeRender = function (...args) {
138
+ prev.apply(this, args);
139
+ const cam = args[2];
140
+ if (cam !== camera || done.current === frame.current)
141
+ return;
142
+ done.current = frame.current;
143
+ const controls = controlsRef.current;
144
+ const { targetObject: tObj, followObject: fObj, isCameraBusy: busy, orbiting: orb, authored: auth } = live.current;
145
+ const f = follow.current, a = aim.current;
146
+ f.sync(fObj);
147
+ a.sync(tObj);
148
+ let moved = false;
149
+ // FOLLOW: move the camera and the orbit point with the object.
150
+ if (fObj && f.point(fObj, frame.current, _p)) {
151
+ if (!f.locked) {
152
+ f.last.copy(_p);
153
+ f.locked = true;
154
+ }
155
+ _d.copy(_p).sub(f.last);
156
+ f.last.copy(_p);
157
+ if (_d.lengthSq() > 0 && !busy()) {
158
+ camera.position.add(_d);
159
+ if (controls)
160
+ controls.target.add(_d);
161
+ moved = true;
162
+ }
163
+ }
164
+ // ORBIT TARGET: look at the object; the camera stays where it is.
165
+ if (tObj && controls && a.point(tObj, frame.current, _p)) {
166
+ if (!a.locked || !controls.target.equals(_p)) {
167
+ controls.target.copy(_p);
168
+ if (orb)
169
+ camera.lookAt(_p);
170
+ a.locked = true;
171
+ moved = true;
172
+ }
173
+ }
174
+ // HEIGHT LIMITS (world Y): last, so nothing — user, target, follow, a clip — ends
175
+ // the frame under the floor or over the ceiling. The camera keeps facing the target.
176
+ const minH = typeof auth.minHeight === 'number' ? auth.minHeight : -Infinity;
177
+ const maxH = typeof auth.maxHeight === 'number' ? auth.maxHeight : Infinity;
178
+ const y = Math.min(maxH, Math.max(minH, camera.position.y));
179
+ if (y !== camera.position.y) {
180
+ camera.position.y = y;
181
+ if (orb && controls)
182
+ camera.lookAt(controls.target);
183
+ moved = true;
184
+ }
185
+ if (moved)
186
+ camera.updateMatrixWorld();
187
+ // Limits hold the camera where the moving target left it (see the header).
188
+ if (tObj && controls && a.locked)
189
+ widenLimits(controls, camera.position, auth);
190
+ };
191
+ return () => { scene.onBeforeRender = prev; };
192
+ }, [scene, camera, controlsRef]);
193
+ }
194
+ export default useOrbitTargetFollow;
package/dist/index.d.ts CHANGED
@@ -25,6 +25,7 @@ export type { Object3DRefLike, Object3DSource } from './utils/resolveObject3D';
25
25
  export { applyTerrainUniforms, applyTerrainTextures } from './utils/terrainUniforms';
26
26
  export { useTerrainTextures } from './hooks/useTerrainTextures';
27
27
  export { terrainHeightAt } from './utils/terrainHeight';
28
+ export { isMobileDevice } from './utils/deviceTier';
28
29
  export { setTerrainTextureResolver, resolveTerrainTextureSets, useTerrainTextureResolverVersion, type TerrainTextureResolver } from './utils/terrainTextureResolver';
29
30
  export { landHeightAt, landWorldHeightAt, landSeedOffset, landToTerrainConfig, LAND_SEGMENTS } from './utils/landPatch';
30
31
  export type { TerrainTextureState } from './hooks/useTerrainTextures';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGtD,OAAO,EAAE,iBAAiB,IAAI,iBAAiB,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAG5F,cAAc,SAAS,CAAC;AAGxB,cAAc,cAAc,CAAC;AAG7B,cAAc,SAAS,CAAC;AAExB,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,MAAM,CAAC;AACrB,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,eAAe,EAAE,aAAa,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAE7N,YAAY,EAAE,oBAAoB,IAAI,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAGjG,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,iBAAiB,EAAE,WAAW,GAAG,MAAM,iBAAiB,CAAC;AACrG,OAAO,EAAE,mBAAmB,IAAI,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AACvF,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAC7F,YAAY,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC/E,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,yBAAyB,EAAE,yBAAyB,EAAE,gCAAgC,EAAE,KAAK,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACrK,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACxH,YAAY,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACvF,YAAY,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EACL,oBAAoB,EACpB,4BAA4B,EAC5B,qBAAqB,EACrB,eAAe,EACf,8BAA8B,GAC/B,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,sBAAsB,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpG,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnF,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC1F,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACrF,YAAY,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,6BAA6B,EAC7B,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EACL,6BAA6B,EAC7B,oBAAoB,EACpB,kBAAkB,EAClB,aAAa,EACb,kBAAkB,GACnB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAGlF,OAAO,EACL,sBAAsB,EACtB,aAAa,EACb,sBAAsB,EACtB,oBAAoB,EACpB,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,kCAAkC,CAAC;AAC1C,YAAY,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAC5F,OAAO,EACL,cAAc,EACd,wBAAwB,EACxB,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EACL,aAAa,EACb,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,sBAAsB,EACtB,oBAAoB,EACpB,UAAU,EACV,eAAe,EACf,aAAa,GACd,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EACL,wBAAwB,EACxB,yBAAyB,EACzB,gBAAgB,EAChB,sBAAsB,EACtB,4BAA4B,GAC7B,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACV,sBAAsB,EACtB,eAAe,EACf,uBAAuB,GACxB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,iBAAiB,EACjB,gBAAgB,EAChB,yBAAyB,EACzB,eAAe,EACf,kBAAkB,EAClB,yBAAyB,EACzB,cAAc,EACd,eAAe,EACf,oBAAoB,GACrB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EACV,mBAAmB,EACnB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,yBAAyB,EACzB,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,+BAA+B,CAAC;AACvC,YAAY,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -26,6 +26,7 @@ export { resolveObject3D } from './utils/resolveObject3D.js';
26
26
  export { applyTerrainUniforms, applyTerrainTextures } from './utils/terrainUniforms.js';
27
27
  export { useTerrainTextures } from './hooks/useTerrainTextures.js';
28
28
  export { terrainHeightAt } from './utils/terrainHeight.js';
29
+ export { isMobileDevice } from './utils/deviceTier.js';
29
30
  export { setTerrainTextureResolver, resolveTerrainTextureSets, useTerrainTextureResolverVersion } from './utils/terrainTextureResolver.js';
30
31
  export { landHeightAt, landWorldHeightAt, landSeedOffset, landToTerrainConfig, LAND_SEGMENTS } from './utils/landPatch.js';
31
32
  export { applyLandscapeMaps, applyLandscapeUniforms } from './utils/landscapeUniforms.js';