@3driseai/3drise-viewer 0.1.13 → 0.1.15

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.
@@ -1 +1 @@
1
- {"version":3,"file":"Frame.d.ts","sourceRoot":"","sources":["../../src/components/Frame.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAI7D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEtC,OAAO,KAAK,EAAE,qBAAqB,EAAE,aAAa,EAAE,qBAAqB,EAAE,eAAe,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzJ,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAKtD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,YAAY,EAAE,cAAc,EAAE,CAAC;AAE/B,MAAM,WAAW,UAAW,SAAQ,gBAAgB;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,OAAO,CAAC;IAClB,aAAa,EAAE,qBAAqB,CAAC;IACrC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,aAAa,EAAE,aAAa,CAAC;IAC7B,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAC;IACrC,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;IAC7B,cAAc,EAAE,qBAAqB,CAAC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,iBAAiB,CAAC,EAAE,CAAC,aAAa,EAAE,qBAAqB,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;CACpH;AAGD,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAoB,EAAE,aAAa,EAAE,gBAAqB,EAAE,YAAiB,EAAE,cAAc,EAAE,IAAI,EAAE,gBAAgB,EAAE,QAAQ,EAAE,iBAAiB,EAAE,aAAa,EAAE,eAAe,GAAG,EAAE,UAAU,+BA2ClR"}
1
+ {"version":3,"file":"Frame.d.ts","sourceRoot":"","sources":["../../src/components/Frame.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAI7D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEtC,OAAO,KAAK,EAAE,qBAAqB,EAAE,aAAa,EAAE,qBAAqB,EAAE,eAAe,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzJ,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAKtD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,YAAY,EAAE,cAAc,EAAE,CAAC;AAE/B,MAAM,WAAW,UAAW,SAAQ,gBAAgB;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,OAAO,CAAC;IAClB,aAAa,EAAE,qBAAqB,CAAC;IACrC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,aAAa,EAAE,aAAa,CAAC;IAC7B,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAC;IACrC,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC;IAC7B,cAAc,EAAE,qBAAqB,CAAC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,iBAAiB,CAAC,EAAE,CAAC,aAAa,EAAE,qBAAqB,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;CACpH;AAGD,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAoB,EAAE,aAAa,EAAE,gBAAqB,EAAE,YAAiB,EAAE,cAAc,EAAE,IAAI,EAAE,gBAAgB,EAAE,QAAQ,EAAE,iBAAiB,EAAE,aAAa,EAAE,eAAe,GAAG,EAAE,UAAU,+BAqDlR"}
@@ -33,7 +33,8 @@ export default function Frame({ url, isActive, createdObject, position, rotation
33
33
  useCursor(hovered);
34
34
  const enabledEffects = transformEffects.filter(e => e.enabled !== false);
35
35
  const animateMaterial = enabledEffects.length > 0 || colorEffects.some(e => e.enabled !== false);
36
- useObjectAnimation(createdObject, animateMaterial, { enabled: true, hovered }, frameRef);
36
+ // console.log(isActive)
37
+ useObjectAnimation(createdObject, animateMaterial, { enabled: !isActive, hovered }, frameRef);
37
38
  const scale = [
38
39
  0.9 * (layoutSettings.width || 1),
39
40
  0.9 * (layoutSettings.height || 1),
@@ -50,7 +51,8 @@ export default function Frame({ url, isActive, createdObject, position, rotation
50
51
  };
51
52
  if (!texture)
52
53
  return null;
53
- return (_jsx("mesh", { name: url, ref: frameRef, position: position, rotation: rotation, scale: scale, raycast: () => null, onPointerEnter: handlePointerEnter, onPointerLeave: handlePointerLeave, onClick: onClick, userData: {
54
+ return (_jsx("mesh", { name: url, ref: frameRef, position: position, rotation: rotation, scale: scale, onPointerEnter: handlePointerEnter, onPointerLeave: handlePointerLeave, onClick: onClick, userData: {
54
55
  url,
55
- }, children: children ? children : (_jsxs(_Fragment, { children: [_jsx(FrameGeometry, { shapeType: 'rectangle', layoutSettings: layoutSettings, scale: layoutSettings.outerFrameScale }), _jsx(MaterialLibrary, { materialSettings: materialSettings }), showFront && (_jsx(FrameImage, { ref: imageRef, texture: texture, shapeType: 'rectangle', position: [0, 0, 0.02] })), showBack && (_jsx(FrameImage, { texture: texture, shapeType: 'rectangle', position: [0, 0, -0.02], rotation: [0, Math.PI, 0], isBack: true }))] })) }));
56
+ }, children: children ? children :
57
+ (_jsxs(_Fragment, { children: [_jsx(FrameGeometry, { shapeType: 'rectangle', layoutSettings: layoutSettings, scale: layoutSettings.outerFrameScale }), _jsx(MaterialLibrary, { materialSettings: materialSettings }), showFront && (_jsx(FrameImage, { ref: imageRef, texture: texture, shapeType: 'rectangle', position: [0, 0, 0.02] })), showBack && (_jsx(FrameImage, { texture: texture, shapeType: 'rectangle', position: [0, 0, -0.02], rotation: [0, Math.PI, 0], isBack: true }))] })) }));
56
58
  }
@@ -1 +1 @@
1
- {"version":3,"file":"FrameImage.d.ts","sourceRoot":"","sources":["../../src/components/FrameImage.tsx"],"names":[],"mappings":"AAeA,eAAO,MAAM,UAAU,EAUjB,GAAI,CAAC"}
1
+ {"version":3,"file":"FrameImage.d.ts","sourceRoot":"","sources":["../../src/components/FrameImage.tsx"],"names":[],"mappings":"AAgBA,eAAO,MAAM,UAAU,EAYjB,GAAI,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { forwardRef } from "react";
3
+ import { IMAGE_KEY } from "@3driseai/3drise-core";
3
4
  import { FrameGeometry } from "./FrameGeometry.js";
4
5
  export const FrameImage = forwardRef(({ texture, shapeType, position, rotation, isBack = false, layoutSettings, }, ref) => {
5
6
  const width = layoutSettings?.width || 1;
@@ -10,6 +11,6 @@ export const FrameImage = forwardRef(({ texture, shapeType, position, rotation,
10
11
  width: width * imageScale,
11
12
  height: height * imageScale,
12
13
  };
13
- return (_jsxs("mesh", { ref: ref, raycast: () => null, position: position, rotation: rotation, children: [_jsx(FrameGeometry, { shapeType: shapeType, layoutSettings: scaledSettings }), _jsx("meshBasicMaterial", { map: texture, toneMapped: false, transparent: true })] }));
14
+ return (_jsxs("mesh", { ref: ref, raycast: () => null, userData: { [IMAGE_KEY]: true }, position: position, rotation: rotation, children: [_jsx(FrameGeometry, { shapeType: shapeType, layoutSettings: scaledSettings }), _jsx("meshBasicMaterial", { map: texture, toneMapped: false, transparent: true })] }));
14
15
  });
15
16
  FrameImage.displayName = 'FrameImage';
@@ -5,7 +5,6 @@ import { Object3D } from "three";
5
5
  import { type ThreeEvent } from "@react-three/fiber";
6
6
  export interface GalleryLayoutProps extends ObjectReadyProps {
7
7
  createdObject: CreatedObjectSettings;
8
- selectedFrameUrl?: string;
9
8
  handleObjectClick?: (createdObject: CreatedObjectSettings, ref: Object3D, event: ThreeEvent<MouseEvent>) => void;
10
9
  }
11
10
  declare const GalleryLayout: React.FC<GalleryLayoutProps>;
@@ -1 +1 @@
1
- {"version":3,"file":"GalleryLayout.d.ts","sourceRoot":"","sources":["../../src/components/GalleryLayout.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAG7D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAGnE,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,MAAM,WAAW,kBAAmB,SAAQ,gBAAgB;IACxD,aAAa,EAAE,qBAAqB,CAAC;IACrC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,CAAC,aAAa,EAAE,qBAAqB,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;CACpH;AAGD,QAAA,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAW/C,CAAC;AACF,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"GalleryLayout.d.ts","sourceRoot":"","sources":["../../src/components/GalleryLayout.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAG7D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAGnE,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,MAAM,WAAW,kBAAmB,SAAQ,gBAAgB;IACxD,aAAa,EAAE,qBAAqB,CAAC;IAErC,iBAAiB,CAAC,EAAE,CAAC,aAAa,EAAE,qBAAqB,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;CACpH;AAGD,QAAA,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAiC/C,CAAC;AACF,eAAe,aAAa,CAAC"}
@@ -1,10 +1,11 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useObjectReady } from '../hooks/useObjectReady.js';
3
- import { useMemo, useRef } from 'react';
3
+ import { useMemo, useRef, useState } from 'react';
4
4
  import { updateCurrentLayoutSettings } from '@3driseai/3drise-core';
5
5
  import Frame from './Frame.js';
6
6
  import { ImageGeometry } from './ImageGeometry.js';
7
- const GalleryLayout = ({ createdObject, selectedFrameUrl, handleObjectClick, onObjectReady, onObjectDispose, }) => {
7
+ const GalleryLayout = ({ createdObject, handleObjectClick, onObjectReady, onObjectDispose, }) => {
8
+ const [selectedFrameUrl, setSelectedFrameUrl] = useState(undefined);
8
9
  const groupRef = useRef(null);
9
10
  useObjectReady(groupRef, createdObject, onObjectReady, onObjectDispose);
10
11
  const { config, materialSettings, animations } = createdObject;
@@ -14,6 +15,9 @@ const GalleryLayout = ({ createdObject, selectedFrameUrl, handleObjectClick, onO
14
15
  outerFrameScale: 1,
15
16
  };
16
17
  const { frames, layoutSettings } = useMemo(() => updateCurrentLayoutSettings(galleryLayout), [galleryLayout]);
17
- return (_jsx("group", { ref: groupRef, scale: [1, 1, 1], position: [0, 0, 0], rotation: [0, 0, 0], children: frames.map((frame, index) => (_jsx(Frame, { url: frame.url, isActive: selectedFrameUrl === frame.url, createdObject: createdObject, position: frame.position, rotation: frame.rotation, hoverSettings: { sensitivity: 0.5, applyToChildren: false }, transformEffects: animations?.transform?.effects || [], layoutSettings: layoutSettings, side: galleryLayout?.side || 2, materialSettings: materialSettings, handleObjectClick: handleObjectClick, children: layoutSettings.geometryType !== 'none' && (_jsx(ImageGeometry, { url: frame.url, geometryType: layoutSettings.geometryType, outerFrameScale: layoutSettings.outerFrameScale })) }, index))) }));
18
+ return (_jsx("group", { ref: groupRef, scale: [1, 1, 1], position: [0, 0, 0], rotation: [0, 0, 0], children: frames.map((frame, index) => (_jsx(Frame, { url: frame.url, isActive: selectedFrameUrl === frame.url, createdObject: createdObject, position: frame.position, rotation: frame.rotation, hoverSettings: { sensitivity: 0.5, applyToChildren: false }, transformEffects: animations?.transform?.effects || [], layoutSettings: layoutSettings, side: galleryLayout?.side ?? 2, materialSettings: materialSettings, handleObjectClick: (createdObject, ref, event) => {
19
+ setSelectedFrameUrl(frame.url);
20
+ handleObjectClick?.(createdObject, ref, event);
21
+ }, children: layoutSettings.geometryType !== 'none' && (_jsx(ImageGeometry, { url: frame.url, geometryType: layoutSettings.geometryType, outerFrameScale: layoutSettings.outerFrameScale, side: galleryLayout?.side ?? 2 })) }, index))) }));
18
22
  };
19
23
  export default GalleryLayout;
@@ -1 +1 @@
1
- {"version":3,"file":"ImageGeometry.d.ts","sourceRoot":"","sources":["../../src/components/ImageGeometry.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzF,MAAM,WAAW,kBAAkB;IAC/B,YAAY,EAAE,YAAY,CAAC;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,aAAa,CAAC,EAAE,WAAW,CAAC;CAC/B;AAED,eAAO,MAAM,aAAa,EAuDpB,GAAI,CAAC"}
1
+ {"version":3,"file":"ImageGeometry.d.ts","sourceRoot":"","sources":["../../src/components/ImageGeometry.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzF,MAAM,WAAW,kBAAkB;IAC/B,YAAY,EAAE,YAAY,CAAC;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,aAAa,CAAC,EAAE,WAAW,CAAC;CAC/B;AASD,eAAO,MAAM,aAAa,EAiFpB,GAAI,CAAC"}
@@ -1,9 +1,15 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { forwardRef, useEffect, useMemo, useState } from 'react';
3
- import { DoubleSide, Mesh, MeshBasicMaterial } from 'three';
3
+ import { Mesh, MeshBasicMaterial } from 'three';
4
4
  import { createMeshByType } from '@3driseai/3drise-core';
5
- import { texturesManager } from '@3driseai/3drise-core';
6
- export const ImageGeometry = forwardRef(({ geometryType, url, position = [0, 0, 0], rotation = [0, 0, 0], scale = [1, 1, 1], }, ref) => {
5
+ import { texturesManager, IMAGE_KEY } from '@3driseai/3drise-core';
6
+ // Stable references so unrelated re-renders (e.g. hover state toggles on the
7
+ // parent Frame) don't invalidate the meshConfig/createdMesh memos below and
8
+ // leak a fresh Mesh/BufferGeometry/Material every render.
9
+ const DEFAULT_POSITION = [0, 0, 0];
10
+ const DEFAULT_ROTATION = [0, 0, 0];
11
+ const DEFAULT_SCALE = [1, 1, 1];
12
+ export const ImageGeometry = forwardRef(({ geometryType, url, position = DEFAULT_POSITION, rotation = DEFAULT_ROTATION, scale = DEFAULT_SCALE, side = 2, outerFrameScale = 1, }, ref) => {
7
13
  const [texture, setTexture] = useState(null);
8
14
  useEffect(() => {
9
15
  const textureName = url.split('/').pop()?.split('.')[0] || `texture-${Date.now()}`;
@@ -40,12 +46,13 @@ export const ImageGeometry = forwardRef(({ geometryType, url, position = [0, 0,
40
46
  }, [geometryType, position, rotation, scale]);
41
47
  const createdMesh = useMemo(() => {
42
48
  const mesh = createMeshByType(meshConfig)?.mesh;
49
+ mesh.userData = { [IMAGE_KEY]: true };
43
50
  if (!(mesh instanceof Mesh))
44
51
  return null;
45
52
  if (texture) {
46
53
  mesh.material = new MeshBasicMaterial({
47
54
  map: texture,
48
- side: DoubleSide,
55
+ side: side,
49
56
  toneMapped: false,
50
57
  transparent: true,
51
58
  polygonOffset: true,
@@ -55,6 +62,31 @@ export const ImageGeometry = forwardRef(({ geometryType, url, position = [0, 0,
55
62
  }
56
63
  return mesh;
57
64
  }, [meshConfig, texture]);
65
+ // Every recompute above allocates a new geometry + material; dispose the
66
+ // previous instance so they don't leak GPU resources.
67
+ useEffect(() => {
68
+ return () => {
69
+ createdMesh?.geometry?.dispose();
70
+ const material = createdMesh?.material;
71
+ if (Array.isArray(material))
72
+ material.forEach((m) => m.dispose());
73
+ else
74
+ material?.dispose();
75
+ };
76
+ }, [createdMesh]);
77
+ // `side` isn't a createdMesh dependency (changing it shouldn't rebuild the
78
+ // geometry), so apply it to the existing material reactively instead.
79
+ useEffect(() => {
80
+ const material = createdMesh?.material;
81
+ if (!material)
82
+ return;
83
+ if (Array.isArray(material))
84
+ material.forEach((m) => { m.side = side; m.needsUpdate = true; });
85
+ else {
86
+ material.side = side;
87
+ material.needsUpdate = true;
88
+ }
89
+ }, [createdMesh, side]);
58
90
  if (!createdMesh)
59
91
  return null;
60
92
  return (_jsx("group", { position: position, rotation: rotation, children: _jsx("primitive", { name: url, ref: ref, object: createdMesh }) }));
@@ -1 +1 @@
1
- {"version":3,"file":"ObjectNode.d.ts","sourceRoot":"","sources":["../../src/components/ObjectNode.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,KAAK,EAAe,qBAAqB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAChG,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,KAAK,eAAe,GAAG,gBAAgB,GAAG;IACtC,IAAI,EAAE,cAAc,CAAC;IACrB,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,qBAAqB,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;CAC7G,CAAC;AAgBF,QAAA,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAmHzC,CAAC;AACF,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"ObjectNode.d.ts","sourceRoot":"","sources":["../../src/components/ObjectNode.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,KAAK,EAAe,qBAAqB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAChG,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD,KAAK,eAAe,GAAG,gBAAgB,GAAG;IACtC,IAAI,EAAE,cAAc,CAAC;IACrB,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,qBAAqB,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;CAC7G,CAAC;AAgBF,QAAA,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAqHzC,CAAC;AACF,eAAe,UAAU,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"LightningEffect.d.ts","sourceRoot":"","sources":["../../../src/components/effects/LightningEffect.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,UAAU,oBAAqB,SAAQ,gBAAgB;IACnD,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,eAAe,CAAC;IACxB,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACnC;AAsDD,QAAA,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAwLnD,CAAC;AACF,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"LightningEffect.d.ts","sourceRoot":"","sources":["../../../src/components/effects/LightningEffect.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,UAAU,oBAAqB,SAAQ,gBAAgB;IACnD,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,eAAe,CAAC;IACxB,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACnC;AAsDD,QAAA,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CA2LnD,CAAC;AACF,eAAe,eAAe,CAAC"}
@@ -60,7 +60,10 @@ const LightningEffect = ({ boundingRadius, config, animations, onObjectReady, on
60
60
  const startValuesCacheRef = useRef({});
61
61
  const rootRef = useRef(null);
62
62
  useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
63
- const depth = Math.min(Math.max(branchDepth, 2), 6);
63
+ const requestedDepth = Number(branchDepth);
64
+ const depth = Number.isFinite(requestedDepth)
65
+ ? Math.min(Math.max(Math.floor(requestedDepth), 2), 6)
66
+ : 2;
64
67
  const N = Math.pow(2, depth) + 1; // vertex count: 5, 9, 17, 33, 65
65
68
  const coreCol = useMemo(() => new THREE.Color(color), [color]);
66
69
  const glowCol = useMemo(() => new THREE.Color(glowColor), [glowColor]);
@@ -1 +1 @@
1
- {"version":3,"file":"TerrainSurface.d.ts","sourceRoot":"","sources":["../../../src/components/terrain/TerrainSurface.tsx"],"names":[],"mappings":"AACA,OAAO,KAAqC,MAAM,OAAO,CAAC;AAU1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD;;;;;;;;;;;;;;;;GAgBG;AACH,QAAA,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAgG/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;AAW1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD;;;;;;;;;;;;;;;;GAgBG;AACH,QAAA,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAqG/C,CAAC;AAEF,eAAe,cAAc,CAAC"}
@@ -4,6 +4,7 @@ import { useFrame } from '@react-three/fiber';
4
4
  import { useObjectReady } from '../../hooks/useObjectReady.js';
5
5
  import { useTerrainClipmap } from '../../hooks/useTerrainClipmap.js';
6
6
  import { useCloudSun } from '../../hooks/useCloudSun.js';
7
+ import { useLiveTransitionEvents } from '../../hooks/useLiveTransitionEvents.js';
7
8
  import { TERRAIN_LIVE_KEY } from '../../hooks/useTerrainLive.js';
8
9
  import { TerrainMaterial } from '../../shaders/terrain/TerrainMaterial.js';
9
10
  import { applySunUniforms } from '../../utils/cloudUniforms.js';
@@ -58,6 +59,10 @@ const TerrainSurface = ({ id = '3drise-terrain', config, quality, skySettings, t
58
59
  return () => { delete root.userData[TERRAIN_LIVE_KEY]; };
59
60
  }, [id, liveHandle]);
60
61
  useObjectReady(rootRef, { id, type: 'terrain', config, quality }, onObjectReady, onObjectDispose);
62
+ // Sequence-authored transitions (`transition.state === 'terrain'`) — liveConfig
63
+ // is the same ref a controller's live handle writes into, merged last over
64
+ // the config each frame below.
65
+ useLiveTransitionEvents('terrain', liveConfig, config, rootRef);
61
66
  useFrame((state) => {
62
67
  const camera = state.camera;
63
68
  updateSun();
@@ -1 +1 @@
1
- {"version":3,"file":"VolumetricCloudsPass.d.ts","sourceRoot":"","sources":["../../../src/components/volumetricClouds/VolumetricCloudsPass.tsx"],"names":[],"mappings":"AACA,OAAO,KAAkD,MAAM,OAAO,CAAC;AAKvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAQvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEvH,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAErF;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,WAAW,yBAA0B,SAAQ,gBAAgB;IAC/D,6EAA6E;IAC7E,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IAC9B,MAAM,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAClC,OAAO,EAAE,mBAAmB,CAAC;IAC7B;oDACgD;IAChD,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,qFAAqF;IACrF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,6DAA6D;IAC7D,SAAS,CAAC,EAAE,qBAAqB,CAAC;CACrC;AAkBD,QAAA,MAAM,oBAAoB,EAAE,KAAK,CAAC,EAAE,CAAC,yBAAyB,CAmW7D,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
1
+ {"version":3,"file":"VolumetricCloudsPass.d.ts","sourceRoot":"","sources":["../../../src/components/volumetricClouds/VolumetricCloudsPass.tsx"],"names":[],"mappings":"AACA,OAAO,KAAkD,MAAM,OAAO,CAAC;AAMvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAQvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEvH,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAErF;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,WAAW,yBAA0B,SAAQ,gBAAgB;IAC/D,6EAA6E;IAC7E,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IAC9B,MAAM,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAClC,OAAO,EAAE,mBAAmB,CAAC;IAC7B;oDACgD;IAChD,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,qFAAqF;IACrF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,6DAA6D;IAC7D,SAAS,CAAC,EAAE,qBAAqB,CAAC;CACrC;AAkBD,QAAA,MAAM,oBAAoB,EAAE,KAAK,CAAC,EAAE,CAAC,yBAAyB,CAyW7D,CAAC;AAEF,eAAe,oBAAoB,CAAC"}
@@ -5,6 +5,7 @@ import { useFrame, useThree } from '@react-three/fiber';
5
5
  import { useFrameRenderer } from '../../hooks/useFrameRenderer.js';
6
6
  import { useThunder } from '../../hooks/useThunder.js';
7
7
  import { useObjectReady } from '../../hooks/useObjectReady.js';
8
+ import { useLiveTransitionEvents } from '../../hooks/useLiveTransitionEvents.js';
8
9
  import { CLOUD_LIVE_KEY } from '../../hooks/useCloudLive.js';
9
10
  import { useCloudNoise } from '../../hooks/useCloudNoise.js';
10
11
  import { useCloudSun } from '../../hooks/useCloudSun.js';
@@ -154,6 +155,11 @@ const VolumetricCloudsPass = ({ id = '3drise-clouds', deck, cirrus, quality, thu
154
155
  // ObjectReadySource's union is widened by Record<string, any>, but spelling the
155
156
  // cast out keeps the intent: this reports the layer's settings, not a mesh's.
156
157
  useObjectReady(rootRef, { id, type: 'clouds', deck, cirrus, quality }, onObjectReady, onObjectDispose);
158
+ // Sequence-authored transitions (`transition.state === 'clouds'`), on top of
159
+ // whatever the deck's own keyframe/mouse-move animations are already doing —
160
+ // liveDeck is the same ref a controller's live handle writes into, and it is
161
+ // already merged last, over both, each frame.
162
+ useLiveTransitionEvents('clouds', liveDeck, deck, rootRef);
157
163
  // Only a resolution change genuinely invalidates the history — the buffers are
158
164
  // reallocated and nothing in them maps to the new grid.
159
165
  //
@@ -0,0 +1,18 @@
1
+ import type { RefObject } from 'react';
2
+ import type { Object3D } from 'three';
3
+ /**
4
+ * Drives sequence-step transitions (see `useAnimationEvents`) into a
5
+ * renderer's existing live-override ref — the same ref a controller's live
6
+ * handle (`useCloudLive`/`useTerrainLive`/`useOceanLive`) already writes into
7
+ * on every slider drag, and which the renderer already merges over its config
8
+ * each frame. That merge order is what makes a transition win over whatever
9
+ * the settings currently say, with no extra "is animating" flag needed.
10
+ *
11
+ * Matches `SkyController`'s pattern (capture current value, lerp to target
12
+ * over the step's duration) but generically: any `to` key that is a number or
13
+ * number[] animates smoothly via `useNumericAnimation`; anything else
14
+ * (booleans, strings) switches immediately, the same way `visible` does.
15
+ */
16
+ export declare function useLiveTransitionEvents(stateName: string, liveRef: React.MutableRefObject<Record<string, any>>, currentConfig?: Record<string, any> | null, rootRef?: RefObject<Object3D | null>): void;
17
+ export default useLiveTransitionEvents;
18
+ //# sourceMappingURL=useLiveTransitionEvents.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useLiveTransitionEvents.d.ts","sourceRoot":"","sources":["../../src/hooks/useLiveTransitionEvents.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAItC;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CACnC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,EACpD,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,EAC1C,OAAO,CAAC,EAAE,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC,GACrC,IAAI,CAwCN;AAED,eAAe,uBAAuB,CAAC"}
@@ -0,0 +1,57 @@
1
+ import { useAnimationEvents } from './useAnimationEvents.js';
2
+ import { useNumericAnimation } from './useNumericAnimation.js';
3
+ /**
4
+ * Drives sequence-step transitions (see `useAnimationEvents`) into a
5
+ * renderer's existing live-override ref — the same ref a controller's live
6
+ * handle (`useCloudLive`/`useTerrainLive`/`useOceanLive`) already writes into
7
+ * on every slider drag, and which the renderer already merges over its config
8
+ * each frame. That merge order is what makes a transition win over whatever
9
+ * the settings currently say, with no extra "is animating" flag needed.
10
+ *
11
+ * Matches `SkyController`'s pattern (capture current value, lerp to target
12
+ * over the step's duration) but generically: any `to` key that is a number or
13
+ * number[] animates smoothly via `useNumericAnimation`; anything else
14
+ * (booleans, strings) switches immediately, the same way `visible` does.
15
+ */
16
+ export function useLiveTransitionEvents(stateName, liveRef, currentConfig, rootRef) {
17
+ const { animateNumeric } = useNumericAnimation();
18
+ useAnimationEvents({
19
+ onStepStart: (event) => {
20
+ if (!event.step?.transitions)
21
+ return;
22
+ for (const transition of event.step.transitions) {
23
+ if (transition.state !== stateName)
24
+ continue;
25
+ const toProps = transition.properties.to || {};
26
+ const fromProps = transition.properties.from || {};
27
+ // Immediate boolean switch, not animated — mirrors SkyController's `visible`.
28
+ if (toProps.visible !== undefined && rootRef?.current) {
29
+ rootRef.current.visible = toProps.visible;
30
+ }
31
+ const from = {};
32
+ const to = {};
33
+ for (const key of Object.keys(toProps)) {
34
+ if (key === 'visible')
35
+ continue;
36
+ const toVal = toProps[key];
37
+ if (toVal === undefined)
38
+ continue;
39
+ if (typeof toVal === 'number' || Array.isArray(toVal)) {
40
+ to[key] = toVal;
41
+ from[key] = fromProps[key] ?? liveRef.current[key] ?? currentConfig?.[key] ?? toVal;
42
+ }
43
+ else {
44
+ // Nothing to interpolate for a string/boolean — apply straight away.
45
+ liveRef.current[key] = toVal;
46
+ }
47
+ }
48
+ if (Object.keys(to).length > 0) {
49
+ animateNumeric(from, to, (updated) => {
50
+ Object.assign(liveRef.current, updated);
51
+ }, () => { }, event.step?.duration);
52
+ }
53
+ }
54
+ },
55
+ });
56
+ }
57
+ export default useLiveTransitionEvents;
@@ -1 +1 @@
1
- {"version":3,"file":"useObjectEdges.d.ts","sourceRoot":"","sources":["../../src/hooks/useObjectEdges.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAmH/D,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,CAAC,EAAE,cAAc,GAAG,IAAI,CA6ChH"}
1
+ {"version":3,"file":"useObjectEdges.d.ts","sourceRoot":"","sources":["../../src/hooks/useObjectEdges.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAmH/D,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,CAAC,EAAE,cAAc,GAAG,IAAI,CA8DhH"}
@@ -131,13 +131,30 @@ export function useObjectEdges(objectId, edgesSettings, objectSource) {
131
131
  }
132
132
  setObject(resolveObject3D(objectSource) ?? findById(objectId));
133
133
  }, [edgesEnabled, objectId, objectSource]);
134
+ // Descendant meshes (e.g. gallery images) can still be loading their
135
+ // texture asynchronously when `object` first resolves, so the initial
136
+ // build below can run before there's anything to outline. Poll the real
137
+ // mesh count each frame and bump a tick when it changes so edges get
138
+ // rebuilt once the subtree actually has geometry.
139
+ const meshCountRef = useRef(-1);
140
+ const [structureTick, setStructureTick] = useState(0);
141
+ useFrame(() => {
142
+ if (!object)
143
+ return;
144
+ const count = collectMeshes(object).length;
145
+ if (count !== meshCountRef.current) {
146
+ meshCountRef.current = count;
147
+ setStructureTick((t) => t + 1);
148
+ }
149
+ });
134
150
  const edgesGroup = useMemo(() => {
135
151
  if (!object || !edgesSettings.enabled)
136
152
  return null;
137
153
  return buildEdgesGroup(object, edgesSettings);
138
- // Rebuild when geometry-affecting settings change
154
+ // Rebuild when geometry-affecting settings change, or the subtree's
155
+ // mesh count changes (structureTick — see the polling effect above).
139
156
  // eslint-disable-next-line react-hooks/exhaustive-deps
140
- }, [object, edgesSettings.type, edgesSettings.enabled, edgesSettings.tubeRadius, edgesSettings.cubeSize]);
157
+ }, [object, edgesSettings.type, edgesSettings.enabled, edgesSettings.tubeRadius, edgesSettings.cubeSize, structureTick]);
141
158
  // Attach edges to the live object so they move with it
142
159
  useEffect(() => {
143
160
  if (!object || !edgesGroup)
@@ -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;AAI7E,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAE7E;;;;;;;;;;;;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;;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,OAAO,GACV,EAAE,uBAAuB,GAAG,mBAAmB,CAkE/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;AAIvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAI7E,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAE7E;;;;;;;;;;;;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;;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,OAAO,GACV,EAAE,uBAAuB,GAAG,mBAAmB,CAuE/C;AAED,eAAe,gBAAgB,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { useEffect, useMemo, useRef } from 'react';
2
2
  import { useFrame } from '@react-three/fiber';
3
3
  import { useCloudSun } from './useCloudSun.js';
4
+ import { useLiveTransitionEvents } from './useLiveTransitionEvents.js';
4
5
  import { OCEAN_LIVE_KEY } from './useOceanLive.js';
5
6
  import { OceanSurfaceMaterial } from '../shaders/ocean/OceanSurfaceMaterial.js';
6
7
  import { applySunUniforms } from '../utils/cloudUniforms.js';
@@ -35,6 +36,10 @@ export function useOceanMaterial({ id, config, quality, skySettings, timeSetting
35
36
  root.userData[OCEAN_LIVE_KEY] = liveHandle;
36
37
  return () => { delete root.userData[OCEAN_LIVE_KEY]; };
37
38
  }, [id, liveHandle]);
39
+ // Sequence-authored transitions (`transition.state === 'ocean'`) — shared by
40
+ // both hosts (OceanSurface, WaterBody), the same way the material and the
41
+ // live handle already are.
42
+ useLiveTransitionEvents('ocean', liveConfig, config, rootRef);
38
43
  useFrame((state) => {
39
44
  const shader = material;
40
45
  updateSun();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@3driseai/3drise-viewer",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -39,6 +39,6 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "@3driseai/3drise-common": "^1.0.1",
42
- "@3driseai/3drise-core": "^1.0.8"
42
+ "@3driseai/3drise-core": "^1.0.9"
43
43
  }
44
44
  }