@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.
- package/dist/components/AnimatedObject.d.ts.map +1 -1
- package/dist/components/AnimatedObject.js +3 -2
- package/dist/components/CreatedObject.d.ts.map +1 -1
- package/dist/components/CreatedObject.js +18 -14
- package/dist/components/ModelMaterialPassContext.d.ts +7 -0
- package/dist/components/ModelMaterialPassContext.d.ts.map +1 -0
- package/dist/components/ModelMaterialPassContext.js +7 -0
- package/dist/components/ModelPrimitive.d.ts.map +1 -1
- package/dist/components/ModelPrimitive.js +7 -1
- package/dist/components/ObjectNode.d.ts.map +1 -1
- package/dist/components/ObjectNode.js +10 -6
- package/dist/components/ParticlesGenerator.d.ts.map +1 -1
- package/dist/components/ParticlesGenerator.js +11 -5
- package/dist/components/particles/BioluminescentWakeParticles.d.ts +5 -2
- package/dist/components/particles/BioluminescentWakeParticles.d.ts.map +1 -1
- package/dist/components/particles/BioluminescentWakeParticles.js +28 -6
- package/dist/components/particles/BrownianDustParticles.d.ts +5 -2
- package/dist/components/particles/BrownianDustParticles.d.ts.map +1 -1
- package/dist/components/particles/BrownianDustParticles.js +28 -6
- package/dist/components/particles/ChladniSandParticles.d.ts +15 -0
- package/dist/components/particles/ChladniSandParticles.d.ts.map +1 -0
- package/dist/components/particles/ChladniSandParticles.js +912 -0
- package/dist/components/particles/ElectronOrbitalsParticles.d.ts +5 -2
- package/dist/components/particles/ElectronOrbitalsParticles.d.ts.map +1 -1
- package/dist/components/particles/ElectronOrbitalsParticles.js +28 -6
- package/dist/components/particles/FaradayPowderParticles.d.ts +15 -0
- package/dist/components/particles/FaradayPowderParticles.d.ts.map +1 -0
- package/dist/components/particles/FaradayPowderParticles.js +783 -0
- package/dist/components/particles/GrinderSparksParticles.d.ts +5 -2
- package/dist/components/particles/GrinderSparksParticles.d.ts.map +1 -1
- package/dist/components/particles/GrinderSparksParticles.js +37 -13
- package/dist/components/particles/NeonRainParticles.d.ts +15 -0
- package/dist/components/particles/NeonRainParticles.d.ts.map +1 -0
- package/dist/components/particles/NeonRainParticles.js +716 -0
- package/dist/components/particles/TidalStreamParticles.d.ts +5 -2
- package/dist/components/particles/TidalStreamParticles.d.ts.map +1 -1
- package/dist/components/particles/TidalStreamParticles.js +28 -6
- package/dist/components/particles/index.d.ts +6 -0
- package/dist/components/particles/index.d.ts.map +1 -1
- package/dist/components/particles/index.js +3 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +2 -1
- package/dist/hooks/index.d.ts +3 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +2 -0
- package/dist/hooks/useConfigLive.d.ts +54 -0
- package/dist/hooks/useConfigLive.d.ts.map +1 -0
- package/dist/hooks/useConfigLive.js +100 -0
- package/dist/hooks/useMaterialApplication.d.ts.map +1 -1
- package/dist/hooks/useMaterialApplication.js +1 -0
- package/dist/hooks/useNodeOverrides.d.ts +13 -0
- package/dist/hooks/useNodeOverrides.d.ts.map +1 -0
- package/dist/hooks/useNodeOverrides.js +254 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/utils/configAnimations.d.ts +43 -0
- package/dist/utils/configAnimations.d.ts.map +1 -0
- package/dist/utils/configAnimations.js +73 -0
- package/dist/utils/particleTrails.d.ts.map +1 -1
- package/dist/utils/particleTrails.js +17 -8
- package/package.json +2 -2
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ObjectAnimations, TidalStreamParticlesConfig } from '@3driseai/3drise-core';
|
|
3
3
|
import type { ObjectReadyProps } from '../../types/objectReady';
|
|
4
4
|
export interface TidalStreamParticlesProps 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: TidalStreamParticlesConfig;
|
|
8
|
-
|
|
10
|
+
/** Per-domain keyframes; the `particles` domain drives this object's config knobs. */
|
|
11
|
+
animations?: ObjectAnimations;
|
|
9
12
|
}
|
|
10
13
|
declare const TidalStreamParticles: React.FC<TidalStreamParticlesProps>;
|
|
11
14
|
export default TidalStreamParticles;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TidalStreamParticles.d.ts","sourceRoot":"","sources":["../../../src/components/particles/TidalStreamParticles.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"TidalStreamParticles.d.ts","sourceRoot":"","sources":["../../../src/components/particles/TidalStreamParticles.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;AAmtBD,QAAA,MAAM,oBAAoB,EAAE,KAAK,CAAC,EAAE,CAAC,yBAAyB,CAiD7D,CAAC;AAEF,eAAe,oBAAoB,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 {
|
|
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
|
* Tidal Stream — A spiral galaxy shredding dwarf satellites into tidal streams: symplectic orbits, density-wave arms, Doppler-shifted blackbody stars; the cursor is a subhalo, black hole or spectroscope. Use for space scenes.
|
|
11
13
|
*/
|
|
@@ -762,9 +764,12 @@ const DEFAULTS = {
|
|
|
762
764
|
trailColor: '#7fa8ff',
|
|
763
765
|
trailShare: 0.25,
|
|
764
766
|
};
|
|
765
|
-
|
|
767
|
+
/** Knobs that rebuild the simulation: they change on commit, never through the live path. */
|
|
768
|
+
const ANIMATABLE = new Set(GENERATIVE_PARTICLES_ANIMATABLE.tidalStream.map((p) => p.value));
|
|
769
|
+
/** Knobs that rebuild the simulation: they change on commit, never through the live path. */
|
|
770
|
+
const STRUCTURAL = ['particleCount', 'satelliteCount', 'orbitEccentricity', 'streamAge', 'discStars'];
|
|
771
|
+
const TidalStreamParticles = ({ id, size, config, animations, onObjectReady, onObjectDispose }) => {
|
|
766
772
|
const rootRef = useRef(null);
|
|
767
|
-
const animCache = useRef({});
|
|
768
773
|
useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
|
|
769
774
|
const s = size > 0 ? size : 1;
|
|
770
775
|
const cfg = useMemo(() => ({ ...DEFAULTS, ...config }), [config]);
|
|
@@ -777,11 +782,28 @@ const TidalStreamParticles = ({ size, config, animations, onObjectReady, onObjec
|
|
|
777
782
|
applyConfig(uniforms, cfg, s);
|
|
778
783
|
instance.apply(cfg);
|
|
779
784
|
}, [instance, uniforms, cfg, s]);
|
|
785
|
+
// Slider drags and keyframe animations are both overrides on the committed config.
|
|
786
|
+
// This is the one place they are merged and pushed, so they compose instead of
|
|
787
|
+
// overwriting each other; the animation wins, because it is the active driver.
|
|
788
|
+
const keyframes = animations?.particles?.animations;
|
|
789
|
+
const animOverride = useRef({});
|
|
790
|
+
const animFrom = useRef({});
|
|
791
|
+
const animStart = useRef(0);
|
|
792
|
+
// Editing a keyframe restarts its cycle rather than dropping the user mid-way.
|
|
793
|
+
useEffect(() => { animStart.current = performance.now(); animFrom.current = {}; }, [keyframes]);
|
|
794
|
+
const push = useCallback((overrides) => {
|
|
795
|
+
const merged = { ...cfg, ...overrides, ...animOverride.current };
|
|
796
|
+
const z = merged.size;
|
|
797
|
+
applyConfig(uniforms, merged, z && z > 0 ? z : s);
|
|
798
|
+
}, [cfg, uniforms, s]);
|
|
799
|
+
const live = useConfigLiveHandle(rootRef, id, 'particles', cfg, STRUCTURAL, push);
|
|
780
800
|
const pointer = usePointerField();
|
|
781
801
|
useFrame((state, delta) => {
|
|
782
802
|
instance.update(Math.min(delta, 0.1), state.camera, pointer);
|
|
783
|
-
if (
|
|
784
|
-
|
|
803
|
+
if (keyframes?.length) {
|
|
804
|
+
applyConfigKeyframeAnimations(keyframes, animFrom.current, cfg, animOverride.current, performance.now() - animStart.current, ANIMATABLE);
|
|
805
|
+
push(live.current);
|
|
806
|
+
}
|
|
785
807
|
});
|
|
786
808
|
return (_jsx("group", { ref: rootRef, children: _jsx("primitive", { object: instance.object }) }));
|
|
787
809
|
};
|
|
@@ -8,4 +8,10 @@ export { default as ElectronOrbitalsParticles } from './ElectronOrbitalsParticle
|
|
|
8
8
|
export type { ElectronOrbitalsParticlesProps } from './ElectronOrbitalsParticles';
|
|
9
9
|
export { default as GrinderSparksParticles } from './GrinderSparksParticles';
|
|
10
10
|
export type { GrinderSparksParticlesProps } from './GrinderSparksParticles';
|
|
11
|
+
export { default as ChladniSandParticles } from './ChladniSandParticles';
|
|
12
|
+
export type { ChladniSandParticlesProps } from './ChladniSandParticles';
|
|
13
|
+
export { default as FaradayPowderParticles } from './FaradayPowderParticles';
|
|
14
|
+
export type { FaradayPowderParticlesProps } from './FaradayPowderParticles';
|
|
15
|
+
export { default as NeonRainParticles } from './NeonRainParticles';
|
|
16
|
+
export type { NeonRainParticlesProps } from './NeonRainParticles';
|
|
11
17
|
//# 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"}
|
|
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"}
|
|
@@ -4,3 +4,6 @@ export { default as BrownianDustParticles } from './BrownianDustParticles.js';
|
|
|
4
4
|
export { default as BioluminescentWakeParticles } from './BioluminescentWakeParticles.js';
|
|
5
5
|
export { default as ElectronOrbitalsParticles } from './ElectronOrbitalsParticles.js';
|
|
6
6
|
export { default as GrinderSparksParticles } from './GrinderSparksParticles.js';
|
|
7
|
+
export { default as ChladniSandParticles } from './ChladniSandParticles.js';
|
|
8
|
+
export { default as FaradayPowderParticles } from './FaradayPowderParticles.js';
|
|
9
|
+
export { default as NeonRainParticles } from './NeonRainParticles.js';
|
package/dist/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,gBAAgB,oCAA2B,CAAC;AACzD,eAAO,MAAM,qBAAqB,
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,gBAAgB,oCAA2B,CAAC;AACzD,eAAO,MAAM,qBAAqB,aAehC,CAAC"}
|
package/dist/constants.js
CHANGED
|
@@ -12,10 +12,11 @@ export const SELF_MANAGED_MATERIAL = new Set([
|
|
|
12
12
|
'effect',
|
|
13
13
|
'space',
|
|
14
14
|
'grid',
|
|
15
|
+
'particles',
|
|
15
16
|
// The water surface owns its shader — one material for both hosts, built
|
|
16
17
|
// around the wave field. Left out of this set, useMaterialApplication
|
|
17
18
|
// overwrites it with whatever the registry has under materialType, which for
|
|
18
19
|
// a legacy ocean is the old flat-plane water material.
|
|
19
|
-
'ocean',
|
|
20
|
+
// 'ocean',
|
|
20
21
|
// 'moon'
|
|
21
22
|
]);
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ export { useClipmap } from './useClipmap';
|
|
|
17
17
|
export type { OceanLiveHandle } from './useOceanLive';
|
|
18
18
|
export { OCEAN_LIVE_KEY, useOceanLive } from './useOceanLive';
|
|
19
19
|
export { useOceanMaterial } from './useOceanMaterial';
|
|
20
|
+
export type { ConfigLiveHandle, ConfigLiveValue, ConfigLiveFamily } from './useConfigLive';
|
|
21
|
+
export { CONFIG_LIVE_KEY, useConfigLive, useConfigLiveHandle } from './useConfigLive';
|
|
20
22
|
export type { UseOceanMaterialOptions, OceanMaterialHandle } from './useOceanMaterial';
|
|
21
23
|
export type { UseContinuousEffectsOptions, UseContinuousEffectsResult } from './useContinuousEffects';
|
|
22
24
|
export { useContinuousEffects } from './useContinuousEffects';
|
|
@@ -55,6 +57,7 @@ export { useObjectEdges } from './useObjectEdges';
|
|
|
55
57
|
export type { ObjectEffectsFunctions, ObjectEffectsState, UseObjectEffectsReturn } from './useObjectEffects';
|
|
56
58
|
export { useObjectEffects } from './useObjectEffects';
|
|
57
59
|
export { useModelObject } from './useModelObject';
|
|
60
|
+
export { useNodeOverrides, reconcileNodeOverrides, restoreNodeOverrides } from './useNodeOverrides';
|
|
58
61
|
export { useObjectReady, useObjectReadyReporter } from './useObjectReady';
|
|
59
62
|
export type { GridSurfaceOptions, GridSurfaceUniforms } from './useGridSurface';
|
|
60
63
|
export { useGridSurface } from './useGridSurface';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"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"}
|
package/dist/hooks/index.js
CHANGED
|
@@ -13,6 +13,7 @@ export { useTerrainClipmap } from './useTerrainClipmap.js';
|
|
|
13
13
|
export { useClipmap } from './useClipmap.js';
|
|
14
14
|
export { OCEAN_LIVE_KEY, useOceanLive } from './useOceanLive.js';
|
|
15
15
|
export { useOceanMaterial } from './useOceanMaterial.js';
|
|
16
|
+
export { CONFIG_LIVE_KEY, useConfigLive, useConfigLiveHandle } from './useConfigLive.js';
|
|
16
17
|
export { useContinuousEffects } from './useContinuousEffects.js';
|
|
17
18
|
export { useCubeCamera } from './useCubeCamera.js';
|
|
18
19
|
export { useEnvironmentProbe } from './useEnvironmentProbe.js';
|
|
@@ -39,6 +40,7 @@ export { useObjectAnimation } from './useObjectAnimation.js';
|
|
|
39
40
|
export { useObjectEdges } from './useObjectEdges.js';
|
|
40
41
|
export { useObjectEffects } from './useObjectEffects.js';
|
|
41
42
|
export { useModelObject } from './useModelObject.js';
|
|
43
|
+
export { useNodeOverrides, reconcileNodeOverrides, restoreNodeOverrides } from './useNodeOverrides.js';
|
|
42
44
|
export { useObjectReady, useObjectReadyReporter } from './useObjectReady.js';
|
|
43
45
|
export { useGridSurface } from './useGridSurface.js';
|
|
44
46
|
export { useOrbitControls } from './useOrbitControls.js';
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { type MutableRefObject, type RefObject } from 'react';
|
|
2
|
+
import type { Object3D } from 'three';
|
|
3
|
+
/**
|
|
4
|
+
* Live config editing for the generative families (particles now, effects next),
|
|
5
|
+
* the counterpart to `useOceanLive` / `useTerrainLive`.
|
|
6
|
+
*
|
|
7
|
+
* A slider drag fires on every pointer-move. Routing that through Redux hands the
|
|
8
|
+
* component a new config sixty times a second; every continuous knob only touches
|
|
9
|
+
* uniforms, so the controller writes straight into them instead, and Redux only
|
|
10
|
+
* hears about it on slider end.
|
|
11
|
+
*
|
|
12
|
+
* const live = useConfigLive(settings.id);
|
|
13
|
+
* <ParticlesControllerUi onLiveChange={live.set} onSettingsChange={dispatch} ... />
|
|
14
|
+
*
|
|
15
|
+
* The renderer side registers the handle and owns the merge:
|
|
16
|
+
*
|
|
17
|
+
* const liveRef = useConfigLiveHandle(rootRef, id, 'particles', STRUCTURAL, push);
|
|
18
|
+
* // push(overrides) -> applyConfig(uniforms, { ...config, ...overrides, ...animated }, size)
|
|
19
|
+
*
|
|
20
|
+
* Structural knobs rebuild the object, so the handle ignores them: they change on
|
|
21
|
+
* release, through the committed config, exactly once.
|
|
22
|
+
*
|
|
23
|
+
* The handle is found by the object's id, resolved at call time (see the note on
|
|
24
|
+
* useOceanLive: an object resolved once while the panel opens before the object
|
|
25
|
+
* exists stays null forever). It lives on its own userData key and carries its own
|
|
26
|
+
* id, so it never competes with the scene's `userData.id` used by selection.
|
|
27
|
+
*/
|
|
28
|
+
/** Where a generative component stashes its handle on its root group's userData. */
|
|
29
|
+
export declare const CONFIG_LIVE_KEY = "__configLive";
|
|
30
|
+
export type ConfigLiveValue = number | string | boolean;
|
|
31
|
+
/** Generative families that support live config editing. */
|
|
32
|
+
export type ConfigLiveFamily = 'particles' | 'effects';
|
|
33
|
+
export interface ConfigLiveHandle {
|
|
34
|
+
id: string;
|
|
35
|
+
family: ConfigLiveFamily;
|
|
36
|
+
/** False once the component has unmounted or re-registered. */
|
|
37
|
+
alive: boolean;
|
|
38
|
+
set(key: string, value: ConfigLiveValue): void;
|
|
39
|
+
}
|
|
40
|
+
/** Client side: a stable `set(key, value)` for the generative object with this id. */
|
|
41
|
+
export declare function useConfigLive(id?: string | null): {
|
|
42
|
+
set(key: string, value: ConfigLiveValue): void;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Renderer side: registers the live handle and returns the ref holding everything
|
|
46
|
+
* dragged since the last commit. `apply` is called with that ref's contents on every
|
|
47
|
+
* live change; the component owns the merge, so keyframe animations compose with it.
|
|
48
|
+
*
|
|
49
|
+
* Pass `config` so a committed config drops the drag state — the commit *is* the
|
|
50
|
+
* same value, and keeping the override would pin the knob against later edits.
|
|
51
|
+
*/
|
|
52
|
+
export declare function useConfigLiveHandle(rootRef: RefObject<Object3D | null>, id: string | undefined, family: ConfigLiveFamily, config: object, structural: readonly string[], apply: (overrides: Record<string, ConfigLiveValue>) => void): MutableRefObject<Record<string, ConfigLiveValue>>;
|
|
53
|
+
export default useConfigLive;
|
|
54
|
+
//# sourceMappingURL=useConfigLive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useConfigLive.d.ts","sourceRoot":"","sources":["../../src/hooks/useConfigLive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,gBAAgB,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAChG,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAGtC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,oFAAoF;AACpF,eAAO,MAAM,eAAe,iBAAiB,CAAC;AAE9C,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAExD,4DAA4D;AAC5D,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG,SAAS,CAAC;AAEvD,MAAM,WAAW,gBAAgB;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,gBAAgB,CAAC;IACzB,+DAA+D;IAC/D,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;CAClD;AAgBD,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG;IAAE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI,CAAA;CAAE,CAYpG;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAC/B,OAAO,EAAE,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC,EACnC,EAAE,EAAE,MAAM,GAAG,SAAS,EACtB,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,KAAK,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,KAAK,IAAI,GAC5D,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CA8BnD;AAED,eAAe,aAAa,CAAC"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { useLayoutEffect, useMemo, useRef } from 'react';
|
|
2
|
+
import { SceneStore } from '@3driseai/3drise-core';
|
|
3
|
+
/**
|
|
4
|
+
* Live config editing for the generative families (particles now, effects next),
|
|
5
|
+
* the counterpart to `useOceanLive` / `useTerrainLive`.
|
|
6
|
+
*
|
|
7
|
+
* A slider drag fires on every pointer-move. Routing that through Redux hands the
|
|
8
|
+
* component a new config sixty times a second; every continuous knob only touches
|
|
9
|
+
* uniforms, so the controller writes straight into them instead, and Redux only
|
|
10
|
+
* hears about it on slider end.
|
|
11
|
+
*
|
|
12
|
+
* const live = useConfigLive(settings.id);
|
|
13
|
+
* <ParticlesControllerUi onLiveChange={live.set} onSettingsChange={dispatch} ... />
|
|
14
|
+
*
|
|
15
|
+
* The renderer side registers the handle and owns the merge:
|
|
16
|
+
*
|
|
17
|
+
* const liveRef = useConfigLiveHandle(rootRef, id, 'particles', STRUCTURAL, push);
|
|
18
|
+
* // push(overrides) -> applyConfig(uniforms, { ...config, ...overrides, ...animated }, size)
|
|
19
|
+
*
|
|
20
|
+
* Structural knobs rebuild the object, so the handle ignores them: they change on
|
|
21
|
+
* release, through the committed config, exactly once.
|
|
22
|
+
*
|
|
23
|
+
* The handle is found by the object's id, resolved at call time (see the note on
|
|
24
|
+
* useOceanLive: an object resolved once while the panel opens before the object
|
|
25
|
+
* exists stays null forever). It lives on its own userData key and carries its own
|
|
26
|
+
* id, so it never competes with the scene's `userData.id` used by selection.
|
|
27
|
+
*/
|
|
28
|
+
/** Where a generative component stashes its handle on its root group's userData. */
|
|
29
|
+
export const CONFIG_LIVE_KEY = '__configLive';
|
|
30
|
+
/** Keys that never go through the live path, whatever the family. */
|
|
31
|
+
const ALWAYS_COMMIT = ['trailMode', 'trailShare', 'enabled', 'type'];
|
|
32
|
+
function findHandle(id) {
|
|
33
|
+
if (!id || !SceneStore.scene)
|
|
34
|
+
return undefined;
|
|
35
|
+
let found;
|
|
36
|
+
SceneStore.scene.traverse((o) => {
|
|
37
|
+
if (found)
|
|
38
|
+
return;
|
|
39
|
+
const h = o.userData?.[CONFIG_LIVE_KEY];
|
|
40
|
+
if (h && h.id === id)
|
|
41
|
+
found = h;
|
|
42
|
+
});
|
|
43
|
+
return found;
|
|
44
|
+
}
|
|
45
|
+
/** Client side: a stable `set(key, value)` for the generative object with this id. */
|
|
46
|
+
export function useConfigLive(id) {
|
|
47
|
+
return useMemo(() => {
|
|
48
|
+
let cached;
|
|
49
|
+
return {
|
|
50
|
+
set: (key, value) => {
|
|
51
|
+
if (!id)
|
|
52
|
+
return;
|
|
53
|
+
// one scene traversal per drag, not per pointer-move
|
|
54
|
+
if (!cached || !cached.alive || cached.id !== id)
|
|
55
|
+
cached = findHandle(id);
|
|
56
|
+
cached?.set(key, value);
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}, [id]);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Renderer side: registers the live handle and returns the ref holding everything
|
|
63
|
+
* dragged since the last commit. `apply` is called with that ref's contents on every
|
|
64
|
+
* live change; the component owns the merge, so keyframe animations compose with it.
|
|
65
|
+
*
|
|
66
|
+
* Pass `config` so a committed config drops the drag state — the commit *is* the
|
|
67
|
+
* same value, and keeping the override would pin the knob against later edits.
|
|
68
|
+
*/
|
|
69
|
+
export function useConfigLiveHandle(rootRef, id, family, config, structural, apply) {
|
|
70
|
+
const live = useRef({});
|
|
71
|
+
const applyRef = useRef(apply);
|
|
72
|
+
applyRef.current = apply;
|
|
73
|
+
// the committed config supersedes whatever the drag wrote
|
|
74
|
+
useLayoutEffect(() => { live.current = {}; }, [config]);
|
|
75
|
+
useLayoutEffect(() => {
|
|
76
|
+
const root = rootRef.current;
|
|
77
|
+
if (!root || !id)
|
|
78
|
+
return;
|
|
79
|
+
const skip = new Set([...structural, ...ALWAYS_COMMIT]);
|
|
80
|
+
const handle = {
|
|
81
|
+
id,
|
|
82
|
+
family,
|
|
83
|
+
alive: true,
|
|
84
|
+
set(key, value) {
|
|
85
|
+
if (!handle.alive || skip.has(key))
|
|
86
|
+
return;
|
|
87
|
+
live.current = { ...live.current, [key]: value };
|
|
88
|
+
applyRef.current(live.current);
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
root.userData[CONFIG_LIVE_KEY] = handle;
|
|
92
|
+
return () => {
|
|
93
|
+
handle.alive = false;
|
|
94
|
+
if (root.userData[CONFIG_LIVE_KEY] === handle)
|
|
95
|
+
delete root.userData[CONFIG_LIVE_KEY];
|
|
96
|
+
};
|
|
97
|
+
}, [rootRef, id, family, structural]);
|
|
98
|
+
return live;
|
|
99
|
+
}
|
|
100
|
+
export default useConfigLive;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMaterialApplication.d.ts","sourceRoot":"","sources":["../../src/hooks/useMaterialApplication.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAG9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG/D,eAAO,MAAM,sBAAsB,GAAI,QAAQ,cAAc,EAAE,kBAAkB,gBAAgB,EAAE,4BAA4B,OAAO,EAAE,YAAY,MAAM,IAAI,KAAG,
|
|
1
|
+
{"version":3,"file":"useMaterialApplication.d.ts","sourceRoot":"","sources":["../../src/hooks/useMaterialApplication.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAG9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG/D,eAAO,MAAM,sBAAsB,GAAI,QAAQ,cAAc,EAAE,kBAAkB,gBAAgB,EAAE,4BAA4B,OAAO,EAAE,YAAY,MAAM,IAAI,KAAG,IAwIhK,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Object3D } from 'three';
|
|
2
|
+
import type { NodeOverrides } from '@3driseai/3drise-core';
|
|
3
|
+
export declare function reconcileNodeOverrides(root: Object3D, overrides: NodeOverrides | undefined, master?: Object3D | null): void;
|
|
4
|
+
/** Put every node this root touched back to its original state and free our materials. */
|
|
5
|
+
export declare function restoreNodeOverrides(root: Object3D, master?: Object3D | null): void;
|
|
6
|
+
/**
|
|
7
|
+
* @param object the mounted model clone (one layout slot)
|
|
8
|
+
* @param overrides createdObject.config.nodeOverrides (getNodeOverrides)
|
|
9
|
+
* @param master the cache master scene (peekModelAsset(url)?.scene) — source of originals
|
|
10
|
+
*/
|
|
11
|
+
export declare function useNodeOverrides(object: Object3D | null, overrides: NodeOverrides | undefined, master?: Object3D | null): void;
|
|
12
|
+
export default useNodeOverrides;
|
|
13
|
+
//# sourceMappingURL=useNodeOverrides.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useNodeOverrides.d.ts","sourceRoot":"","sources":["../../src/hooks/useNodeOverrides.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAY,QAAQ,EAAE,MAAM,OAAO,CAAC;AAUhD,OAAO,KAAK,EAAwD,aAAa,EAAgB,MAAM,uBAAuB,CAAC;AAiM/H,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,GAAG,SAAS,EAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,IAAI,GAAG,IAAI,CA0C3H;AAED,0FAA0F;AAC1F,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,IAAI,GAAG,IAAI,CAEnF;AAID;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI,EAAE,SAAS,EAAE,aAAa,GAAG,SAAS,EAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,IAAI,GAAG,IAAI,CAa9H;AAED,eAAe,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// useNodeOverrides — apply per-mesh overrides to a mounted model instance
|
|
3
|
+
// =============================================================================
|
|
4
|
+
//
|
|
5
|
+
// Reconciles `createdObject.config.nodeOverrides` (keyed by node name, or "/i/j" index path for
|
|
6
|
+
// unnamed nodes — see core nodeOverrides.ts) onto one model clone:
|
|
7
|
+
// visibility, local transform, and material (tweak the mesh's own material, or replace it
|
|
8
|
+
// with a full 3drise material).
|
|
9
|
+
//
|
|
10
|
+
// Reconcile, not patch: every run computes the desired state for every node it has ever
|
|
11
|
+
// touched, so removing an override (or one field of it) restores the original value.
|
|
12
|
+
//
|
|
13
|
+
// Originals:
|
|
14
|
+
// - transform / visibility come from the cache master (the never-mounted GLB scene), so
|
|
15
|
+
// a gizmo that moved the live node before the override was saved cannot corrupt them.
|
|
16
|
+
// - material "base" is whatever the model-level pass left on the mesh (GLB material, or
|
|
17
|
+
// the object's materialSettings when `apply` is on). Materials this hook creates are
|
|
18
|
+
// tagged so they are never mistaken for a base.
|
|
19
|
+
//
|
|
20
|
+
import { useContext, useEffect } from 'react';
|
|
21
|
+
import { MaterialRegistryAPI, NODE_OVERRIDE_MATERIAL_KEY, applyMaterialTweak, collectOverrideTargets, createAndApplyMaterial, resolveNodeKey, resolveTextures, } from '@3driseai/3drise-core';
|
|
22
|
+
import { ModelMaterialPassContext } from '../components/ModelMaterialPassContext.js';
|
|
23
|
+
const nodeStates = new WeakMap();
|
|
24
|
+
const touchedByRoot = new WeakMap();
|
|
25
|
+
const stateOf = (node) => {
|
|
26
|
+
let s = nodeStates.get(node);
|
|
27
|
+
if (!s) {
|
|
28
|
+
s = {};
|
|
29
|
+
nodeStates.set(node, s);
|
|
30
|
+
}
|
|
31
|
+
return s;
|
|
32
|
+
};
|
|
33
|
+
const isOwn = (material) => {
|
|
34
|
+
if (!material)
|
|
35
|
+
return false;
|
|
36
|
+
const list = Array.isArray(material) ? material : [material];
|
|
37
|
+
return list.some(m => m?.userData?.[NODE_OVERRIDE_MATERIAL_KEY]);
|
|
38
|
+
};
|
|
39
|
+
const tag = (m) => {
|
|
40
|
+
m.userData[NODE_OVERRIDE_MATERIAL_KEY] = true;
|
|
41
|
+
return m;
|
|
42
|
+
};
|
|
43
|
+
const disposeOwn = (s) => {
|
|
44
|
+
if (s.ownInstance) {
|
|
45
|
+
try {
|
|
46
|
+
MaterialRegistryAPI.releaseMaterial(s.ownInstance);
|
|
47
|
+
}
|
|
48
|
+
catch { /* ignore */ }
|
|
49
|
+
}
|
|
50
|
+
else if (s.ownMaterial) {
|
|
51
|
+
(Array.isArray(s.ownMaterial) ? s.ownMaterial : [s.ownMaterial]).forEach(m => m?.dispose());
|
|
52
|
+
}
|
|
53
|
+
s.ownMaterial = undefined;
|
|
54
|
+
s.ownInstance = undefined;
|
|
55
|
+
s.materialKey = undefined;
|
|
56
|
+
};
|
|
57
|
+
/** Original local transform + visibility for `node`: master first, snapshot fallback. */
|
|
58
|
+
const originalOf = (node) => {
|
|
59
|
+
const m = stateOf(node).masterNode;
|
|
60
|
+
if (m) {
|
|
61
|
+
return {
|
|
62
|
+
position: [m.position.x, m.position.y, m.position.z],
|
|
63
|
+
rotation: [m.rotation.x, m.rotation.y, m.rotation.z],
|
|
64
|
+
scale: [m.scale.x, m.scale.y, m.scale.z],
|
|
65
|
+
visible: m.visible,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const s = stateOf(node);
|
|
69
|
+
if (!s.snapshot) {
|
|
70
|
+
s.snapshot = {
|
|
71
|
+
position: [node.position.x, node.position.y, node.position.z],
|
|
72
|
+
rotation: [node.rotation.x, node.rotation.y, node.rotation.z],
|
|
73
|
+
scale: [node.scale.x, node.scale.y, node.scale.z],
|
|
74
|
+
visible: node.visible,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return s.snapshot;
|
|
78
|
+
};
|
|
79
|
+
// ── Transform / visibility ──────────────────────────────────────────────────
|
|
80
|
+
const reconcileTransform = (node, override) => {
|
|
81
|
+
const s = stateOf(node);
|
|
82
|
+
const hasTransform = !!(override?.position || override?.rotation || override?.scale);
|
|
83
|
+
const hasVisible = override?.visible !== undefined;
|
|
84
|
+
if (!hasTransform && !hasVisible && !s.transformTouched && !s.visibleTouched)
|
|
85
|
+
return;
|
|
86
|
+
const orig = originalOf(node);
|
|
87
|
+
if (hasTransform || s.transformTouched) {
|
|
88
|
+
node.position.set(...(override?.position ?? orig.position));
|
|
89
|
+
node.rotation.set(...(override?.rotation ?? orig.rotation));
|
|
90
|
+
node.scale.set(...(override?.scale ?? orig.scale));
|
|
91
|
+
s.transformTouched = hasTransform;
|
|
92
|
+
}
|
|
93
|
+
if (hasVisible || s.visibleTouched) {
|
|
94
|
+
node.visible = override?.visible ?? orig.visible;
|
|
95
|
+
s.visibleTouched = hasVisible;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
// ── Material ────────────────────────────────────────────────────────────────
|
|
99
|
+
const assign = (mesh, material) => {
|
|
100
|
+
mesh.material = material;
|
|
101
|
+
};
|
|
102
|
+
const reconcileMaterial = (mesh, desired) => {
|
|
103
|
+
const current = mesh.material;
|
|
104
|
+
if (!current)
|
|
105
|
+
return;
|
|
106
|
+
const s = stateOf(mesh);
|
|
107
|
+
// A material that isn't ours is a fresh base (GLB, or the model-level pass replaced
|
|
108
|
+
// what we had). Ours still on the mesh means the stored base is still valid.
|
|
109
|
+
if (!isOwn(current)) {
|
|
110
|
+
if (s.ownMaterial || s.ownInstance) {
|
|
111
|
+
// Our material was swapped out from under us — release it, it's orphaned.
|
|
112
|
+
disposeOwn(s);
|
|
113
|
+
}
|
|
114
|
+
s.baseMaterial = current;
|
|
115
|
+
}
|
|
116
|
+
const base = s.baseMaterial ?? current;
|
|
117
|
+
if (!desired) {
|
|
118
|
+
if (isOwn(current)) {
|
|
119
|
+
assign(mesh, base);
|
|
120
|
+
disposeOwn(s);
|
|
121
|
+
}
|
|
122
|
+
s.token = (s.token ?? 0) + 1; // cancel any in-flight replace
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const key = JSON.stringify(desired.mode === 'replace' ? { r: desired.settings } : { t: desired.tweak });
|
|
126
|
+
if (isOwn(current) && s.materialKey === key)
|
|
127
|
+
return; // already up to date
|
|
128
|
+
const token = (s.token ?? 0) + 1;
|
|
129
|
+
s.token = token;
|
|
130
|
+
if (desired.mode === 'tweak') {
|
|
131
|
+
const make = (m) => {
|
|
132
|
+
const copy = tag(m.clone());
|
|
133
|
+
applyMaterialTweak(copy, desired.tweak);
|
|
134
|
+
return copy;
|
|
135
|
+
};
|
|
136
|
+
const next = Array.isArray(base) ? base.map(make) : make(base);
|
|
137
|
+
const prev = { ownMaterial: s.ownMaterial, ownInstance: s.ownInstance };
|
|
138
|
+
assign(mesh, next);
|
|
139
|
+
disposeOwn({ ...prev });
|
|
140
|
+
s.ownMaterial = next;
|
|
141
|
+
s.ownInstance = undefined;
|
|
142
|
+
s.materialKey = key;
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// replace — full 3drise material
|
|
146
|
+
const settings = desired.settings;
|
|
147
|
+
if (!settings) {
|
|
148
|
+
if (isOwn(current)) {
|
|
149
|
+
assign(mesh, base);
|
|
150
|
+
disposeOwn(s);
|
|
151
|
+
}
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const typeDef = MaterialRegistryAPI.getTypeDefinition(settings.materialType);
|
|
155
|
+
if (!typeDef)
|
|
156
|
+
return;
|
|
157
|
+
const variant = settings.materialVariant || Object.keys(typeDef.variants)[0];
|
|
158
|
+
resolveTextures(variant, settings).then(resolved => {
|
|
159
|
+
if (stateOf(mesh).token !== token)
|
|
160
|
+
return; // superseded
|
|
161
|
+
let instance;
|
|
162
|
+
try {
|
|
163
|
+
instance = createAndApplyMaterial(variant, resolved);
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
tag(instance.material);
|
|
169
|
+
const prev = { ownMaterial: s.ownMaterial, ownInstance: s.ownInstance };
|
|
170
|
+
assign(mesh, instance.material);
|
|
171
|
+
disposeOwn({ ...prev });
|
|
172
|
+
s.ownInstance = instance;
|
|
173
|
+
s.ownMaterial = instance.material;
|
|
174
|
+
s.materialKey = key;
|
|
175
|
+
});
|
|
176
|
+
};
|
|
177
|
+
/** A named node's material targets: itself if it has one, else every descendant mesh. */
|
|
178
|
+
const materialTargetsOf = (node) => {
|
|
179
|
+
if (node.material)
|
|
180
|
+
return [node];
|
|
181
|
+
const out = [];
|
|
182
|
+
node.traverse(child => {
|
|
183
|
+
if (child !== node && child.material)
|
|
184
|
+
out.push(child);
|
|
185
|
+
});
|
|
186
|
+
return out;
|
|
187
|
+
};
|
|
188
|
+
// ── Reconcile / restore ─────────────────────────────────────────────────────
|
|
189
|
+
export function reconcileNodeOverrides(root, overrides, master) {
|
|
190
|
+
let touched = touchedByRoot.get(root);
|
|
191
|
+
if (!touched) {
|
|
192
|
+
touched = new Set();
|
|
193
|
+
touchedByRoot.set(root, touched);
|
|
194
|
+
}
|
|
195
|
+
const targets = collectOverrideTargets(root, overrides);
|
|
196
|
+
const desiredNode = new Map();
|
|
197
|
+
const desiredMaterial = new Map();
|
|
198
|
+
// Parents come first in traversal order, so a child's own material override wins
|
|
199
|
+
// over one set on its group.
|
|
200
|
+
targets.forEach((nodes, key) => {
|
|
201
|
+
const override = overrides[key];
|
|
202
|
+
// Resolve the master counterpart once per node, by the same key.
|
|
203
|
+
const masterNodes = master ? resolveNodeKey(master, key) : [];
|
|
204
|
+
nodes.forEach((node, i) => {
|
|
205
|
+
const s = stateOf(node);
|
|
206
|
+
if (master && s.masterNode === undefined)
|
|
207
|
+
s.masterNode = masterNodes[i] ?? masterNodes[0] ?? null;
|
|
208
|
+
desiredNode.set(node, override);
|
|
209
|
+
if (override.material) {
|
|
210
|
+
materialTargetsOf(node).forEach(mesh => desiredMaterial.set(mesh, override.material));
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
const all = new Set([...touched, ...desiredNode.keys(), ...desiredMaterial.keys()]);
|
|
215
|
+
all.forEach(node => {
|
|
216
|
+
reconcileTransform(node, desiredNode.get(node));
|
|
217
|
+
if (node.material)
|
|
218
|
+
reconcileMaterial(node, desiredMaterial.get(node));
|
|
219
|
+
});
|
|
220
|
+
// Keep tracking only what still differs from the original.
|
|
221
|
+
const stillTouched = new Set();
|
|
222
|
+
all.forEach(node => {
|
|
223
|
+
const s = nodeStates.get(node);
|
|
224
|
+
if (s && (s.transformTouched || s.visibleTouched || s.ownMaterial || s.ownInstance || desiredMaterial.has(node))) {
|
|
225
|
+
stillTouched.add(node);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
touchedByRoot.set(root, stillTouched);
|
|
229
|
+
}
|
|
230
|
+
/** Put every node this root touched back to its original state and free our materials. */
|
|
231
|
+
export function restoreNodeOverrides(root, master) {
|
|
232
|
+
reconcileNodeOverrides(root, {}, master);
|
|
233
|
+
}
|
|
234
|
+
// ── Hook ────────────────────────────────────────────────────────────────────
|
|
235
|
+
/**
|
|
236
|
+
* @param object the mounted model clone (one layout slot)
|
|
237
|
+
* @param overrides createdObject.config.nodeOverrides (getNodeOverrides)
|
|
238
|
+
* @param master the cache master scene (peekModelAsset(url)?.scene) — source of originals
|
|
239
|
+
*/
|
|
240
|
+
export function useNodeOverrides(object, overrides, master) {
|
|
241
|
+
const materialPass = useContext(ModelMaterialPassContext);
|
|
242
|
+
useEffect(() => {
|
|
243
|
+
if (!object)
|
|
244
|
+
return;
|
|
245
|
+
reconcileNodeOverrides(object, overrides, master);
|
|
246
|
+
}, [object, overrides, master, materialPass]);
|
|
247
|
+
// Unmount / instance swap: leave the instance as the GLB made it.
|
|
248
|
+
useEffect(() => {
|
|
249
|
+
if (!object)
|
|
250
|
+
return;
|
|
251
|
+
return () => restoreNodeOverrides(object, master);
|
|
252
|
+
}, [object, master]);
|
|
253
|
+
}
|
|
254
|
+
export default useNodeOverrides;
|
package/dist/index.d.ts
CHANGED
|
@@ -30,6 +30,8 @@ export { applyOceanUniforms, buildWaveTable, seaState } from './utils/oceanUnifo
|
|
|
30
30
|
export type { WaveTable } from './utils/oceanUniforms';
|
|
31
31
|
export { applyCameraUniforms, applyCirrusUniforms, applyDeckUniforms, applySunUniforms, applyThunderUniforms, } from './utils/cloudUniforms';
|
|
32
32
|
export { applyCloudsKeyframeAnimations, applyCloudsMouseMove, resolveDeckConfig, } from './utils/handleCloudsAnimations';
|
|
33
|
+
export { applyConfigKeyframeAnimations, applyConfigMouseMove, hasConfigOverrides, resolveConfig, } from './utils/configAnimations';
|
|
34
|
+
export type { ConfigOverride, ConfigStartValues } from './utils/configAnimations';
|
|
33
35
|
export { volumetricCloudPresets, cirrusPresets, getCloudPresetsForType, getCloudPresetByName, getCirrusPresetByName, searchCloudPresetsByTags, } from './presets/volumetricCloudPresets';
|
|
34
36
|
export type { VolumetricCloudPreset, CirrusPreset } from './presets/volumetricCloudPresets';
|
|
35
37
|
export { terrainPresets, getTerrainPresetsForType, getTerrainPresetByName, searchTerrainPresetsByTags, } from './presets/terrainPresets';
|