@3driseai/3drise-viewer 0.1.35 → 0.1.37

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":"ClipsRuntime.d.ts","sourceRoot":"","sources":["../../src/animationsV2/ClipsRuntime.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ClipsRuntime.d.ts","sourceRoot":"","sources":["../../src/animationsV2/ClipsRuntime.ts"],"names":[],"mappings":"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"}
@@ -136,7 +136,9 @@ class ClipsRuntime {
136
136
  return false;
137
137
  const key = targetKey(state, node, path);
138
138
  this.live.set(key, value);
139
- this.written.delete(key); // a clip holding the same value must write it again
139
+ // The live write wins over a clip that merely HOLDS this property: the clip writes again only
140
+ // when its winning value moves (a UI slider beside a scrubbed story must stick while the story
141
+ // stands still). Previously the mark was cleared here, so the held value came straight back.
140
142
  return true;
141
143
  }
142
144
  /** What was last written by `setLiveValue`, else the authored value (action patches included). */
@@ -1 +1 @@
1
- {"version":3,"file":"HarmonicShellEffect.d.ts","sourceRoot":"","sources":["../../../src/components/effects/HarmonicShellEffect.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGpG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,MAAM,WAAW,wBAAyB,SAAQ,gBAAgB;IAC9D,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,mFAAmF;IACnF,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,8EAA8E;IAC9E,SAAS,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC/B,yEAAyE;IACzE,SAAS,CAAC,EAAE,eAAe,CAAC;CAC/B;AAoMD,QAAA,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CAAC,wBAAwB,CA6C3D,CAAC;AACF,eAAe,mBAAmB,CAAC"}
1
+ {"version":3,"file":"HarmonicShellEffect.d.ts","sourceRoot":"","sources":["../../../src/components/effects/HarmonicShellEffect.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGpG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAQhE,MAAM,WAAW,wBAAyB,SAAQ,gBAAgB;IAC9D,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,mFAAmF;IACnF,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,8EAA8E;IAC9E,SAAS,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC/B,yEAAyE;IACzE,SAAS,CAAC,EAAE,eAAe,CAAC;CAC/B;AAwCD,QAAA,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CAAC,wBAAwB,CA8I3D,CAAC;AACF,eAAe,mBAAmB,CAAC"}
@@ -6,166 +6,23 @@ import { withConfigDefaults } from '../../utils/configAnimations.js';
6
6
  import { DEFAULT_GENERATIVE_EFFECTS, EFFECTS_ANIMATABLE } from "@3driseai/3drise-core";
7
7
  import { useObjectReady } from '../../hooks/useObjectReady.js';
8
8
  import { useConfigDriver } from '../../hooks/useConfigDriver.js';
9
+ import { findById } from '../../utils/sceneQuery.js';
10
+ import { shellAttach, shellAttached, shellRelease } from '../../operations/catalogue/inShell.js';
11
+ import { isOperationOn } from '../../operations/runtime.js';
12
+ import { SHELL_FRAG, SHELL_VERT, bakeObjectSdf, bakeShapeSdf, createHarmonicState, createShellUniforms, registerShellField, unregisterShellField, updateHarmonicWeights, } from './harmonicShellField.js';
9
13
  /**
10
14
  * An iridescent shell around the host, displaced by real spherical harmonics
11
15
  * up to l = 4 whose weights drift slowly. The colour is thin-film
12
16
  * interference, as on a soap bubble, and the lines where the harmonic field
13
17
  * crosses zero glow.
18
+ *
19
+ * With a target (text, a shape, or another scene object) the shell becomes it:
20
+ * `morph` 0 → 1 traces every vertex of the sphere onto the target's distance
21
+ * field (see harmonicShellField.ts), the harmonics keep breathing on it, and
22
+ * the triangles stretched across the gaps between letters fade out.
14
23
  */
15
- /** Band (l) of each of the 24 real-SH terms in the shader. */
16
- const SH_BANDS = [1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4];
17
- function mulberry32(seed) {
18
- return () => {
19
- seed |= 0;
20
- seed = (seed + 0x6d2b79f5) | 0;
21
- let r = Math.imul(seed ^ (seed >>> 15), 1 | seed);
22
- r = (r + Math.imul(r ^ (r >>> 7), 61 | r)) ^ r;
23
- return ((r ^ (r >>> 14)) >>> 0) / 4294967296;
24
- };
25
- }
26
- /** Fixed per-term frequencies and phases, so the same config always moves the same way. */
27
- function createHarmonicState() {
28
- const rand = mulberry32(7);
29
- const f1 = new Float32Array(24), f2 = new Float32Array(24), p1 = new Float32Array(24), p2 = new Float32Array(24);
30
- for (let i = 0; i < 24; i++) {
31
- f1[i] = 0.13 + 0.3 * rand();
32
- f2[i] = 0.07 + 0.2 * rand();
33
- p1[i] = rand() * Math.PI * 2;
34
- p2[i] = rand() * Math.PI * 2;
35
- }
36
- // per-band gate, eased so changing `degree` blends bands in and out
37
- return { f1, f2, p1, p2, gate: new Float32Array([0, 0, 0, 0, 0]) };
38
- }
39
- /**
40
- * Writes the 24 weights for time T into `out`. Bands above `degree` fade
41
- * out; the top band leads and lower bands sit at about half strength.
42
- */
43
- function updateHarmonicWeights(state, out, T, degree, dt) {
44
- const deg = Math.round(Math.min(Math.max(degree, 1), 4));
45
- const ease = Math.min(1, dt * 2.5);
46
- let energy = 0;
47
- for (let l = 1; l <= 4; l++) {
48
- // l = 1 only shifts the shell sideways, so it stays quiet unless it is the only band
49
- const target = l > deg ? 0 : l === deg ? 1 : l === 1 ? 0.2 : 0.55;
50
- state.gate[l] += (target - state.gate[l]) * ease;
51
- energy += state.gate[l] * state.gate[l] * (2 * l + 1);
52
- }
53
- const norm = 1 / Math.sqrt(Math.max(energy / 5, 1e-3));
54
- for (let i = 0; i < 24; i++) {
55
- const g = state.gate[SH_BANDS[i]];
56
- out[i] = g * norm * (0.7 * Math.sin(T * state.f1[i] + state.p1[i]) + 0.3 * Math.sin(T * state.f2[i] + state.p2[i]));
57
- }
58
- }
59
- const vert = /* glsl */ `
60
- uniform float uW[24];
61
- uniform float uAmplitude;
62
- uniform float uBound;
63
- uniform float uSizeMultiplier;
64
- varying vec3 vNormal;
65
- varying vec3 vViewDir;
66
- varying float vField;
67
-
68
- // Real spherical harmonics for l = 1..4, as polynomials in the unit vector,
69
- // each scaled so its peak is about 1. The CPU animates the 24 weights.
70
- float sh(vec3 n){
71
- float x = n.x, y = n.y, z = n.z;
72
- float x2 = x * x, y2 = y * y, z2 = z * z;
73
- float s = 0.0;
74
- s += uW[0] * y + uW[1] * z + uW[2] * x;
75
- s += uW[3] * 2.0 * x * y + uW[4] * 2.0 * y * z + uW[5] * (1.5 * z2 - 0.5)
76
- + uW[6] * 2.0 * x * z + uW[7] * (x2 - y2);
77
- s += uW[8] * y * (3.0 * x2 - y2)
78
- + uW[9] * 5.2 * x * y * z
79
- + uW[10] * 0.8 * y * (5.0 * z2 - 1.0)
80
- + uW[11] * 0.5 * z * (5.0 * z2 - 3.0)
81
- + uW[12] * 0.8 * x * (5.0 * z2 - 1.0)
82
- + uW[13] * 2.6 * z * (x2 - y2)
83
- + uW[14] * x * (x2 - 3.0 * y2);
84
- s += uW[15] * 4.0 * x * y * (x2 - y2)
85
- + uW[16] * 3.0 * y * z * (3.0 * x2 - y2)
86
- + uW[17] * 1.5 * x * y * (7.0 * z2 - 1.0)
87
- + uW[18] * 0.95 * y * z * (7.0 * z2 - 3.0)
88
- + uW[19] * 0.125 * (35.0 * z2 * z2 - 30.0 * z2 + 3.0)
89
- + uW[20] * 0.95 * x * z * (7.0 * z2 - 3.0)
90
- + uW[21] * 0.78 * (x2 - y2) * (7.0 * z2 - 1.0)
91
- + uW[22] * 3.0 * x * z * (x2 - 3.0 * y2)
92
- + uW[23] * (x2 * (x2 - 3.0 * y2) - y2 * (3.0 * x2 - y2));
93
- return s;
94
- }
95
-
96
- vec3 shellPoint(vec3 n, out float f){
97
- f = sh(n);
98
- return n * uBound * uSizeMultiplier * (1.0 + uAmplitude * f);
99
- }
100
-
101
- void main(){
102
- vec3 n = normalize(position);
103
- float f, f1, f2;
104
- vec3 p0 = shellPoint(n, f);
105
-
106
- // normal from two nearby points on the displaced surface
107
- vec3 up = abs(n.y) > 0.99 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);
108
- vec3 t1 = normalize(cross(n, up));
109
- vec3 t2 = cross(n, t1);
110
- vec3 p1 = shellPoint(normalize(n + t1 * 0.01), f1);
111
- vec3 p2 = shellPoint(normalize(n + t2 * 0.01), f2);
112
- vec3 nrm = normalize(cross(p1 - p0, p2 - p0));
113
- if (dot(nrm, n) < 0.0) nrm = -nrm;
114
-
115
- vField = f;
116
- vec4 mv = modelViewMatrix * vec4(p0, 1.0);
117
- vNormal = normalize(normalMatrix * nrm);
118
- vViewDir = normalize(-mv.xyz);
119
- gl_Position = projectionMatrix * mv;
120
- }
121
- `;
122
- const frag = /* glsl */ `
123
- uniform vec3 uColor;
124
- uniform vec3 uRimColor;
125
- uniform float uOpacity;
126
- uniform float uIntensity;
127
- uniform float uFilmThickness;
128
- uniform float uNodalLines;
129
- uniform float uTint;
130
- varying vec3 vNormal;
131
- varying vec3 vViewDir;
132
- varying float vField;
133
-
134
- const float TAU = 6.28318530718;
135
-
136
- void main(){
137
- vec3 N = normalize(vNormal);
138
- vec3 V = normalize(vViewDir);
139
- float ndv = abs(dot(N, V));
140
-
141
- // Thin-film interference, as on a soap bubble: a film of index 1.33 and
142
- // thickness d delays the inner reflection by 2*n*d*cos(theta_t). Each
143
- // wavelength interferes on its own, which splits white light into bands.
144
- // The film is thicker where the harmonic pushes the shell out, so the
145
- // colour bands follow the shape.
146
- float cosT = sqrt(max(1.0 - (1.0 - ndv * ndv) / (1.33 * 1.33), 0.0));
147
- float opd = 2.0 * 1.33 * uFilmThickness * (1.0 + 0.35 * vField) * cosT;
148
- vec3 film = 0.5 + 0.5 * cos(TAU * opd / vec3(650.0, 532.0, 450.0));
149
- // tint: keep the film's light and dark bands, carry them in the base colour
150
- float lum = dot(film, vec3(0.299, 0.587, 0.114));
151
- film = mix(film, lum * uColor * 1.7 + film * 0.12, uTint);
152
-
153
- float fres = pow(1.0 - ndv, 3.0);
154
- vec3 col = uColor * 0.25 + film * (0.2 + 0.8 * fres);
155
- col = mix(col, uRimColor, fres * 0.45);
156
-
157
- // nodal lines: where the harmonic field crosses zero
158
- float nd = abs(vField) / max(fwidth(vField), 1e-4);
159
- float nodal = (1.0 - smoothstep(0.5, 1.5, nd)) * uNodalLines;
160
- col += uRimColor * nodal * 1.5;
161
-
162
- float a = (0.06 + 0.9 * fres + nodal * 0.8) * uOpacity;
163
- if (a < 0.003) discard;
164
- gl_FragColor = vec4(col * uIntensity, a);
165
- }
166
- `;
167
- /** Nothing rebuilds the shell: the vertex shader displaces a unit sphere. */
168
- const STRUCTURAL = [];
24
+ /** Knobs that rebuild the target field (never driven per frame). The sphere itself never rebuilds. */
25
+ const STRUCTURAL = ['target', 'text', 'font', 'shape', 'targetObject'];
169
26
  /** Knobs a keyframe or pointer binding may drive per frame — exactly what applyConfig pushes. */
170
27
  const ANIMATABLE = new Set(EFFECTS_ANIMATABLE.harmonicShell.map((p) => p.value));
171
28
  /** The single push point: every config knob reaches its uniform here. */
@@ -173,15 +30,19 @@ function applyConfig(u, c, bound) {
173
30
  u.uSpeed.value = c.speed;
174
31
  u.uIntensity.value = c.intensity;
175
32
  u.uOpacity.value = c.opacity;
176
- u.uColor.value.set(c.color);
177
- u.uRimColor.value.set(c.rimColor);
178
- u.uAmplitude.value = c.amplitude;
179
- u.uBound.value = bound;
180
- u.uSizeMultiplier.value = c.sizeMultiplier;
181
- u.uFilmThickness.value = c.filmThickness;
33
+ u.uHshColor.value.set(c.color);
34
+ u.uHshRim.value.set(c.rimColor);
35
+ u.uHshAmplitude.value = c.amplitude;
36
+ u.uHshBound.value = bound;
37
+ u.uHshSize.value = c.sizeMultiplier;
38
+ u.uHshFilm.value = c.filmThickness;
182
39
  u.uNodalLines.value = c.nodalLines;
183
40
  u.uDegree.value = c.degree;
184
- u.uTint.value = c.tint ?? 0;
41
+ u.uHshTint.value = c.tint ?? 0;
42
+ u.uHshMorph.value = c.morph ?? 0;
43
+ u.uHshDepth.value = (c.depth ?? 0.25) * bound;
44
+ u.uBreath.value = c.breath ?? 0.12;
45
+ u.uWeb.value = (c.webCut ?? 0.02) * bound;
185
46
  }
186
47
  const HarmonicShellEffect = ({ boundingRadius, config, id, keyframes, mouseMove, onObjectReady, onObjectDispose }) => {
187
48
  const cfg = useMemo(() => withConfigDefaults(DEFAULT_GENERATIVE_EFFECTS.harmonicShell, config), [config]);
@@ -189,34 +50,165 @@ const HarmonicShellEffect = ({ boundingRadius, config, id, keyframes, mouseMove,
189
50
  useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
190
51
  const state = useMemo(createHarmonicState, []);
191
52
  const uniforms = useMemo(() => ({
192
- uTime: { value: 0 },
53
+ ...createShellUniforms(),
193
54
  uSpeed: { value: 1 },
194
55
  uIntensity: { value: 1 },
195
56
  uOpacity: { value: 1 },
196
- uColor: { value: new THREE.Color() },
197
- uRimColor: { value: new THREE.Color() },
198
- uAmplitude: { value: 0.22 },
199
- uBound: { value: 1 },
200
- uSizeMultiplier: { value: 1.35 },
201
- uFilmThickness: { value: 380 },
202
57
  uNodalLines: { value: 0.6 },
203
58
  uDegree: { value: 3 },
204
- uTint: { value: 0 },
205
- uW: { value: new Float32Array(24) },
59
+ uBreath: { value: 0.12 },
60
+ uWeb: { value: 0.02 },
206
61
  }), []);
207
- const material = useMemo(() => new THREE.ShaderMaterial({ vertexShader: vert, fragmentShader: frag, uniforms, transparent: true, depthWrite: false, blending: THREE.AdditiveBlending }), [uniforms]);
62
+ const material = useMemo(() => new THREE.ShaderMaterial({ vertexShader: SHELL_VERT, fragmentShader: SHELL_FRAG, uniforms, transparent: true, depthWrite: false, blending: THREE.AdditiveBlending, side: THREE.DoubleSide }), [uniforms]);
208
63
  useEffect(() => () => material.dispose(), [material]);
209
64
  // the vertex shader displaces a unit sphere, so the mesh never needs rebuilding
210
65
  const geometry = useMemo(() => new THREE.SphereGeometry(1, 192, 128), []);
211
66
  useEffect(() => () => geometry.dispose(), [geometry]);
67
+ // the field other objects read (the inShell effect): registered while mounted
68
+ const field = useMemo(() => ({ root: null, uniforms: uniforms, inv: new THREE.Matrix4(), target: null }), [uniforms]);
69
+ useEffect(() => {
70
+ if (!rootRef.current)
71
+ return;
72
+ field.root = rootRef.current;
73
+ registerShellField(field);
74
+ return () => unregisterShellField(field);
75
+ }, [field]);
212
76
  const push = useCallback((merged) => {
213
77
  applyConfig(uniforms, merged, boundingRadius);
214
78
  }, [uniforms, boundingRadius]);
215
79
  useLayoutEffect(() => { push(cfg); }, [push, cfg]);
216
80
  useConfigDriver({ rootRef, id, family: 'effects', base: cfg, structural: STRUCTURAL, allowed: ANIMATABLE, keyframes, mouseMove, push });
81
+ // ── the target field: rebuilt when a structural knob changes ──
82
+ const targetKind = cfg.target ?? 'none';
83
+ const targetObjectId = cfg.targetObject ?? '';
84
+ const targetRef = useRef(null);
85
+ useEffect(() => {
86
+ let cancelled = false;
87
+ const u = uniforms;
88
+ targetRef.current = null;
89
+ field.target = null;
90
+ if (targetKind === 'text' || targetKind === 'shape') {
91
+ const kind = targetKind === 'text' ? 'text' : (cfg.shape ?? 'star');
92
+ // the field spans a little more than the resting shell, so letters can reach past the sphere
93
+ bakeShapeSdf(kind, String(cfg.text ?? ''), String(cfg.font ?? 'sans'), boundingRadius * cfg.sizeMultiplier * 1.15).then((sdf) => {
94
+ if (cancelled || !sdf)
95
+ return;
96
+ const old = u.uHshSdf.value;
97
+ u.uHshSdf.value = sdf.tex;
98
+ u.uHshSdfHalf.value.set(sdf.half, sdf.half);
99
+ u.uHshMode.value = 1;
100
+ if (old && old.image?.width > 1)
101
+ old.dispose();
102
+ });
103
+ }
104
+ else if (targetKind === 'object' && targetObjectId) {
105
+ // the object may land after us (a model loading): look again until it is there
106
+ let tries = 0;
107
+ const find = () => {
108
+ if (cancelled)
109
+ return;
110
+ const obj = findById(targetObjectId);
111
+ if (!obj) {
112
+ if (tries++ < 60)
113
+ setTimeout(find, 500);
114
+ else
115
+ console.warn(`[harmonicShell] target object "${targetObjectId}" not found in the scene`);
116
+ return;
117
+ }
118
+ bakeObjectSdf(obj).then((sdf) => {
119
+ if (cancelled || !sdf)
120
+ return;
121
+ targetRef.current = obj;
122
+ field.target = obj;
123
+ u.uHshSdf3.value = sdf.tex;
124
+ u.uHshSdf3Min.value.copy(sdf.min);
125
+ u.uHshSdf3Size.value.copy(sdf.size);
126
+ u.uHshMode.value = 2;
127
+ }).catch((err) => console.warn('[harmonicShell] object field bake failed', err));
128
+ };
129
+ find();
130
+ }
131
+ else {
132
+ u.uHshMode.value = 0;
133
+ }
134
+ return () => { cancelled = true; };
135
+ // eslint-disable-next-line react-hooks/exhaustive-deps
136
+ }, [targetKind, targetObjectId, cfg.text, cfg.font, cfg.shape, boundingRadius, uniforms, field]);
137
+ // ── objects crossing the shell show it: the inShell layer, attached and released at runtime ──
138
+ const inside = useMemo(() => ({ frame: 0, on: new Set(), box: new THREE.Box3(), sphere: new THREE.Sphere(), centre: new THREE.Vector3(), scale: new THREE.Vector3() }), []);
139
+ // the objects the shell affects (ids on the shell's config; nothing on them). Looked up each scan: they may land later or be replaced.
140
+ const insideIds = useMemo(() => (Array.isArray(cfg.insideObjects) ? cfg.insideObjects.filter((v) => typeof v === 'string' && v) : []), [cfg.insideObjects]);
141
+ const insideIdsRef = useRef(insideIds);
142
+ insideIdsRef.current = insideIds;
143
+ const look = useMemo(() => ({ look: cfg.insideLook === 'ghost' ? 'ghost' : 'film', bands: cfg.insideBands ?? 1, seam: cfg.insideSeam ?? 1, warp: cfg.insideWarp ?? 0.6, core: cfg.insideCore ?? 0.5 }), [cfg.insideLook, cfg.insideBands, cfg.insideSeam, cfg.insideWarp, cfg.insideCore]);
144
+ const lookRef = useRef(look);
145
+ lookRef.current = look;
146
+ // the look changed: push it to everything the shell is on
147
+ useEffect(() => { inside.on.forEach((o) => shellAttach(o, look)); }, [look, inside]);
148
+ useEffect(() => () => { inside.on.forEach((o) => shellRelease(o)); inside.on.clear(); }, [inside]);
149
+ // Objects not on the list are never touched. No separate pre-warm: the runtime's warm pass applies and quietly reverts the operation, and
150
+ // that revert would strip a layer the shell has just attached. The attach compiles its program
151
+ // in the background before anything shows (runProgress), and fires when the bounds touch — before
152
+ // any pixel is inside — so the compile is hidden on the first crossing.
153
+ const scanInside = () => {
154
+ const root = rootRef.current;
155
+ if (!root)
156
+ return;
157
+ const ids = insideIdsRef.current;
158
+ if (!ids.length) {
159
+ if (inside.on.size) {
160
+ inside.on.forEach((o) => shellRelease(o));
161
+ inside.on.clear();
162
+ }
163
+ return;
164
+ }
165
+ inside.centre.setFromMatrixPosition(root.matrixWorld);
166
+ root.matrixWorld.decompose(new THREE.Vector3(), new THREE.Quaternion(), inside.scale);
167
+ const R = uniforms.uHshBound.value * uniforms.uHshSize.value * (1 + uniforms.uHshAmplitude.value) * Math.max(inside.scale.x, inside.scale.y, inside.scale.z);
168
+ const seen = new Set();
169
+ for (const id of ids) {
170
+ const o = findById(id);
171
+ // objects with real meshes (mesh / model); the shell's own wrap target is the shell, not something inside it
172
+ if (!o || o.userData?.visualFx !== true || o.userData.objectType === 'group' || o === targetRef.current || !o.visible)
173
+ continue;
174
+ inside.box.setFromObject(o);
175
+ if (inside.box.isEmpty())
176
+ continue;
177
+ inside.box.getBoundingSphere(inside.sphere);
178
+ const d = inside.sphere.center.distanceTo(inside.centre);
179
+ const was = inside.on.has(o);
180
+ // a little hysteresis, so an object resting on the boundary doesn't flicker on and off
181
+ if (d < R + inside.sphere.radius * (was ? 1.15 : 1.0))
182
+ seen.add(o);
183
+ }
184
+ for (const o of seen)
185
+ if (!inside.on.has(o) && shellAttach(o, lookRef.current, isOperationOn('inShell', o)))
186
+ inside.on.add(o);
187
+ for (const o of inside.on)
188
+ if (!seen.has(o)) {
189
+ inside.on.delete(o);
190
+ shellRelease(o);
191
+ }
192
+ for (const o of inside.on)
193
+ if (!shellAttached(o))
194
+ inside.on.delete(o); // taken away underneath (an own effect reverted) → re-attached next scan
195
+ };
196
+ const _m = useMemo(() => new THREE.Matrix4(), []);
217
197
  useFrame((_, delta) => {
218
- uniforms.uTime.value += delta * uniforms.uSpeed.value;
219
- updateHarmonicWeights(state, uniforms.uW.value, uniforms.uTime.value, uniforms.uDegree.value, delta);
198
+ if (++inside.frame % 6 === 0)
199
+ scanInside();
200
+ uniforms.uHshTime.value += delta * uniforms.uSpeed.value;
201
+ updateHarmonicWeights(state, uniforms.uHshW.value, uniforms.uHshTime.value, uniforms.uDegree.value, delta);
202
+ const root = rootRef.current;
203
+ if (root) {
204
+ field.inv.copy(root.matrixWorld).invert();
205
+ const t = targetRef.current;
206
+ // shell-local → the target object's local space (where its field was baked), live: both may move
207
+ if (t && uniforms.uHshMode.value === 2) {
208
+ uniforms.uHshSdf3Inv.value.copy(_m.copy(t.matrixWorld).invert()).multiply(root.matrixWorld);
209
+ uniforms.uHshSdf3Mat.value.copy(uniforms.uHshSdf3Inv.value).invert();
210
+ }
211
+ }
220
212
  });
221
213
  return (_jsx("group", { ref: rootRef, children: _jsx("mesh", { geometry: geometry, material: material }) }));
222
214
  };
@@ -0,0 +1,77 @@
1
+ import * as THREE from 'three';
2
+ import type { Object3D } from 'three';
3
+ export interface HarmonicState {
4
+ f1: Float32Array;
5
+ f2: Float32Array;
6
+ p1: Float32Array;
7
+ p2: Float32Array;
8
+ gate: Float32Array;
9
+ }
10
+ /** Fixed per-term frequencies and phases, so the same config always moves the same way. */
11
+ export declare function createHarmonicState(): HarmonicState;
12
+ /**
13
+ * Writes the 24 weights for time T into `out`. Bands above `degree` fade
14
+ * out; the top band leads and lower bands sit at about half strength.
15
+ */
16
+ export declare function updateHarmonicWeights(state: HarmonicState, out: Float32Array, T: number, degree: number, dt: number): void;
17
+ /** The shared uniforms, declared (the shell's own ShaderMaterial; a layered material gets them from the kit). */
18
+ export declare const HSH_FIELD_DECL = "\n uniform float uHshW[24];\n uniform float uHshAmplitude; uniform float uHshBound; uniform float uHshSize;\n uniform vec3 uHshColor; uniform vec3 uHshRim; uniform float uHshFilm; uniform float uHshTint; uniform float uHshTime;\n uniform float uHshMorph; uniform float uHshMode;\n uniform sampler2D uHshSdf; uniform vec2 uHshSdfHalf; uniform float uHshDepth;\n uniform sampler3D uHshSdf3; uniform vec3 uHshSdf3Min; uniform vec3 uHshSdf3Size; uniform mat4 uHshSdf3Inv; uniform mat4 uHshSdf3Mat;\n";
19
+ /**
20
+ * The field, for any shader (after the declarations): the harmonic polynomial, the
21
+ * thin film, and the target distance functions. Include once per stage.
22
+ */
23
+ export declare const HSH_FIELD_FUNCS = "\n\n // Real spherical harmonics for l = 1..4, as polynomials in the unit vector,\n // each scaled so its peak is about 1. The CPU animates the 24 weights.\n float hshSh(vec3 n){\n float x = n.x, y = n.y, z = n.z;\n float x2 = x * x, y2 = y * y, z2 = z * z;\n float s = 0.0;\n s += uHshW[0] * y + uHshW[1] * z + uHshW[2] * x;\n s += uHshW[3] * 2.0 * x * y + uHshW[4] * 2.0 * y * z + uHshW[5] * (1.5 * z2 - 0.5)\n + uHshW[6] * 2.0 * x * z + uHshW[7] * (x2 - y2);\n s += uHshW[8] * y * (3.0 * x2 - y2)\n + uHshW[9] * 5.2 * x * y * z\n + uHshW[10] * 0.8 * y * (5.0 * z2 - 1.0)\n + uHshW[11] * 0.5 * z * (5.0 * z2 - 3.0)\n + uHshW[12] * 0.8 * x * (5.0 * z2 - 1.0)\n + uHshW[13] * 2.6 * z * (x2 - y2)\n + uHshW[14] * x * (x2 - 3.0 * y2);\n s += uHshW[15] * 4.0 * x * y * (x2 - y2)\n + uHshW[16] * 3.0 * y * z * (3.0 * x2 - y2)\n + uHshW[17] * 1.5 * x * y * (7.0 * z2 - 1.0)\n + uHshW[18] * 0.95 * y * z * (7.0 * z2 - 3.0)\n + uHshW[19] * 0.125 * (35.0 * z2 * z2 - 30.0 * z2 + 3.0)\n + uHshW[20] * 0.95 * x * z * (7.0 * z2 - 3.0)\n + uHshW[21] * 0.78 * (x2 - y2) * (7.0 * z2 - 1.0)\n + uHshW[22] * 3.0 * x * z * (x2 - 3.0 * y2)\n + uHshW[23] * (x2 * (x2 - 3.0 * y2) - y2 * (3.0 * x2 - y2));\n return s;\n }\n\n // The resting shell: radius along direction n, in shell-local units.\n float hshRadius(vec3 n, out float field){\n field = hshSh(n);\n return uHshBound * uHshSize * (1.0 + uHshAmplitude * field);\n }\n\n // Thin-film interference, as on a soap bubble: a film of index 1.33 and\n // thickness d delays the inner reflection by 2*n*d*cos(theta_t). Each\n // wavelength interferes on its own, which splits white light into bands.\n vec3 hshFilm(float ndv, float field){\n float cosT = sqrt(max(1.0 - (1.0 - ndv * ndv) / (1.33 * 1.33), 0.0));\n float opd = 2.0 * 1.33 * uHshFilm * (1.0 + 0.35 * field) * cosT;\n vec3 film = 0.5 + 0.5 * cos(6.28318530718 * opd / vec3(650.0, 532.0, 450.0));\n float lum = dot(film, vec3(0.299, 0.587, 0.114));\n return mix(film, lum * uHshColor * 1.7 + film * 0.12, uHshTint);\n }\n\n // \u2500\u2500 the target: signed distance in shell-local units (negative inside) \u2500\u2500\n float hshSdf2(vec2 p){\n vec2 uv = p / (2.0 * uHshSdfHalf) + 0.5;\n vec2 c = clamp(uv, 0.0, 1.0);\n return texture2D(uHshSdf, c).r + length((uv - c) * 2.0 * uHshSdfHalf);\n }\n float hshSdf(vec3 p){\n if (uHshMode > 1.5) {\n // an object's own field, baked in its local space\n vec3 q = (uHshSdf3Inv * vec4(p, 1.0)).xyz;\n vec3 uvw = (q - uHshSdf3Min) / uHshSdf3Size; vec3 c = clamp(uvw, 0.0, 1.0);\n return texture(uHshSdf3, c).r + length((uvw - c) * uHshSdf3Size);\n }\n // text / shape: the 2D field extruded to \u00B1depth\n float d2 = hshSdf2(p.xy); float dz = abs(p.z) - uHshDepth;\n vec2 w = max(vec2(d2, dz), 0.0);\n return min(max(d2, dz), 0.0) + length(w);\n }\n vec3 hshSdfGrad(vec3 p){\n float e = 0.012;\n vec3 g = vec3(hshSdf(p + vec3(e, 0, 0)) - hshSdf(p - vec3(e, 0, 0)),\n hshSdf(p + vec3(0, e, 0)) - hshSdf(p - vec3(0, e, 0)),\n hshSdf(p + vec3(0, 0, e)) - hshSdf(p - vec3(0, 0, e)));\n float l = length(g); return l > 1e-6 ? g / l : vec3(0.0, 0.0, 1.0);\n }\n\n // Signed distance from a shell-local point to the shell's CURRENT surface\n // (the resting shell blended with the target by uHshMorph). 'field' is the\n // harmonic value in that direction, for the film.\n // Where a sphere vertex starts its trace: the resting shell point stretched to the\n // target's extent, so a wide word or a long object gets vertices along its whole length\n // instead of everything converging on the part nearest the centre.\n vec3 hshTraceStart(vec3 p0){\n float R0 = uHshBound * uHshSize;\n if (uHshMode > 1.5) {\n vec3 c = uHshSdf3Min + 0.5 * uHshSdf3Size; // the target's centre, in its own space\n vec3 h = uHshSdf3Size * 0.44; // half extents (the baked box is padded 12%)\n vec3 ct = (uHshSdf3Inv * vec4(0.0, 0.0, 0.0, 1.0)).xyz;\n vec3 qt = (uHshSdf3Inv * vec4(p0, 1.0)).xyz;\n qt = c + (qt - ct) * (h / R0);\n return (uHshSdf3Mat * vec4(qt, 1.0)).xyz;\n }\n return p0 * vec3(uHshSdfHalf / R0, max(uHshDepth, 0.05) / R0);\n }\n\n float hshDist(vec3 p, out float field){\n float r = length(p); vec3 n = r > 1e-5 ? p / r : vec3(0.0, 1.0, 0.0);\n float dS = r - hshRadius(n, field);\n return uHshMode > 0.5 && uHshMorph > 0.0 ? mix(dS, hshSdf(p), uHshMorph) : dS;\n }\n";
24
+ /** Declarations + functions: the whole field for a shader that declares nothing itself. */
25
+ export declare const HSH_FIELD_GLSL: string;
26
+ /** The shell's own material: the sphere displaced by the field, traced onto the target when it has one. */
27
+ export declare const SHELL_VERT = "\n uniform float uBreath;\n varying vec3 vNormal;\n varying vec3 vViewDir;\n varying float vField;\n varying vec3 vLocal;\n \n uniform float uHshW[24];\n uniform float uHshAmplitude; uniform float uHshBound; uniform float uHshSize;\n uniform vec3 uHshColor; uniform vec3 uHshRim; uniform float uHshFilm; uniform float uHshTint; uniform float uHshTime;\n uniform float uHshMorph; uniform float uHshMode;\n uniform sampler2D uHshSdf; uniform vec2 uHshSdfHalf; uniform float uHshDepth;\n uniform sampler3D uHshSdf3; uniform vec3 uHshSdf3Min; uniform vec3 uHshSdf3Size; uniform mat4 uHshSdf3Inv; uniform mat4 uHshSdf3Mat;\n\n\n // Real spherical harmonics for l = 1..4, as polynomials in the unit vector,\n // each scaled so its peak is about 1. The CPU animates the 24 weights.\n float hshSh(vec3 n){\n float x = n.x, y = n.y, z = n.z;\n float x2 = x * x, y2 = y * y, z2 = z * z;\n float s = 0.0;\n s += uHshW[0] * y + uHshW[1] * z + uHshW[2] * x;\n s += uHshW[3] * 2.0 * x * y + uHshW[4] * 2.0 * y * z + uHshW[5] * (1.5 * z2 - 0.5)\n + uHshW[6] * 2.0 * x * z + uHshW[7] * (x2 - y2);\n s += uHshW[8] * y * (3.0 * x2 - y2)\n + uHshW[9] * 5.2 * x * y * z\n + uHshW[10] * 0.8 * y * (5.0 * z2 - 1.0)\n + uHshW[11] * 0.5 * z * (5.0 * z2 - 3.0)\n + uHshW[12] * 0.8 * x * (5.0 * z2 - 1.0)\n + uHshW[13] * 2.6 * z * (x2 - y2)\n + uHshW[14] * x * (x2 - 3.0 * y2);\n s += uHshW[15] * 4.0 * x * y * (x2 - y2)\n + uHshW[16] * 3.0 * y * z * (3.0 * x2 - y2)\n + uHshW[17] * 1.5 * x * y * (7.0 * z2 - 1.0)\n + uHshW[18] * 0.95 * y * z * (7.0 * z2 - 3.0)\n + uHshW[19] * 0.125 * (35.0 * z2 * z2 - 30.0 * z2 + 3.0)\n + uHshW[20] * 0.95 * x * z * (7.0 * z2 - 3.0)\n + uHshW[21] * 0.78 * (x2 - y2) * (7.0 * z2 - 1.0)\n + uHshW[22] * 3.0 * x * z * (x2 - 3.0 * y2)\n + uHshW[23] * (x2 * (x2 - 3.0 * y2) - y2 * (3.0 * x2 - y2));\n return s;\n }\n\n // The resting shell: radius along direction n, in shell-local units.\n float hshRadius(vec3 n, out float field){\n field = hshSh(n);\n return uHshBound * uHshSize * (1.0 + uHshAmplitude * field);\n }\n\n // Thin-film interference, as on a soap bubble: a film of index 1.33 and\n // thickness d delays the inner reflection by 2*n*d*cos(theta_t). Each\n // wavelength interferes on its own, which splits white light into bands.\n vec3 hshFilm(float ndv, float field){\n float cosT = sqrt(max(1.0 - (1.0 - ndv * ndv) / (1.33 * 1.33), 0.0));\n float opd = 2.0 * 1.33 * uHshFilm * (1.0 + 0.35 * field) * cosT;\n vec3 film = 0.5 + 0.5 * cos(6.28318530718 * opd / vec3(650.0, 532.0, 450.0));\n float lum = dot(film, vec3(0.299, 0.587, 0.114));\n return mix(film, lum * uHshColor * 1.7 + film * 0.12, uHshTint);\n }\n\n // \u2500\u2500 the target: signed distance in shell-local units (negative inside) \u2500\u2500\n float hshSdf2(vec2 p){\n vec2 uv = p / (2.0 * uHshSdfHalf) + 0.5;\n vec2 c = clamp(uv, 0.0, 1.0);\n return texture2D(uHshSdf, c).r + length((uv - c) * 2.0 * uHshSdfHalf);\n }\n float hshSdf(vec3 p){\n if (uHshMode > 1.5) {\n // an object's own field, baked in its local space\n vec3 q = (uHshSdf3Inv * vec4(p, 1.0)).xyz;\n vec3 uvw = (q - uHshSdf3Min) / uHshSdf3Size; vec3 c = clamp(uvw, 0.0, 1.0);\n return texture(uHshSdf3, c).r + length((uvw - c) * uHshSdf3Size);\n }\n // text / shape: the 2D field extruded to \u00B1depth\n float d2 = hshSdf2(p.xy); float dz = abs(p.z) - uHshDepth;\n vec2 w = max(vec2(d2, dz), 0.0);\n return min(max(d2, dz), 0.0) + length(w);\n }\n vec3 hshSdfGrad(vec3 p){\n float e = 0.012;\n vec3 g = vec3(hshSdf(p + vec3(e, 0, 0)) - hshSdf(p - vec3(e, 0, 0)),\n hshSdf(p + vec3(0, e, 0)) - hshSdf(p - vec3(0, e, 0)),\n hshSdf(p + vec3(0, 0, e)) - hshSdf(p - vec3(0, 0, e)));\n float l = length(g); return l > 1e-6 ? g / l : vec3(0.0, 0.0, 1.0);\n }\n\n // Signed distance from a shell-local point to the shell's CURRENT surface\n // (the resting shell blended with the target by uHshMorph). 'field' is the\n // harmonic value in that direction, for the film.\n // Where a sphere vertex starts its trace: the resting shell point stretched to the\n // target's extent, so a wide word or a long object gets vertices along its whole length\n // instead of everything converging on the part nearest the centre.\n vec3 hshTraceStart(vec3 p0){\n float R0 = uHshBound * uHshSize;\n if (uHshMode > 1.5) {\n vec3 c = uHshSdf3Min + 0.5 * uHshSdf3Size; // the target's centre, in its own space\n vec3 h = uHshSdf3Size * 0.44; // half extents (the baked box is padded 12%)\n vec3 ct = (uHshSdf3Inv * vec4(0.0, 0.0, 0.0, 1.0)).xyz;\n vec3 qt = (uHshSdf3Inv * vec4(p0, 1.0)).xyz;\n qt = c + (qt - ct) * (h / R0);\n return (uHshSdf3Mat * vec4(qt, 1.0)).xyz;\n }\n return p0 * vec3(uHshSdfHalf / R0, max(uHshDepth, 0.05) / R0);\n }\n\n float hshDist(vec3 p, out float field){\n float r = length(p); vec3 n = r > 1e-5 ? p / r : vec3(0.0, 1.0, 0.0);\n float dS = r - hshRadius(n, field);\n return uHshMode > 0.5 && uHshMorph > 0.0 ? mix(dS, hshSdf(p), uHshMorph) : dS;\n }\n\n\n vec3 shellPoint(vec3 n, out float f){ return n * hshRadius(n, f); }\n\n void main(){\n vec3 n = normalize(position);\n float f, f1, f2;\n vec3 p0 = shellPoint(n, f);\n\n // normal from two nearby points on the displaced surface\n vec3 up = abs(n.y) > 0.99 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);\n vec3 t1 = normalize(cross(n, up));\n vec3 t2 = cross(n, t1);\n vec3 p1 = shellPoint(normalize(n + t1 * 0.01), f1);\n vec3 p2 = shellPoint(normalize(n + t2 * 0.01), f2);\n vec3 nrm = normalize(cross(p1 - p0, p2 - p0));\n if (dot(nrm, n) < 0.0) nrm = -nrm;\n\n vec3 p = p0; vec3 N = nrm;\n if (uHshMode > 0.5 && uHshMorph > 0.0) {\n // trace the vertex onto the target surface along the field's gradient\n vec3 q = hshTraceStart(p0);\n for (int i = 0; i < 12; i++) { float d = hshSdf(q); q -= d * hshSdfGrad(q) * 0.85; }\n vec3 g = hshSdfGrad(q);\n // the harmonics keep breathing on the target, along its normal\n q += g * uHshBound * uHshAmplitude * uBreath * f;\n p = mix(p0, q, uHshMorph);\n N = normalize(mix(nrm, g, uHshMorph));\n }\n\n vLocal = p;\n vField = f;\n vec4 mv = modelViewMatrix * vec4(p, 1.0);\n vNormal = normalize(normalMatrix * N);\n vViewDir = normalize(-mv.xyz);\n gl_Position = projectionMatrix * mv;\n }\n";
28
+ export declare const SHELL_FRAG = "\n uniform float uOpacity;\n uniform float uIntensity;\n uniform float uNodalLines;\n uniform float uWeb;\n varying vec3 vNormal;\n varying vec3 vViewDir;\n varying float vField;\n varying vec3 vLocal;\n \n uniform float uHshW[24];\n uniform float uHshAmplitude; uniform float uHshBound; uniform float uHshSize;\n uniform vec3 uHshColor; uniform vec3 uHshRim; uniform float uHshFilm; uniform float uHshTint; uniform float uHshTime;\n uniform float uHshMorph; uniform float uHshMode;\n uniform sampler2D uHshSdf; uniform vec2 uHshSdfHalf; uniform float uHshDepth;\n uniform sampler3D uHshSdf3; uniform vec3 uHshSdf3Min; uniform vec3 uHshSdf3Size; uniform mat4 uHshSdf3Inv; uniform mat4 uHshSdf3Mat;\n\n\n // Real spherical harmonics for l = 1..4, as polynomials in the unit vector,\n // each scaled so its peak is about 1. The CPU animates the 24 weights.\n float hshSh(vec3 n){\n float x = n.x, y = n.y, z = n.z;\n float x2 = x * x, y2 = y * y, z2 = z * z;\n float s = 0.0;\n s += uHshW[0] * y + uHshW[1] * z + uHshW[2] * x;\n s += uHshW[3] * 2.0 * x * y + uHshW[4] * 2.0 * y * z + uHshW[5] * (1.5 * z2 - 0.5)\n + uHshW[6] * 2.0 * x * z + uHshW[7] * (x2 - y2);\n s += uHshW[8] * y * (3.0 * x2 - y2)\n + uHshW[9] * 5.2 * x * y * z\n + uHshW[10] * 0.8 * y * (5.0 * z2 - 1.0)\n + uHshW[11] * 0.5 * z * (5.0 * z2 - 3.0)\n + uHshW[12] * 0.8 * x * (5.0 * z2 - 1.0)\n + uHshW[13] * 2.6 * z * (x2 - y2)\n + uHshW[14] * x * (x2 - 3.0 * y2);\n s += uHshW[15] * 4.0 * x * y * (x2 - y2)\n + uHshW[16] * 3.0 * y * z * (3.0 * x2 - y2)\n + uHshW[17] * 1.5 * x * y * (7.0 * z2 - 1.0)\n + uHshW[18] * 0.95 * y * z * (7.0 * z2 - 3.0)\n + uHshW[19] * 0.125 * (35.0 * z2 * z2 - 30.0 * z2 + 3.0)\n + uHshW[20] * 0.95 * x * z * (7.0 * z2 - 3.0)\n + uHshW[21] * 0.78 * (x2 - y2) * (7.0 * z2 - 1.0)\n + uHshW[22] * 3.0 * x * z * (x2 - 3.0 * y2)\n + uHshW[23] * (x2 * (x2 - 3.0 * y2) - y2 * (3.0 * x2 - y2));\n return s;\n }\n\n // The resting shell: radius along direction n, in shell-local units.\n float hshRadius(vec3 n, out float field){\n field = hshSh(n);\n return uHshBound * uHshSize * (1.0 + uHshAmplitude * field);\n }\n\n // Thin-film interference, as on a soap bubble: a film of index 1.33 and\n // thickness d delays the inner reflection by 2*n*d*cos(theta_t). Each\n // wavelength interferes on its own, which splits white light into bands.\n vec3 hshFilm(float ndv, float field){\n float cosT = sqrt(max(1.0 - (1.0 - ndv * ndv) / (1.33 * 1.33), 0.0));\n float opd = 2.0 * 1.33 * uHshFilm * (1.0 + 0.35 * field) * cosT;\n vec3 film = 0.5 + 0.5 * cos(6.28318530718 * opd / vec3(650.0, 532.0, 450.0));\n float lum = dot(film, vec3(0.299, 0.587, 0.114));\n return mix(film, lum * uHshColor * 1.7 + film * 0.12, uHshTint);\n }\n\n // \u2500\u2500 the target: signed distance in shell-local units (negative inside) \u2500\u2500\n float hshSdf2(vec2 p){\n vec2 uv = p / (2.0 * uHshSdfHalf) + 0.5;\n vec2 c = clamp(uv, 0.0, 1.0);\n return texture2D(uHshSdf, c).r + length((uv - c) * 2.0 * uHshSdfHalf);\n }\n float hshSdf(vec3 p){\n if (uHshMode > 1.5) {\n // an object's own field, baked in its local space\n vec3 q = (uHshSdf3Inv * vec4(p, 1.0)).xyz;\n vec3 uvw = (q - uHshSdf3Min) / uHshSdf3Size; vec3 c = clamp(uvw, 0.0, 1.0);\n return texture(uHshSdf3, c).r + length((uvw - c) * uHshSdf3Size);\n }\n // text / shape: the 2D field extruded to \u00B1depth\n float d2 = hshSdf2(p.xy); float dz = abs(p.z) - uHshDepth;\n vec2 w = max(vec2(d2, dz), 0.0);\n return min(max(d2, dz), 0.0) + length(w);\n }\n vec3 hshSdfGrad(vec3 p){\n float e = 0.012;\n vec3 g = vec3(hshSdf(p + vec3(e, 0, 0)) - hshSdf(p - vec3(e, 0, 0)),\n hshSdf(p + vec3(0, e, 0)) - hshSdf(p - vec3(0, e, 0)),\n hshSdf(p + vec3(0, 0, e)) - hshSdf(p - vec3(0, 0, e)));\n float l = length(g); return l > 1e-6 ? g / l : vec3(0.0, 0.0, 1.0);\n }\n\n // Signed distance from a shell-local point to the shell's CURRENT surface\n // (the resting shell blended with the target by uHshMorph). 'field' is the\n // harmonic value in that direction, for the film.\n // Where a sphere vertex starts its trace: the resting shell point stretched to the\n // target's extent, so a wide word or a long object gets vertices along its whole length\n // instead of everything converging on the part nearest the centre.\n vec3 hshTraceStart(vec3 p0){\n float R0 = uHshBound * uHshSize;\n if (uHshMode > 1.5) {\n vec3 c = uHshSdf3Min + 0.5 * uHshSdf3Size; // the target's centre, in its own space\n vec3 h = uHshSdf3Size * 0.44; // half extents (the baked box is padded 12%)\n vec3 ct = (uHshSdf3Inv * vec4(0.0, 0.0, 0.0, 1.0)).xyz;\n vec3 qt = (uHshSdf3Inv * vec4(p0, 1.0)).xyz;\n qt = c + (qt - ct) * (h / R0);\n return (uHshSdf3Mat * vec4(qt, 1.0)).xyz;\n }\n return p0 * vec3(uHshSdfHalf / R0, max(uHshDepth, 0.05) / R0);\n }\n\n float hshDist(vec3 p, out float field){\n float r = length(p); vec3 n = r > 1e-5 ? p / r : vec3(0.0, 1.0, 0.0);\n float dS = r - hshRadius(n, field);\n return uHshMode > 0.5 && uHshMorph > 0.0 ? mix(dS, hshSdf(p), uHshMorph) : dS;\n }\n\n\n void main(){\n vec3 N = normalize(vNormal);\n vec3 V = normalize(vViewDir);\n float ndv = abs(dot(N, V));\n\n // the film is thicker where the harmonic pushes the shell out, so the colour bands follow the shape\n vec3 film = hshFilm(ndv, vField);\n\n float fres = pow(1.0 - ndv, 3.0);\n vec3 col = uHshColor * 0.25 + film * (0.2 + 0.8 * fres);\n col = mix(col, uHshRim, fres * 0.45);\n\n // nodal lines: where the harmonic field crosses zero\n float nd = abs(vField) / max(fwidth(vField), 1e-4);\n float nodal = (1.0 - smoothstep(0.5, 1.5, nd)) * uNodalLines;\n col += uHshRim * nodal * 1.5;\n\n float a = (0.06 + 0.9 * fres + nodal * 0.8) * uOpacity;\n // webbing between letters: pixels that are not on the target fade out as the morph completes\n if (uHshMode > 0.5 && uHshMorph > 0.0) { float d = abs(hshSdf(vLocal)); a *= 1.0 - uHshMorph * smoothstep(uWeb, uWeb * 3.0, d); }\n if (a < 0.003) discard;\n gl_FragColor = vec4(col * uIntensity, a);\n }\n";
29
+ export type HshUniforms = Record<string, THREE.IUniform>;
30
+ /** Fresh uniform objects for one shell (the shell writes them every frame; effects read them). */
31
+ export declare function createShellUniforms(): HshUniforms;
32
+ /** The keys an effect copies from a shell into its own uniforms (everything but the time, which it keeps). */
33
+ export declare const HSH_SHARED_KEYS: readonly ["uHshW", "uHshAmplitude", "uHshBound", "uHshSize", "uHshColor", "uHshRim", "uHshFilm", "uHshTint", "uHshMorph", "uHshMode", "uHshSdf", "uHshSdfHalf", "uHshDepth", "uHshSdf3", "uHshSdf3Min", "uHshSdf3Size", "uHshSdf3Inv", "uHshSdf3Mat"];
34
+ /** Copy a shell's current values into another uniform set (objects are shared by reference, scalars by value). */
35
+ export declare function copyShellUniforms(from: HshUniforms, to: HshUniforms): void;
36
+ export interface ShellField {
37
+ /** The shell's root group (its world matrix is the field's frame). */
38
+ root: Object3D;
39
+ uniforms: HshUniforms;
40
+ /** World → shell-local, refreshed by the shell every frame. */
41
+ inv: THREE.Matrix4;
42
+ /** The object the shell is wrapping right now (mode object), so it can be left out of the inside look. */
43
+ target: Object3D | null;
44
+ }
45
+ export declare function registerShellField(f: ShellField): void;
46
+ export declare function unregisterShellField(f: ShellField): void;
47
+ export declare const shellFields: () => ReadonlySet<ShellField>;
48
+ /** The shell whose centre is nearest a world point (what `inShell` reacts to), or null when the scene has none. */
49
+ export declare function nearestShellField(worldPos: THREE.Vector3): ShellField | null;
50
+ export declare const SHELL_SHAPES: readonly ["star", "heart", "ring", "hexagon", "triangle", "cross", "disc", "square", "diamond", "pentagon", "octagon", "sparkle", "hexstar", "crescent", "drop", "bolt", "arrow", "cloud", "flower", "gear", "shield", "leaf", "capsule", "infinity", "chevron", "hourglass", "clover", "spade", "sun", "halfmoon"];
51
+ export type ShellShape = (typeof SHELL_SHAPES)[number];
52
+ /** CSS font families the `font` option maps to (the page's own fonts, with system fallbacks). */
53
+ export declare const SHELL_FONTS: Record<string, string>;
54
+ export interface Sdf2 {
55
+ tex: THREE.DataTexture;
56
+ half: number;
57
+ }
58
+ /**
59
+ * The 2D field of a word or a shape, sized to the shell: `half` is the half-width it
60
+ * spans in shell-local units (the shell's resting radius, a little more).
61
+ * Text waits for the page font once (document.fonts), then draws.
62
+ */
63
+ export declare function bakeShapeSdf(kind: 'text' | ShellShape, text: string, font: string, half: number): Promise<Sdf2 | null>;
64
+ export interface Sdf3 {
65
+ tex: THREE.Data3DTexture;
66
+ min: THREE.Vector3;
67
+ size: THREE.Vector3;
68
+ }
69
+ /**
70
+ * The object's signed distance field in its root-local space: all its meshes merged
71
+ * (position only, see mergedPositions), sampled on a 48³ grid with the BVH's
72
+ * closest-point query, sign from the hit face. Baked in slices so the page stays
73
+ * responsive; cached on the root until its geometry changes. ~100–300 ms for a
74
+ * typical model on a desktop CPU — do it at load (pre-bake), never on trigger.
75
+ */
76
+ export declare function bakeObjectSdf(root: Object3D): Promise<Sdf3 | null>;
77
+ //# sourceMappingURL=harmonicShellField.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harmonicShellField.d.ts","sourceRoot":"","sources":["../../../src/components/effects/harmonicShellField.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAgBtC,MAAM,WAAW,aAAa;IAC1B,EAAE,EAAE,YAAY,CAAC;IAAC,EAAE,EAAE,YAAY,CAAC;IAAC,EAAE,EAAE,YAAY,CAAC;IAAC,EAAE,EAAE,YAAY,CAAC;IACvE,IAAI,EAAE,YAAY,CAAC;CACtB;AAED,2FAA2F;AAC3F,wBAAgB,mBAAmB,IAAI,aAAa,CAUnD;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAe1H;AAID,iHAAiH;AACjH,eAAO,MAAM,cAAc,0fAO1B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe,miJAiG3B,CAAC;AAEF,2FAA2F;AAC3F,eAAO,MAAM,cAAc,QAAmC,CAAC;AAE/D,2GAA2G;AAC3G,eAAO,MAAM,UAAU,y9MA2CtB,CAAC;AAEF,eAAO,MAAM,UAAU,quMAkCtB,CAAC;AAKF,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;AAczD,kGAAkG;AAClG,wBAAgB,mBAAmB,IAAI,WAAW,CAUjD;AAED,8GAA8G;AAC9G,eAAO,MAAM,eAAe,uPAGlB,CAAC;AAEX,kHAAkH;AAClH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,QAMnE;AAID,MAAM,WAAW,UAAU;IACvB,sEAAsE;IACtE,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,WAAW,CAAC;IACtB,+DAA+D;IAC/D,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC;IACnB,0GAA0G;IAC1G,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;CAC3B;AAID,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,UAAU,QAAoB;AACpE,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,UAAU,QAAuB;AACzE,eAAO,MAAM,WAAW,QAAO,WAAW,CAAC,UAAU,CAAW,CAAC;AAGjE,mHAAmH;AACnH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,GAAG,UAAU,GAAG,IAAI,CAQ5E;AAID,eAAO,MAAM,YAAY,qTAKf,CAAC;AACX,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD,iGAAiG;AACjG,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAK9C,CAAC;AAsBF,MAAM,WAAW,IAAI;IAAG,GAAG,EAAE,KAAK,CAAC,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE;AA2G9D;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAK5H;AAID,MAAM,WAAW,IAAI;IAAG,GAAG,EAAE,KAAK,CAAC,aAAa,CAAC;IAAC,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC;IAAC,IAAI,EAAE,KAAK,CAAC,OAAO,CAAA;CAAE;AA8C3F;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CA4CxE"}