@3driseai/3drise-viewer 0.1.18 → 0.1.20

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 (62) hide show
  1. package/dist/components/AnimatedObject.d.ts.map +1 -1
  2. package/dist/components/AnimatedObject.js +3 -2
  3. package/dist/components/CreatedObject.d.ts.map +1 -1
  4. package/dist/components/CreatedObject.js +18 -14
  5. package/dist/components/ModelMaterialPassContext.d.ts +7 -0
  6. package/dist/components/ModelMaterialPassContext.d.ts.map +1 -0
  7. package/dist/components/ModelMaterialPassContext.js +7 -0
  8. package/dist/components/ModelPrimitive.d.ts.map +1 -1
  9. package/dist/components/ModelPrimitive.js +7 -1
  10. package/dist/components/ObjectNode.d.ts.map +1 -1
  11. package/dist/components/ObjectNode.js +10 -6
  12. package/dist/components/ParticlesGenerator.d.ts.map +1 -1
  13. package/dist/components/ParticlesGenerator.js +11 -5
  14. package/dist/components/particles/BioluminescentWakeParticles.d.ts +5 -2
  15. package/dist/components/particles/BioluminescentWakeParticles.d.ts.map +1 -1
  16. package/dist/components/particles/BioluminescentWakeParticles.js +28 -6
  17. package/dist/components/particles/BrownianDustParticles.d.ts +5 -2
  18. package/dist/components/particles/BrownianDustParticles.d.ts.map +1 -1
  19. package/dist/components/particles/BrownianDustParticles.js +28 -6
  20. package/dist/components/particles/ChladniSandParticles.d.ts +15 -0
  21. package/dist/components/particles/ChladniSandParticles.d.ts.map +1 -0
  22. package/dist/components/particles/ChladniSandParticles.js +912 -0
  23. package/dist/components/particles/ElectronOrbitalsParticles.d.ts +5 -2
  24. package/dist/components/particles/ElectronOrbitalsParticles.d.ts.map +1 -1
  25. package/dist/components/particles/ElectronOrbitalsParticles.js +28 -6
  26. package/dist/components/particles/FaradayPowderParticles.d.ts +15 -0
  27. package/dist/components/particles/FaradayPowderParticles.d.ts.map +1 -0
  28. package/dist/components/particles/FaradayPowderParticles.js +783 -0
  29. package/dist/components/particles/GrinderSparksParticles.d.ts +5 -2
  30. package/dist/components/particles/GrinderSparksParticles.d.ts.map +1 -1
  31. package/dist/components/particles/GrinderSparksParticles.js +37 -13
  32. package/dist/components/particles/NeonRainParticles.d.ts +15 -0
  33. package/dist/components/particles/NeonRainParticles.d.ts.map +1 -0
  34. package/dist/components/particles/NeonRainParticles.js +716 -0
  35. package/dist/components/particles/TidalStreamParticles.d.ts +5 -2
  36. package/dist/components/particles/TidalStreamParticles.d.ts.map +1 -1
  37. package/dist/components/particles/TidalStreamParticles.js +28 -6
  38. package/dist/components/particles/index.d.ts +6 -0
  39. package/dist/components/particles/index.d.ts.map +1 -1
  40. package/dist/components/particles/index.js +3 -0
  41. package/dist/constants.d.ts.map +1 -1
  42. package/dist/constants.js +2 -1
  43. package/dist/hooks/index.d.ts +3 -0
  44. package/dist/hooks/index.d.ts.map +1 -1
  45. package/dist/hooks/index.js +2 -0
  46. package/dist/hooks/useConfigLive.d.ts +54 -0
  47. package/dist/hooks/useConfigLive.d.ts.map +1 -0
  48. package/dist/hooks/useConfigLive.js +100 -0
  49. package/dist/hooks/useMaterialApplication.d.ts.map +1 -1
  50. package/dist/hooks/useMaterialApplication.js +1 -0
  51. package/dist/hooks/useNodeOverrides.d.ts +13 -0
  52. package/dist/hooks/useNodeOverrides.d.ts.map +1 -0
  53. package/dist/hooks/useNodeOverrides.js +254 -0
  54. package/dist/index.d.ts +2 -0
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +1 -0
  57. package/dist/utils/configAnimations.d.ts +43 -0
  58. package/dist/utils/configAnimations.d.ts.map +1 -0
  59. package/dist/utils/configAnimations.js +73 -0
  60. package/dist/utils/particleTrails.d.ts.map +1 -1
  61. package/dist/utils/particleTrails.js +17 -8
  62. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"AnimatedObject.d.ts","sourceRoot":"","sources":["../../src/components/AnimatedObject.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAM7D,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACtG,MAAM,WAAW,mBAAoB,SAAQ,gBAAgB;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE,iBAAiB,CAAC;IACtC,aAAa,EAAE,qBAAqB,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IACtC,iBAAiB,CAAC,EAAE,CAAC,aAAa,EAAE,qBAAqB,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;IACjH,cAAc,CAAC,EAAE,CAAC,CAAC,EAAE,UAAU,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;IACvD,cAAc,CAAC,EAAE,CAAC,CAAC,EAAE,UAAU,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;IACvD,MAAM,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC7B;AAGD,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAiEvD,CAAC;AAEH,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"AnimatedObject.d.ts","sourceRoot":"","sources":["../../src/components/AnimatedObject.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAM7D,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACtG,MAAM,WAAW,mBAAoB,SAAQ,gBAAgB;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE,iBAAiB,CAAC;IACtC,aAAa,EAAE,qBAAqB,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IACtC,iBAAiB,CAAC,EAAE,CAAC,aAAa,EAAE,qBAAqB,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;IACjH,cAAc,CAAC,EAAE,CAAC,CAAC,EAAE,UAAU,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;IACvD,cAAc,CAAC,EAAE,CAAC,CAAC,EAAE,UAAU,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;IACvD,MAAM,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC7B;AAGD,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAqFvD,CAAC;AAEH,eAAe,cAAc,CAAC"}
@@ -64,12 +64,13 @@ visible = true, children, parentRef, handleObjectClick, onPointerEnter, onPointe
64
64
  }
65
65
  };
66
66
  if (createdObject.type === 'particles' || createdObject.type === 'gallery' || (!hasHoverFunctionalities && !hasMouseInteraction)) {
67
- return _jsx(_Fragment, { children: _jsxs("group", { ref: groupRef, position: position, scale: scale, rotation: rotation, visible: visible, onClick: onClick,
67
+ return _jsx(_Fragment, { children: _jsxs("group", { ref: groupRef, position: position, scale: scale, rotation: rotation, visible: visible,
68
+ // onClick={onClick}
68
69
  // onPointerEnter={onPointerEnter}
69
70
  // onPointerLeave={onPointerLeave}
70
71
  name: createdObject.meshSettings.name, userData: { id: createdObject.id }, children: [children, _jsx(EffectsGroup, { createdObject: createdObject, objectRef: groupRef, onObjectReady: onObjectReady, onObjectDispose: onObjectDispose })] }) });
71
72
  }
72
- return (_jsx(_Fragment, { children: _jsxs("group", { ref: groupRef, position: position, scale: scale, rotation: rotation, visible: visible, onClick: onClick, onPointerEnter: onPointerEnter, onPointerLeave: onPointerLeave, name: createdObject.meshSettings.name, userData: { id: createdObject.id }, children: [children, _jsx(EffectsGroup, { createdObject: createdObject, objectRef: groupRef, onObjectReady: onObjectReady, onObjectDispose: onObjectDispose })] }) }));
73
+ return (_jsxs("group", { ref: groupRef, position: position, scale: scale, rotation: rotation, visible: visible, onClick: onClick, onPointerEnter: onPointerEnter, onPointerLeave: onPointerLeave, name: createdObject.meshSettings.name, userData: { id: createdObject.id }, children: [children, _jsx(EffectsGroup, { createdObject: createdObject, objectRef: groupRef, onObjectReady: onObjectReady, onObjectDispose: onObjectDispose })] }));
73
74
  });
74
75
  AnimatedObject.displayName = 'AnimatedObject';
75
76
  export default AnimatedObject;
@@ -1 +1 @@
1
- {"version":3,"file":"CreatedObject.d.ts","sourceRoot":"","sources":["../../src/components/CreatedObject.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAUnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,UAAU,kBAAmB,SAAQ,gBAAgB;IACjD,aAAa,EAAE,qBAAqB,CAAC;IACrC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,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,CAgF9C,CAAC;AAEH,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"CreatedObject.d.ts","sourceRoot":"","sources":["../../src/components/CreatedObject.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAUnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,UAAU,kBAAmB,SAAQ,gBAAgB;IACjD,aAAa,EAAE,qBAAqB,CAAC;IACrC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC,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,CAmF9C,CAAC;AAEH,eAAe,aAAa,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useRef, useEffect, useState, memo } from "react";
2
+ import { useRef, useEffect, useState, useReducer, memo } from "react";
3
+ import { ModelMaterialPassContext } from "./ModelMaterialPassContext.js";
3
4
  import { DEFAULT_EDGES_SETTINGS } from "@3driseai/3drise-core";
4
5
  import { useMaterialApplication } from "../hooks/useMaterialApplication.js";
5
6
  import { useShadowApplication } from "../hooks/useShadowApplication.js";
@@ -29,7 +30,10 @@ const CreatedObject = memo(({ createdObject, children, shouldApply3driseMaterial
29
30
  const [mountedObject, setMountedObject] = useState(null);
30
31
  useObjectEdges(createdObject.id, createdObject.edgesSettings ?? DEFAULT_EDGES_SETTINGS, meshRef);
31
32
  // useObjectGenerativeEffects(mountedObject, createdObject.effects);
32
- useMaterialApplication(meshRef, createdObject.materialSettings, shouldApply3driseMaterial);
33
+ // Bumped when the model-level material pass lands, so per-mesh overrides
34
+ // (useNodeOverrides in ModelPrimitive) re-apply on top of it.
35
+ const [materialPass, bumpMaterialPass] = useReducer((n) => n + 1, 0);
36
+ useMaterialApplication(meshRef, createdObject.materialSettings, shouldApply3driseMaterial, bumpMaterialPass);
33
37
  useShadowApplication(meshRef, createdObject.meshSettings.castShadow, createdObject.meshSettings.receiveShadow);
34
38
  const anchoredPosition = cameraManager.getAnchorPosition(createdObject.meshSettings.anchor?.anchor || 'center', 0, createdObject.meshSettings.anchor?.offset || { y: 0, x: 0, z: 0 }, createdObject.meshSettings.anchor?.padding || 0);
35
39
  useEffect(() => {
@@ -77,19 +81,19 @@ const CreatedObject = memo(({ createdObject, children, shouldApply3driseMaterial
77
81
  e.stopPropagation();
78
82
  };
79
83
  const anchorEnabled = createdObject.meshSettings.anchor && createdObject.meshSettings.anchor.enabled;
80
- return (_jsx(AnimatedObject
81
- // animationId={createdObject.animationId || ''}
82
- , {
84
+ return (_jsx(ModelMaterialPassContext.Provider, { value: materialPass, children: _jsx(AnimatedObject
83
85
  // animationId={createdObject.animationId || ''}
84
- stateKey: createdObject.id, transitionSettings: {
85
- ...animatedTransform,
86
- position: anchorEnabled ? [anchoredPosition.x, anchoredPosition.y, anchoredPosition.z] : animatedTransform.position
87
- }, createdObject: createdObject,
88
- // animateMaterial={!useDreiMaterials}
89
- parentRef: meshRef, onObjectReady: onObjectReady, onObjectDispose: onObjectDispose, visible: createdObject.meshSettings.visible, handleObjectClick: handleObjectClick, onPointerEnter: handleObjectEnter, onPointerLeave: e => handleObjectLeave(e), hovered: hovered, children: createdObject.rigidBodySettings?.enabled ? (_jsx(RigidBody, { type: createdObject.rigidBodySettings?.type || 'dynamic', mass: createdObject.rigidBodySettings?.mass || 1, colliders: createdObject.userData?.type === 'plane'
90
- ? 'cuboid' // Force box collider for planes
91
- : undefined // Auto-detect for others
92
- , friction: createdObject.rigidBodySettings?.friction, restitution: createdObject.rigidBodySettings?.restitution, gravityScale: createdObject.rigidBodySettings?.gravityScale ?? 1, linearDamping: createdObject.rigidBodySettings?.linearDamping ?? 0.01, angularDamping: createdObject.rigidBodySettings?.angularDamping ?? 0.01, children: children })) : (children) }));
86
+ , {
87
+ // animationId={createdObject.animationId || ''}
88
+ stateKey: createdObject.id, transitionSettings: {
89
+ ...animatedTransform,
90
+ position: anchorEnabled ? [anchoredPosition.x, anchoredPosition.y, anchoredPosition.z] : animatedTransform.position
91
+ }, createdObject: createdObject,
92
+ // animateMaterial={!useDreiMaterials}
93
+ parentRef: meshRef, onObjectReady: onObjectReady, onObjectDispose: onObjectDispose, visible: createdObject.meshSettings.visible, handleObjectClick: handleObjectClick, onPointerEnter: handleObjectEnter, onPointerLeave: e => handleObjectLeave(e), hovered: hovered, children: createdObject.rigidBodySettings?.enabled ? (_jsx(RigidBody, { type: createdObject.rigidBodySettings?.type || 'dynamic', mass: createdObject.rigidBodySettings?.mass || 1, colliders: createdObject.userData?.type === 'plane'
94
+ ? 'cuboid' // Force box collider for planes
95
+ : undefined // Auto-detect for others
96
+ , friction: createdObject.rigidBodySettings?.friction, restitution: createdObject.rigidBodySettings?.restitution, gravityScale: createdObject.rigidBodySettings?.gravityScale ?? 1, linearDamping: createdObject.rigidBodySettings?.linearDamping ?? 0.01, angularDamping: createdObject.rigidBodySettings?.angularDamping ?? 0.01, children: children })) : (children) }) }));
93
97
  });
94
98
  CreatedObject.displayName = 'CreatedObject';
95
99
  export default CreatedObject;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Bumped by CreatedObject every time its model-level material pass
3
+ * (useMaterialApplication) finishes. Per-mesh overrides re-apply on top of it, because
4
+ * the pass replaces every material in the model — including the ones an override set.
5
+ */
6
+ export declare const ModelMaterialPassContext: import("react").Context<number>;
7
+ //# sourceMappingURL=ModelMaterialPassContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ModelMaterialPassContext.d.ts","sourceRoot":"","sources":["../../src/components/ModelMaterialPassContext.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,iCAAmB,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { createContext } from 'react';
2
+ /**
3
+ * Bumped by CreatedObject every time its model-level material pass
4
+ * (useMaterialApplication) finishes. Per-mesh overrides re-apply on top of it, because
5
+ * the pass replaces every material in the model — including the ones an override set.
6
+ */
7
+ export const ModelMaterialPassContext = createContext(0);
@@ -1 +1 @@
1
- {"version":3,"file":"ModelPrimitive.d.ts","sourceRoot":"","sources":["../../src/components/ModelPrimitive.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoC,MAAM,OAAO,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAGnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,UAAU,mBAAoB,SAAQ,gBAAgB;IAClD,aAAa,EAAE,qBAAqB,CAAC;IACrC,kFAAkF;IAClF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,SAAS,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,QAAA,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CA8BhD,CAAC;AAIH,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"ModelPrimitive.d.ts","sourceRoot":"","sources":["../../src/components/ModelPrimitive.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA6C,MAAM,OAAO,CAAC;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAKnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,UAAU,mBAAoB,SAAQ,gBAAgB;IAClD,aAAa,EAAE,qBAAqB,CAAC;IACrC,kFAAkF;IAClF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,SAAS,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,QAAA,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAkChD,CAAC;AAIH,eAAe,cAAc,CAAC"}
@@ -1,6 +1,8 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { memo, useCallback, useRef } from 'react';
2
+ import { memo, useCallback, useMemo, useRef } from 'react';
3
3
  import { useModelObject } from '../hooks/useModelObject.js';
4
+ import { useNodeOverrides } from '../hooks/useNodeOverrides.js';
5
+ import { getNodeOverrides, peekModelAsset } from '@3driseai/3drise-core';
4
6
  import { useObjectReadyReporter } from '../hooks/useObjectReady.js';
5
7
  /**
6
8
  * Mounts the Object3D behind a `type: 'model'` scene object, and hands it to the host
@@ -32,6 +34,10 @@ import { useObjectReadyReporter } from '../hooks/useObjectReady.js';
32
34
  const ModelPrimitive = memo(({ createdObject, slot = 0, onObjectReady, onObjectDispose, children }) => {
33
35
  const attachedRef = useRef(null);
34
36
  const object = useModelObject(createdObject, slot);
37
+ // Per-mesh overrides (ModelTree). Originals come from the never-mounted cache master.
38
+ const url = createdObject.userData?.url;
39
+ const master = useMemo(() => (url ? peekModelAsset(url)?.scene ?? null : null), [url, object]);
40
+ useNodeOverrides(object, getNodeOverrides(createdObject), master);
35
41
  const { report, reportDisposed } = useObjectReadyReporter(createdObject, onObjectReady, onObjectDispose);
36
42
  const setObjectRef = useCallback((instance) => {
37
43
  if (instance) {
@@ -1 +1 @@
1
- {"version":3,"file":"ObjectNode.d.ts","sourceRoot":"","sources":["../../src/components/ObjectNode.tsx"],"names":[],"mappings":"AAkBA,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;AAiBrD,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;AAkBF,QAAA,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAqIzC,CAAC;AACF,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"ObjectNode.d.ts","sourceRoot":"","sources":["../../src/components/ObjectNode.tsx"],"names":[],"mappings":"AAkBA,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;AAiBrD,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;AAkBF,QAAA,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAsIzC,CAAC;AACF,eAAe,UAAU,CAAC"}
@@ -8,8 +8,6 @@ import PathRenderer from './PathRenderer.js';
8
8
  import GalleryLayout from './GalleryLayout.js';
9
9
  import EffectsGenerator from './EffectsGenerator.js';
10
10
  import ParticlesGenerator from './ParticlesGenerator.js';
11
- import ParticlesR3f from "./ParticlesR3f.js";
12
- import Lines from "./Lines.js";
13
11
  import LightObject from "./LightObject.js";
14
12
  import EnvironmentObject from "./EnvironmentObject.js";
15
13
  import Galaxy from "./Galaxy.js";
@@ -95,10 +93,16 @@ const ObjectNode = ({ node, onObjectReady, onObjectDispose, handleObjectClick })
95
93
  particles: (obj, onObjectReady) => {
96
94
  // Generative particle objects (config.type) render through ParticlesGenerator;
97
95
  // the legacy shape particles keep their own path.
98
- if (isGenerativeParticlesConfig(obj.config))
99
- return (_jsx(ParticlesGenerator, { settings: { config: obj.config }, ...relabel(obj) }, obj.id));
100
- const particlesSettings = obj;
101
- return (_jsxs(_Fragment, { children: [particlesSettings.config?.particles && (_jsx(ParticlesR3f, { particlesSettings: particlesSettings, duration: 1, transitionSettings: particlesSettings.meshSettings, ...relabel(obj) })), particlesSettings.config?.paths && (_jsx(Lines, { createdObject: particlesSettings, ...relabel(obj) }))] }));
96
+ return (_jsx(ParticlesGenerator, { settings: { id: obj.id, config: obj.config, animations: obj.animations }, ...relabel(obj) }, obj.id));
97
+ // if (isGenerativeParticlesConfig(obj.config))
98
+ // return (<ParticlesGenerator key={obj.id} settings={{ config: obj.config }} {...relabel(obj)} />);
99
+ // const particlesSettings = obj;
100
+ // return (<>
101
+ // {particlesSettings.config?.particles && (
102
+ // <ParticlesR3f particlesSettings={particlesSettings} duration={1} transitionSettings={particlesSettings.meshSettings} {...relabel(obj)} />
103
+ // )}
104
+ // {particlesSettings.config?.paths && (<Lines createdObject={particlesSettings} {...relabel(obj)} />)}
105
+ // </>);
102
106
  },
103
107
  path: (obj, onObjectReady) => (_jsx(PathRenderer, { pathSettings: obj.config, ...relabel(obj) }, obj.id)),
104
108
  grid: (obj, onObjectReady) => (
@@ -1 +1 @@
1
- {"version":3,"file":"ParticlesGenerator.d.ts","sourceRoot":"","sources":["../../src/components/ParticlesGenerator.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAO7D,MAAM,WAAW,uBAAwB,SAAQ,gBAAgB;IAC7D,QAAQ,EAAE,2BAA2B,CAAC;CACzC;AAED,0FAA0F;AAC1F,QAAA,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CAazD,CAAC;AACF,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"ParticlesGenerator.d.ts","sourceRoot":"","sources":["../../src/components/ParticlesGenerator.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAU7D,MAAM,WAAW,uBAAwB,SAAQ,gBAAgB;IAC7D,QAAQ,EAAE,2BAA2B,CAAC;CACzC;AAED,0FAA0F;AAC1F,QAAA,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CAgBzD,CAAC;AACF,eAAe,kBAAkB,CAAC"}
@@ -4,6 +4,9 @@ import BrownianDustParticles from './particles/BrownianDustParticles.js';
4
4
  import BioluminescentWakeParticles from './particles/BioluminescentWakeParticles.js';
5
5
  import ElectronOrbitalsParticles from './particles/ElectronOrbitalsParticles.js';
6
6
  import GrinderSparksParticles from './particles/GrinderSparksParticles.js';
7
+ import ChladniSandParticles from './particles/ChladniSandParticles.js';
8
+ import FaradayPowderParticles from './particles/FaradayPowderParticles.js';
9
+ import NeonRainParticles from './particles/NeonRainParticles.js';
7
10
  /** Standalone generative particle objects (type 'particles'), switched on config.type. */
8
11
  const ParticlesGenerator = ({ settings, onObjectReady, onObjectDispose }) => {
9
12
  const { config, animations } = settings;
@@ -12,11 +15,14 @@ const ParticlesGenerator = ({ settings, onObjectReady, onObjectDispose }) => {
12
15
  const size = config.size ?? 1;
13
16
  const ready = { onObjectReady, onObjectDispose };
14
17
  switch (config.type) {
15
- case 'tidalStream': return _jsx(TidalStreamParticles, { size: size, config: config, animations: animations, ...ready });
16
- case 'brownianDust': return _jsx(BrownianDustParticles, { size: size, config: config, animations: animations, ...ready });
17
- case 'bioluminescentWake': return _jsx(BioluminescentWakeParticles, { size: size, config: config, animations: animations, ...ready });
18
- case 'electronOrbitals': return _jsx(ElectronOrbitalsParticles, { size: size, config: config, animations: animations, ...ready });
19
- case 'grinderSparks': return _jsx(GrinderSparksParticles, { size: size, config: config, animations: animations, ...ready });
18
+ case 'tidalStream': return _jsx(TidalStreamParticles, { id: settings.id, size: size, config: config, animations: animations, ...ready });
19
+ case 'brownianDust': return _jsx(BrownianDustParticles, { id: settings.id, size: size, config: config, animations: animations, ...ready });
20
+ case 'bioluminescentWake': return _jsx(BioluminescentWakeParticles, { id: settings.id, size: size, config: config, animations: animations, ...ready });
21
+ case 'electronOrbitals': return _jsx(ElectronOrbitalsParticles, { id: settings.id, size: size, config: config, animations: animations, ...ready });
22
+ case 'grinderSparks': return _jsx(GrinderSparksParticles, { id: settings.id, size: size, config: config, animations: animations, ...ready });
23
+ case 'chladniSand': return _jsx(ChladniSandParticles, { id: settings.id, size: size, config: config, animations: animations, ...ready });
24
+ case 'faradayPowder': return _jsx(FaradayPowderParticles, { id: settings.id, size: size, config: config, animations: animations, ...ready });
25
+ case 'neonRain': return _jsx(NeonRainParticles, { id: settings.id, size: size, config: config, animations: animations, ...ready });
20
26
  default: return null;
21
27
  }
22
28
  };
@@ -1,11 +1,14 @@
1
1
  import React from 'react';
2
- import type { AnimationOptions, BioluminescentWakeParticlesConfig } from '@3driseai/3drise-core';
2
+ import type { ObjectAnimations, BioluminescentWakeParticlesConfig } from '@3driseai/3drise-core';
3
3
  import type { ObjectReadyProps } from '../../types/objectReady';
4
4
  export interface BioluminescentWakeParticlesProps extends ObjectReadyProps {
5
+ /** Scene object id: the controller finds the live-edit handle by it. */
6
+ id?: string;
5
7
  /** World size of the object (units); the object's transform scales it further. */
6
8
  size: number;
7
9
  config: BioluminescentWakeParticlesConfig;
8
- animations?: AnimationOptions[];
10
+ /** Per-domain keyframes; the `particles` domain drives this object's config knobs. */
11
+ animations?: ObjectAnimations;
9
12
  }
10
13
  declare const BioluminescentWakeParticles: React.FC<BioluminescentWakeParticlesProps>;
11
14
  export default BioluminescentWakeParticles;
@@ -1 +1 @@
1
- {"version":3,"file":"BioluminescentWakeParticles.d.ts","sourceRoot":"","sources":["../../../src/components/particles/BioluminescentWakeParticles.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsD,MAAM,OAAO,CAAC;AAG3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,iCAAiC,EAAE,MAAM,uBAAuB,CAAC;AAKjG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,gCAAiC,SAAQ,gBAAgB;IACtE,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,iCAAiC,CAAC;IAC1C,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACnC;AAo9BD,QAAA,MAAM,2BAA2B,EAAE,KAAK,CAAC,EAAE,CAAC,gCAAgC,CA6B3E,CAAC;AAEF,eAAe,2BAA2B,CAAC"}
1
+ {"version":3,"file":"BioluminescentWakeParticles.d.ts","sourceRoot":"","sources":["../../../src/components/particles/BioluminescentWakeParticles.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmE,MAAM,OAAO,CAAC;AAIxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,iCAAiC,EAAE,MAAM,uBAAuB,CAAC;AAMjG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,gCAAiC,SAAQ,gBAAgB;IACtE,wEAAwE;IACxE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,iCAAiC,CAAC;IAC1C,sFAAsF;IACtF,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACjC;AA09BD,QAAA,MAAM,2BAA2B,EAAE,KAAK,CAAC,EAAE,CAAC,gCAAgC,CAiD3E,CAAC;AAEF,eAAe,2BAA2B,CAAC"}
@@ -1,11 +1,13 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useEffect, useLayoutEffect, useMemo, useRef } from 'react';
2
+ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react';
3
3
  import * as THREE from 'three';
4
4
  import { useFrame } from '@react-three/fiber';
5
- import { applyGenerativeAnimations } from '../../utils/handleGenerativeEffectsAnimations.js';
5
+ import { GENERATIVE_PARTICLES_ANIMATABLE } from '@3driseai/3drise-core';
6
+ import { applyConfigKeyframeAnimations } from '../../utils/configAnimations.js';
6
7
  import { createPointerUniforms, stepPointerField, usePointerField } from '../../utils/pointerField.js';
7
8
  import { createTrailUniforms, applyTrailConfig, createCpuTrail } from '../../utils/particleTrails.js';
8
9
  import { useObjectReady } from '../../hooks/useObjectReady.js';
10
+ import { useConfigLiveHandle } from '../../hooks/useConfigLive.js';
9
11
  /**
10
12
  * Sea Sparkle — Bioluminescent plankton that flash when the water shears: swell orbits, excitable cells that tire and recover, moon glitter; the cursor is a hand, falling pebbles or a hunting fish. Use for night sea and fantasy scenes.
11
13
  */
@@ -1097,9 +1099,12 @@ const DEFAULTS = {
1097
1099
  trailColor: '#2f7dff',
1098
1100
  trailShare: 1,
1099
1101
  };
1100
- const BioluminescentWakeParticles = ({ size, config, animations, onObjectReady, onObjectDispose }) => {
1102
+ /** Knobs that rebuild the simulation: they change on commit, never through the live path. */
1103
+ const STRUCTURAL = ['particleCount', 'seed', 'quality'];
1104
+ /** Knobs an animation (or mouse move) may drive per frame — generated from applyConfig. */
1105
+ const ANIMATABLE = new Set(GENERATIVE_PARTICLES_ANIMATABLE.bioluminescentWake.map((p) => p.value));
1106
+ const BioluminescentWakeParticles = ({ id, size, config, animations, onObjectReady, onObjectDispose }) => {
1101
1107
  const rootRef = useRef(null);
1102
- const animCache = useRef({});
1103
1108
  useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
1104
1109
  const s = size > 0 ? size : 1;
1105
1110
  const cfg = useMemo(() => ({ ...DEFAULTS, ...config }), [config]);
@@ -1112,11 +1117,28 @@ const BioluminescentWakeParticles = ({ size, config, animations, onObjectReady,
1112
1117
  applyConfig(uniforms, cfg, s);
1113
1118
  instance.apply(cfg);
1114
1119
  }, [instance, uniforms, cfg, s]);
1120
+ // Slider drags and keyframe animations are both overrides on the committed config.
1121
+ // This is the one place they are merged and pushed, so they compose instead of
1122
+ // overwriting each other; the animation wins, because it is the active driver.
1123
+ const keyframes = animations?.particles?.animations;
1124
+ const animOverride = useRef({});
1125
+ const animFrom = useRef({});
1126
+ const animStart = useRef(0);
1127
+ // Editing a keyframe restarts its cycle rather than dropping the user mid-way.
1128
+ useEffect(() => { animStart.current = performance.now(); animFrom.current = {}; }, [keyframes]);
1129
+ const push = useCallback((overrides) => {
1130
+ const merged = { ...cfg, ...overrides, ...animOverride.current };
1131
+ const z = merged.size;
1132
+ applyConfig(uniforms, merged, z && z > 0 ? z : s);
1133
+ }, [cfg, uniforms, s]);
1134
+ const live = useConfigLiveHandle(rootRef, id, 'particles', cfg, STRUCTURAL, push);
1115
1135
  const pointer = usePointerField();
1116
1136
  useFrame((state, delta) => {
1117
1137
  instance.update(Math.min(delta, 0.1), state.camera, pointer);
1118
- if (animations?.length)
1119
- applyGenerativeAnimations(instance.materials, animations, animCache.current, Date.now(), false);
1138
+ if (keyframes?.length) {
1139
+ applyConfigKeyframeAnimations(keyframes, animFrom.current, cfg, animOverride.current, performance.now() - animStart.current, ANIMATABLE);
1140
+ push(live.current);
1141
+ }
1120
1142
  });
1121
1143
  return (_jsx("group", { ref: rootRef, children: _jsx("primitive", { object: instance.object }) }));
1122
1144
  };
@@ -1,11 +1,14 @@
1
1
  import React from 'react';
2
- import type { AnimationOptions, BrownianDustParticlesConfig } from '@3driseai/3drise-core';
2
+ import type { ObjectAnimations, BrownianDustParticlesConfig } from '@3driseai/3drise-core';
3
3
  import type { ObjectReadyProps } from '../../types/objectReady';
4
4
  export interface BrownianDustParticlesProps extends ObjectReadyProps {
5
+ /** Scene object id: the controller finds the live-edit handle by it. */
6
+ id?: string;
5
7
  /** World size of the object (units); the object's transform scales it further. */
6
8
  size: number;
7
9
  config: BrownianDustParticlesConfig;
8
- animations?: AnimationOptions[];
10
+ /** Per-domain keyframes; the `particles` domain drives this object's config knobs. */
11
+ animations?: ObjectAnimations;
9
12
  }
10
13
  declare const BrownianDustParticles: React.FC<BrownianDustParticlesProps>;
11
14
  export default BrownianDustParticles;
@@ -1 +1 @@
1
- {"version":3,"file":"BrownianDustParticles.d.ts","sourceRoot":"","sources":["../../../src/components/particles/BrownianDustParticles.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsD,MAAM,OAAO,CAAC;AAG3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AAK3F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,0BAA2B,SAAQ,gBAAgB;IAChE,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,2BAA2B,CAAC;IACpC,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACnC;AAsqBD,QAAA,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC,0BAA0B,CA6B/D,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
1
+ {"version":3,"file":"BrownianDustParticles.d.ts","sourceRoot":"","sources":["../../../src/components/particles/BrownianDustParticles.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmE,MAAM,OAAO,CAAC;AAIxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AAM3F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,0BAA2B,SAAQ,gBAAgB;IAChE,wEAAwE;IACxE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,2BAA2B,CAAC;IACpC,sFAAsF;IACtF,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACjC;AA4qBD,QAAA,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC,0BAA0B,CAiD/D,CAAC;AAEF,eAAe,qBAAqB,CAAC"}
@@ -1,11 +1,13 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useEffect, useLayoutEffect, useMemo, useRef } from 'react';
2
+ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react';
3
3
  import * as THREE from 'three';
4
4
  import { useFrame } from '@react-three/fiber';
5
- import { applyGenerativeAnimations } from '../../utils/handleGenerativeEffectsAnimations.js';
5
+ import { GENERATIVE_PARTICLES_ANIMATABLE } from '@3driseai/3drise-core';
6
+ import { applyConfigKeyframeAnimations } from '../../utils/configAnimations.js';
6
7
  import { createPointerUniforms, stepPointerField, usePointerField } from '../../utils/pointerField.js';
7
8
  import { createTrailUniforms, applyTrailConfig, createCpuTrail } from '../../utils/particleTrails.js';
8
9
  import { useObjectReady } from '../../hooks/useObjectReady.js';
10
+ import { useConfigLiveHandle } from '../../hooks/useConfigLive.js';
9
11
  /**
10
12
  * Sunbeam Dust — Dust drifting in window light: Langevin motion in Kolmogorov turbulence, mullion shadows, forward scattering, glints and bokeh; the cursor is a hand, a torch or a charged rod. Use for interiors and cinematic scenes.
11
13
  */
@@ -731,9 +733,12 @@ const DEFAULTS = {
731
733
  trailColor: '#ffb870',
732
734
  trailShare: 0.3,
733
735
  };
734
- const BrownianDustParticles = ({ size, config, animations, onObjectReady, onObjectDispose }) => {
736
+ /** Knobs that rebuild the simulation: they change on commit, never through the live path. */
737
+ const ANIMATABLE = new Set(GENERATIVE_PARTICLES_ANIMATABLE.brownianDust.map((p) => p.value));
738
+ /** Knobs that rebuild the simulation: they change on commit, never through the live path. */
739
+ const STRUCTURAL = ['particleCount'];
740
+ const BrownianDustParticles = ({ id, size, config, animations, onObjectReady, onObjectDispose }) => {
735
741
  const rootRef = useRef(null);
736
- const animCache = useRef({});
737
742
  useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
738
743
  const s = size > 0 ? size : 1;
739
744
  const cfg = useMemo(() => ({ ...DEFAULTS, ...config }), [config]);
@@ -746,11 +751,28 @@ const BrownianDustParticles = ({ size, config, animations, onObjectReady, onObje
746
751
  applyConfig(uniforms, cfg, s);
747
752
  instance.apply(cfg);
748
753
  }, [instance, uniforms, cfg, s]);
754
+ // Slider drags and keyframe animations are both overrides on the committed config.
755
+ // This is the one place they are merged and pushed, so they compose instead of
756
+ // overwriting each other; the animation wins, because it is the active driver.
757
+ const keyframes = animations?.particles?.animations;
758
+ const animOverride = useRef({});
759
+ const animFrom = useRef({});
760
+ const animStart = useRef(0);
761
+ // Editing a keyframe restarts its cycle rather than dropping the user mid-way.
762
+ useEffect(() => { animStart.current = performance.now(); animFrom.current = {}; }, [keyframes]);
763
+ const push = useCallback((overrides) => {
764
+ const merged = { ...cfg, ...overrides, ...animOverride.current };
765
+ const z = merged.size;
766
+ applyConfig(uniforms, merged, z && z > 0 ? z : s);
767
+ }, [cfg, uniforms, s]);
768
+ const live = useConfigLiveHandle(rootRef, id, 'particles', cfg, STRUCTURAL, push);
749
769
  const pointer = usePointerField();
750
770
  useFrame((state, delta) => {
751
771
  instance.update(Math.min(delta, 0.1), state.camera, pointer);
752
- if (animations?.length)
753
- applyGenerativeAnimations(instance.materials, animations, animCache.current, Date.now(), false);
772
+ if (keyframes?.length) {
773
+ applyConfigKeyframeAnimations(keyframes, animFrom.current, cfg, animOverride.current, performance.now() - animStart.current, ANIMATABLE);
774
+ push(live.current);
775
+ }
754
776
  });
755
777
  return (_jsx("group", { ref: rootRef, children: _jsx("primitive", { object: instance.object }) }));
756
778
  };
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ import type { ObjectAnimations, ChladniSandParticlesConfig } from '@3driseai/3drise-core';
3
+ import type { ObjectReadyProps } from '../../types/objectReady';
4
+ export interface ChladniSandParticlesProps extends ObjectReadyProps {
5
+ /** Scene object id: the controller finds the live-edit handle by it. */
6
+ id?: string;
7
+ /** World size of the object (units); the object's transform scales it further. */
8
+ size: number;
9
+ config: ChladniSandParticlesConfig;
10
+ /** Per-domain keyframes; the `particles` domain drives this object's config knobs. */
11
+ animations?: ObjectAnimations;
12
+ }
13
+ declare const ChladniSandParticles: React.FC<ChladniSandParticlesProps>;
14
+ export default ChladniSandParticles;
15
+ //# sourceMappingURL=ChladniSandParticles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChladniSandParticles.d.ts","sourceRoot":"","sources":["../../../src/components/particles/ChladniSandParticles.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmE,MAAM,OAAO,CAAC;AAIxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AAM1F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,yBAA0B,SAAQ,gBAAgB;IAC/D,wEAAwE;IACxE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,0BAA0B,CAAC;IACnC,sFAAsF;IACtF,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACjC;AAsvBD,QAAA,MAAM,oBAAoB,EAAE,KAAK,CAAC,EAAE,CAAC,yBAAyB,CAiD7D,CAAC;AAEF,eAAe,oBAAoB,CAAC"}