@3driseai/3drise-viewer 0.1.16 → 0.1.17

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 (116) hide show
  1. package/README.md +4 -3
  2. package/dist/components/ChladniGrid.d.ts +15 -0
  3. package/dist/components/ChladniGrid.d.ts.map +1 -0
  4. package/dist/components/ChladniGrid.js +62 -0
  5. package/dist/components/CustomPrimitive.d.ts +2 -0
  6. package/dist/components/CustomPrimitive.d.ts.map +1 -1
  7. package/dist/components/CustomPrimitive.js +2 -2
  8. package/dist/components/EffectsGenerator.d.ts.map +1 -1
  9. package/dist/components/EffectsGenerator.js +26 -2
  10. package/dist/components/FlowNetGrid.d.ts +15 -0
  11. package/dist/components/FlowNetGrid.d.ts.map +1 -0
  12. package/dist/components/FlowNetGrid.js +61 -0
  13. package/dist/components/GirihGrid.d.ts +15 -0
  14. package/dist/components/GirihGrid.d.ts.map +1 -0
  15. package/dist/components/GirihGrid.js +62 -0
  16. package/dist/components/GravWaveGrid.d.ts +15 -0
  17. package/dist/components/GravWaveGrid.d.ts.map +1 -0
  18. package/dist/components/GravWaveGrid.js +62 -0
  19. package/dist/components/GridsGenerator.d.ts +2 -2
  20. package/dist/components/GridsGenerator.d.ts.map +1 -1
  21. package/dist/components/GridsGenerator.js +21 -0
  22. package/dist/components/HyperbolicGrid.d.ts +15 -0
  23. package/dist/components/HyperbolicGrid.d.ts.map +1 -0
  24. package/dist/components/HyperbolicGrid.js +61 -0
  25. package/dist/components/LayoutInstances.d.ts +13 -0
  26. package/dist/components/LayoutInstances.d.ts.map +1 -0
  27. package/dist/components/LayoutInstances.js +39 -0
  28. package/dist/components/ModelPrimitive.d.ts +2 -0
  29. package/dist/components/ModelPrimitive.d.ts.map +1 -1
  30. package/dist/components/ModelPrimitive.js +3 -3
  31. package/dist/components/ObjectNode.d.ts.map +1 -1
  32. package/dist/components/ObjectNode.js +36 -8
  33. package/dist/components/ReactionDiffusionGrid.d.ts +15 -0
  34. package/dist/components/ReactionDiffusionGrid.d.ts.map +1 -0
  35. package/dist/components/ReactionDiffusionGrid.js +376 -0
  36. package/dist/components/WallpaperGrid.d.ts +15 -0
  37. package/dist/components/WallpaperGrid.d.ts.map +1 -0
  38. package/dist/components/WallpaperGrid.js +555 -0
  39. package/dist/components/cursor/CursorController.d.ts.map +1 -1
  40. package/dist/components/effects/BlackHoleEffect.d.ts +3 -3
  41. package/dist/components/effects/BlackHoleEffect.d.ts.map +1 -1
  42. package/dist/components/effects/BlackHoleEffect.js +293 -163
  43. package/dist/components/effects/DandelionSeedsEffect.d.ts +13 -0
  44. package/dist/components/effects/DandelionSeedsEffect.d.ts.map +1 -0
  45. package/dist/components/effects/DandelionSeedsEffect.js +510 -0
  46. package/dist/components/effects/FallingLeavesEffect.d.ts +13 -0
  47. package/dist/components/effects/FallingLeavesEffect.d.ts.map +1 -0
  48. package/dist/components/effects/FallingLeavesEffect.js +446 -0
  49. package/dist/components/effects/FieldLinesEffect.d.ts +11 -0
  50. package/dist/components/effects/FieldLinesEffect.d.ts.map +1 -0
  51. package/dist/components/effects/FieldLinesEffect.js +260 -0
  52. package/dist/components/effects/FireflySwarmEffect.d.ts +13 -0
  53. package/dist/components/effects/FireflySwarmEffect.d.ts.map +1 -0
  54. package/dist/components/effects/FireflySwarmEffect.js +408 -0
  55. package/dist/components/effects/GalaxyEffect.d.ts +13 -0
  56. package/dist/components/effects/GalaxyEffect.d.ts.map +1 -0
  57. package/dist/components/effects/GalaxyEffect.js +374 -0
  58. package/dist/components/effects/HarmonicShellEffect.d.ts +11 -0
  59. package/dist/components/effects/HarmonicShellEffect.d.ts.map +1 -0
  60. package/dist/components/effects/HarmonicShellEffect.js +209 -0
  61. package/dist/components/effects/IceCrystalsEffect.d.ts +3 -3
  62. package/dist/components/effects/IceCrystalsEffect.d.ts.map +1 -1
  63. package/dist/components/effects/IceCrystalsEffect.js +286 -166
  64. package/dist/components/effects/IceHaloEffect.d.ts +13 -0
  65. package/dist/components/effects/IceHaloEffect.d.ts.map +1 -0
  66. package/dist/components/effects/IceHaloEffect.js +306 -0
  67. package/dist/components/effects/LichtenbergEffect.d.ts +13 -0
  68. package/dist/components/effects/LichtenbergEffect.d.ts.map +1 -0
  69. package/dist/components/effects/LichtenbergEffect.js +517 -0
  70. package/dist/components/effects/MoleculesEffect.d.ts +3 -3
  71. package/dist/components/effects/MoleculesEffect.d.ts.map +1 -1
  72. package/dist/components/effects/MoleculesEffect.js +322 -164
  73. package/dist/components/effects/MurmurationEffect.d.ts +13 -0
  74. package/dist/components/effects/MurmurationEffect.d.ts.map +1 -0
  75. package/dist/components/effects/MurmurationEffect.js +539 -0
  76. package/dist/components/effects/PlanetaryRingsEffect.d.ts +13 -0
  77. package/dist/components/effects/PlanetaryRingsEffect.d.ts.map +1 -0
  78. package/dist/components/effects/PlanetaryRingsEffect.js +458 -0
  79. package/dist/components/effects/PortalEffect.d.ts +3 -3
  80. package/dist/components/effects/PortalEffect.d.ts.map +1 -1
  81. package/dist/components/effects/PortalEffect.js +240 -163
  82. package/dist/components/effects/ShockwaveEffect.d.ts +5 -3
  83. package/dist/components/effects/ShockwaveEffect.d.ts.map +1 -1
  84. package/dist/components/effects/ShockwaveEffect.js +538 -142
  85. package/dist/components/effects/SmokePlumeEffect.d.ts +3 -3
  86. package/dist/components/effects/SmokePlumeEffect.d.ts.map +1 -1
  87. package/dist/components/effects/SmokePlumeEffect.js +180 -116
  88. package/dist/components/effects/SmokeRingEffect.d.ts +3 -3
  89. package/dist/components/effects/SmokeRingEffect.d.ts.map +1 -1
  90. package/dist/components/effects/SmokeRingEffect.js +260 -135
  91. package/dist/components/effects/StrangeAttractorEffect.d.ts +11 -0
  92. package/dist/components/effects/StrangeAttractorEffect.d.ts.map +1 -0
  93. package/dist/components/effects/StrangeAttractorEffect.js +229 -0
  94. package/dist/components/effects/VolumetricFogEffect.d.ts +5 -3
  95. package/dist/components/effects/VolumetricFogEffect.d.ts.map +1 -1
  96. package/dist/components/effects/VolumetricFogEffect.js +211 -93
  97. package/dist/components/effects/WaterCausticsEffect.d.ts +13 -0
  98. package/dist/components/effects/WaterCausticsEffect.d.ts.map +1 -0
  99. package/dist/components/effects/WaterCausticsEffect.js +308 -0
  100. package/dist/components/effects/index.d.ts +9 -0
  101. package/dist/components/effects/index.d.ts.map +1 -1
  102. package/dist/components/effects/index.js +9 -0
  103. package/dist/components/index.d.ts +16 -0
  104. package/dist/components/index.d.ts.map +1 -1
  105. package/dist/components/index.js +8 -0
  106. package/dist/constants.d.ts +1 -1
  107. package/dist/constants.d.ts.map +1 -1
  108. package/dist/constants.js +1 -1
  109. package/dist/hooks/useModelObject.d.ts +6 -1
  110. package/dist/hooks/useModelObject.d.ts.map +1 -1
  111. package/dist/hooks/useModelObject.js +19 -6
  112. package/dist/types/gridConfigs.d.ts +98 -0
  113. package/dist/types/gridConfigs.d.ts.map +1 -1
  114. package/dist/types/index.d.ts +1 -1
  115. package/dist/types/index.d.ts.map +1 -1
  116. package/package.json +2 -2
@@ -1,161 +1,286 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useRef, useMemo } from 'react';
3
- import { useFrame } from '@react-three/fiber';
2
+ import { useEffect, useLayoutEffect, useMemo, useRef } from 'react';
4
3
  import * as THREE from 'three';
4
+ import { useFrame } from '@react-three/fiber';
5
5
  import { applyGenerativeAnimations } from '../../utils/handleGenerativeEffectsAnimations.js';
6
6
  import { useObjectReady } from '../../hooks/useObjectReady.js';
7
- // ── Per-ring puff shader (one Points per ring — GPU computes torus positions) ──
7
+ /**
8
+ * Physically simulated vortex rings.
9
+ *
10
+ * A tiny vortex-filament simulation runs on the CPU (at most 4 rings): each
11
+ * ring moves at Kelvin's self-induced speed U = G/(4 pi R) (ln(8R/a) - 1/4),
12
+ * rings push each other through Biot-Savart induction (so pairs leapfrog),
13
+ * entrain air and widen, and lose circulation to viscosity. The GPU places
14
+ * smoke particles in each ring's Rankine core, spins them around it and adds
15
+ * a Widnall-style wobble as the ring ages.
16
+ *
17
+ * Launch settings are read from uniforms, so keyframe animations drive them too.
18
+ */
19
+ const PRE_NORMAL = { transparent: true, depthWrite: false, blending: THREE.NormalBlending, premultipliedAlpha: true };
20
+ const RING_SLOTS = 4;
21
+ const PARTICLES_PER_RING = 1400;
22
+ function mulberry32(seed) {
23
+ return () => {
24
+ seed |= 0;
25
+ seed = (seed + 0x6d2b79f5) | 0;
26
+ let r = Math.imul(seed ^ (seed >>> 15), 1 | seed);
27
+ r = (r + Math.imul(r ^ (r >>> 7), 61 | r)) ^ r;
28
+ return ((r ^ (r >>> 14)) >>> 0) / 4294967296;
29
+ };
30
+ }
31
+ function buildRingParticles() {
32
+ const rand = mulberry32(23);
33
+ const n = RING_SLOTS * PARTICLES_PER_RING;
34
+ const aParticle = new Float32Array(n * 4);
35
+ const aSize = new Float32Array(n);
36
+ for (let s = 0; s < RING_SLOTS; s++) {
37
+ for (let i = 0; i < PARTICLES_PER_RING; i++) {
38
+ const k = s * PARTICLES_PER_RING + i;
39
+ aParticle[k * 4] = s;
40
+ aParticle[k * 4 + 1] = rand() * Math.PI * 2;
41
+ // most smoke sits in the core, a thinner halo trails outside it
42
+ aParticle[k * 4 + 2] = rand() < 0.75 ? Math.sqrt(rand()) : 1 + rand() * 1.2;
43
+ aParticle[k * 4 + 3] = rand() * Math.PI * 2;
44
+ aSize[k] = 0.5 + rand() * 0.9;
45
+ }
46
+ }
47
+ return { count: n, aParticle, aSize };
48
+ }
49
+ function createRingSim() {
50
+ return {
51
+ rings: Array.from({ length: RING_SLOTS }, () => ({ alive: false, R: 0, Z: 0, a: 0, G: 0, age: 0, theta: 0, alpha: 0, seed: 0 })),
52
+ timer: 0,
53
+ launches: 0,
54
+ };
55
+ }
56
+ /** Advances the rings by dt and writes uRingA (R, Z, a, age) and uRingB (G, alpha, theta, seed) per slot. */
57
+ function stepRingSim(sim, dt, p, outA, outB) {
58
+ const bound = p.bound;
59
+ const maxRings = Math.round(Math.min(Math.max(p.ringCount, 1), RING_SLOTS));
60
+ const R0 = bound * 0.6;
61
+ const a0 = Math.max(bound * p.thickness * 0.55, R0 * 0.05);
62
+ const U0 = bound * p.riseSpeed * 0.9;
63
+ // Kelvin's self-induced speed: U = G/(4 pi R) (ln(8R/a) - 1/4)
64
+ const kelvin = (R, a) => Math.log(8 * R / a) - 0.25;
65
+ const G0 = (U0 * 4 * Math.PI * R0) / kelvin(R0, a0);
66
+ sim.timer -= dt;
67
+ const alive = sim.rings.filter((r) => r.alive).length;
68
+ const pair = p.leapfrog && maxRings >= 2;
69
+ if (sim.timer <= 0 && alive + (pair ? 2 : 1) <= maxRings) {
70
+ const spawn = (dz, dr) => {
71
+ const r = sim.rings.find((x) => !x.alive);
72
+ if (!r)
73
+ return;
74
+ Object.assign(r, { alive: true, R: R0 * dr, Z: bound * 0.55 + dz, a: a0, G: G0, age: 0, theta: 0, alpha: 0, seed: (sim.launches++ * 1.618) % 6.283 });
75
+ };
76
+ if (pair) {
77
+ spawn(0, 1);
78
+ spawn(-R0 * 0.55, 1);
79
+ }
80
+ else
81
+ spawn(0, 1);
82
+ sim.timer = Math.max(p.intervalSeconds, 0.3) * (pair ? 1.8 : 1);
83
+ }
84
+ const sub = 3;
85
+ const h = dt / sub;
86
+ const live = sim.rings.filter((r) => r.alive);
87
+ for (let s = 0; s < sub; s++) {
88
+ const vel = live.map((ri) => {
89
+ let vz = (ri.G / (4 * Math.PI * ri.R)) * kelvin(ri.R, ri.a);
90
+ let vr = 0;
91
+ if (p.leapfrog) {
92
+ for (const rj of live) {
93
+ if (rj === ri)
94
+ continue;
95
+ const [ur, uz] = ringInduced(ri.R, ri.Z, rj.R, rj.Z, rj.G, rj.a);
96
+ vr += ur;
97
+ vz += uz;
98
+ }
99
+ }
100
+ return [vr, vz];
101
+ });
102
+ live.forEach((ri, k) => {
103
+ ri.R += (vel[k][0] + 0.04 * vel[k][1]) * h; // entrainment slowly widens the ring
104
+ ri.R = Math.max(ri.R, R0 * 0.3);
105
+ ri.Z += vel[k][1] * h;
106
+ ri.a += 0.015 * Math.abs(vel[k][1]) * h;
107
+ ri.G *= Math.exp(-0.12 * h); // viscous decay
108
+ // flow runs up through the ring and down outside it, so the core turns this way
109
+ ri.theta -= (ri.G / (2 * Math.PI * ri.a * ri.a)) * h * 0.25;
110
+ ri.age += h;
111
+ });
112
+ }
113
+ const maxR = bound * Math.max(p.expansionRadius, 1.2);
114
+ const maxZ = bound * (1.2 + p.expansionRadius);
115
+ for (const r of sim.rings) {
116
+ if (!r.alive) {
117
+ r.alpha = 0;
118
+ continue;
119
+ }
120
+ const fadeIn = Math.min(r.age / 0.4, 1);
121
+ const outR = 1 - smooth(maxR * 0.75, maxR, r.R);
122
+ const outZ = 1 - smooth(maxZ * 0.6, maxZ, r.Z);
123
+ const outG = smooth(G0 * 0.12, G0 * 0.3, r.G);
124
+ r.alpha = fadeIn * Math.min(outR, outZ, outG);
125
+ if (r.age > 1 && r.alpha < 0.003)
126
+ r.alive = false;
127
+ }
128
+ sim.rings.forEach((r, i) => {
129
+ outA[i * 4] = r.R;
130
+ outA[i * 4 + 1] = r.Z;
131
+ outA[i * 4 + 2] = r.a;
132
+ outA[i * 4 + 3] = r.age;
133
+ outB[i * 4] = r.G;
134
+ outB[i * 4 + 1] = r.alive ? r.alpha : 0;
135
+ outB[i * 4 + 2] = r.theta;
136
+ outB[i * 4 + 3] = r.seed;
137
+ });
138
+ }
139
+ /** Induced (radial, axial) velocity at (R, Z) from a circular vortex filament (Rj, Zj, G, a). */
140
+ function ringInduced(R, Z, Rj, Zj, G, a) {
141
+ const N = 48;
142
+ let vr = 0, vz = 0;
143
+ const dphi = (Math.PI * 2) / N;
144
+ for (let k = 0; k < N; k++) {
145
+ const ph = (k + 0.5) * dphi;
146
+ const c = Math.cos(ph), s = Math.sin(ph);
147
+ // filament point and tangent (x, y=axial, z)
148
+ const rx = R - Rj * c, ry = Z - Zj, rz = -Rj * s;
149
+ const dlx = -Rj * s * dphi, dlz = Rj * c * dphi;
150
+ const d2 = rx * rx + ry * ry + rz * rz + a * a;
151
+ const inv = 1 / (d2 * Math.sqrt(d2));
152
+ // (dl x r): dl = (dlx, 0, dlz)
153
+ const cx = 0 * rz - dlz * ry;
154
+ const cy = dlz * rx - dlx * rz;
155
+ vr += cx * inv;
156
+ vz += cy * inv;
157
+ }
158
+ // sign chosen so a ring with G > 0 induces +y flow through its own centre
159
+ const f = -G / (4 * Math.PI);
160
+ return [vr * f, vz * f];
161
+ }
162
+ function smooth(e0, e1, x) {
163
+ const t = Math.min(Math.max((x - e0) / (e1 - e0), 0), 1);
164
+ return t * t * (3 - 2 * t);
165
+ }
8
166
  const ringVert = /* glsl */ `
9
- attribute float aAngle; // 0..TAU position around torus
10
- attribute float aPuff; // sub-angle within tube cross-section 0..TAU
11
- attribute float aPhase;
12
-
13
- uniform float uProgress; // 0=born, 1=dead
14
- uniform float uMaxRadius;
15
- uniform float uThickness; // tube radius
16
- uniform float uRiseSpeed; // Y lift per unit progress
17
- uniform float uBaseY;
18
- uniform float u_time;
19
-
167
+ attribute vec4 aParticle; // x: ring slot, y: toroidal angle, z: rho (core units), w: poloidal phase
168
+ attribute float aSize;
169
+ uniform vec4 uRingA[4]; // R, Z, core radius a, age
170
+ uniform vec4 uRingB[4]; // circulation, alpha, core rotation angle, seed
171
+ uniform float uWobble;
172
+ uniform float uIntensity;
20
173
  varying float vAlpha;
21
-
22
- void main() {
23
- if (uProgress < 0.0 || uProgress > 1.0) {
174
+ varying float vShade;
175
+ void main(){
176
+ int slot = int(aParticle.x + 0.5);
177
+ vec4 A = uRingA[slot];
178
+ vec4 B = uRingB[slot];
179
+ if (B.y < 0.002){
24
180
  gl_PointSize = 0.0;
25
- gl_Position = vec4(0.0);
181
+ gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
26
182
  vAlpha = 0.0;
183
+ vShade = 0.0;
27
184
  return;
28
185
  }
29
-
30
- float ringR = uMaxRadius * uProgress;
31
-
32
- // Tube cross-section wiggles outward slightly as ring expands
33
- float tubeR = uThickness * (0.8 + 0.4 * uProgress);
34
- float tubePhi = aPuff + u_time * 0.4 + aPhase;
35
- float offR = tubeR * cos(tubePhi);
36
- float offY = tubeR * sin(tubePhi);
37
-
38
- float r = ringR + offR;
39
- float x = cos(aAngle) * r;
40
- float z = sin(aAngle) * r;
41
- float y = uBaseY + uProgress * uRiseSpeed + offY;
42
-
43
- // Puff grows then fades
44
- float lifeAlpha = smoothstep(0.0, 0.12, uProgress) * smoothstep(1.0, 0.65, uProgress);
45
- vAlpha = lifeAlpha;
46
-
47
- float puffSize = uThickness * 50.0 * lifeAlpha;
48
-
49
- vec4 mv = modelViewMatrix * vec4(x, y, z, 1.0);
50
- gl_PointSize = puffSize * (200.0 / -mv.z);
51
- gl_Position = projectionMatrix * mv;
186
+ float R = A.x, Z = A.y, a = A.z, age = A.w;
187
+ float rho = aParticle.z * a;
188
+ // Rankine vortex: solid-body rotation inside the core, falling off as
189
+ // 1/rho^2 outside it, so the outer smoke lags behind and shears into a spiral.
190
+ float k = aParticle.z <= 1.0 ? 1.0 : 1.0 / (aParticle.z * aParticle.z);
191
+ float th = aParticle.w + B.z * k;
192
+ float phi = aParticle.y;
193
+ // Widnall instability: an azimuthal wave of mode 7 grows as the ring ages
194
+ float wob = uWobble * a * min(0.04 * age * age, 1.6) * sin(7.0 * phi + age * 0.5 + B.w);
195
+ float Rr = R + wob + rho * cos(th);
196
+ float y = Z + rho * sin(th) + wob * 0.6;
197
+ vec3 pos = vec3(cos(phi) * Rr, y, sin(phi) * Rr);
198
+ vec4 mv = modelViewMatrix * vec4(pos, 1.0);
199
+ vAlpha = B.y * exp(-aParticle.z * aParticle.z * 0.8);
200
+ vShade = 0.6 + 0.4 * sin(th); // top of the core catches more light
201
+ gl_PointSize = aSize * a * uIntensity * 420.0 / max(-mv.z, 1e-3);
202
+ gl_Position = projectionMatrix * mv;
52
203
  }
53
204
  `;
54
205
  const ringFrag = /* glsl */ `
55
206
  uniform vec3 uSmokeColor;
56
207
  uniform float uOpacity;
57
208
  varying float vAlpha;
58
-
59
- void main() {
60
- if (vAlpha < 0.01) discard;
61
- vec2 c = gl_PointCoord - 0.5;
209
+ varying float vShade;
210
+ void main(){
211
+ vec2 c = gl_PointCoord - 0.5;
62
212
  float d = length(c);
63
- if (d > 0.5) discard;
64
- float g = exp(-d * d * 3.0);
65
- gl_FragColor = vec4(uSmokeColor, g * vAlpha * uOpacity);
213
+ if (d > 0.5 || vAlpha < 0.002) discard;
214
+ float g = exp(-d * d * 14.0);
215
+ float a = g * vAlpha * uOpacity * 0.7;
216
+ gl_FragColor = vec4(uSmokeColor * (0.55 + 0.6 * vShade) * a, a);
66
217
  }
67
218
  `;
68
- const PUFFS_PER_RING = 120;
69
219
  const SmokeRingEffect = ({ boundingRadius, config, animations, onObjectReady, onObjectDispose }) => {
70
- const { ringCount, expansionRadius, riseSpeed, intervalSeconds, smokeColor, thickness, opacity, speed, intensity } = config;
71
- const startValuesCacheRef = useRef({});
220
+ const { intensity = 1, speed = 1, opacity = 0.65, ringCount = 2, expansionRadius = 3, riseSpeed = 0.5, intervalSeconds = 2.5, smokeColor = '#c4c4c8', thickness = 0.18, leapfrog = true, wobble = 0.5, } = config;
72
221
  const rootRef = useRef(null);
222
+ const animCache = useRef({});
73
223
  useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
74
- const N = Math.max(1, Math.min(ringCount, 4));
75
- // Ring states — one per slot
76
- const ringStates = useRef([]);
77
- const { geos, mats } = useMemo(() => {
78
- const maxR = boundingRadius * expansionRadius;
79
- const tubeR = boundingRadius * thickness;
80
- const riseH = boundingRadius * riseSpeed;
81
- const sc = new THREE.Color(smokeColor);
82
- const scv = new THREE.Vector3(sc.r, sc.g, sc.b);
83
- // Stagger initial timers so rings don't all fire together
84
- ringStates.current = Array.from({ length: N }, (_, i) => ({
85
- progress: 0,
86
- timer: (i / N) * intervalSeconds,
87
- active: false,
88
- }));
89
- const geos = [];
90
- const mats = [];
91
- for (let ri = 0; ri < N; ri++) {
92
- const aAngle = new Float32Array(PUFFS_PER_RING);
93
- const aPuff = new Float32Array(PUFFS_PER_RING);
94
- const aPhase = new Float32Array(PUFFS_PER_RING);
95
- for (let i = 0; i < PUFFS_PER_RING; i++) {
96
- aAngle[i] = (i / PUFFS_PER_RING) * Math.PI * 2;
97
- aPuff[i] = Math.random() * Math.PI * 2;
98
- aPhase[i] = Math.random() * Math.PI * 2;
99
- }
100
- const geo = new THREE.BufferGeometry();
101
- geo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(PUFFS_PER_RING * 3), 3));
102
- geo.setAttribute('aAngle', new THREE.BufferAttribute(aAngle, 1));
103
- geo.setAttribute('aPuff', new THREE.BufferAttribute(aPuff, 1));
104
- geo.setAttribute('aPhase', new THREE.BufferAttribute(aPhase, 1));
105
- geo.frustumCulled = false;
106
- const mat = new THREE.ShaderMaterial({
107
- vertexShader: ringVert,
108
- fragmentShader: ringFrag,
109
- uniforms: {
110
- uProgress: { value: -1 }, // -1 = hidden
111
- uMaxRadius: { value: maxR },
112
- uThickness: { value: tubeR * intensity },
113
- uRiseSpeed: { value: riseH },
114
- uBaseY: { value: -boundingRadius * 0.2 },
115
- u_time: { value: 0 },
116
- uSmokeColor: { value: scv },
117
- uOpacity: { value: opacity },
118
- },
119
- transparent: true,
120
- depthWrite: false,
121
- blending: THREE.NormalBlending,
122
- });
123
- geos.push(geo);
124
- mats.push(mat);
125
- }
126
- return { geos, mats };
127
- // eslint-disable-next-line react-hooks/exhaustive-deps
128
- }, [N, boundingRadius, expansionRadius, riseSpeed, intervalSeconds, smokeColor, thickness, opacity, intensity]);
129
- useFrame((state, delta) => {
130
- const t = state.clock.getElapsedTime();
131
- const dt = delta * speed;
132
- ringStates.current.forEach((ring, ri) => {
133
- const mat = mats[ri];
134
- mat.uniforms.u_time.value = t;
135
- if (ring.active) {
136
- ring.progress += dt * 0.32;
137
- if (ring.progress >= 1) {
138
- ring.active = false;
139
- ring.progress = 0;
140
- ring.timer = intervalSeconds / speed;
141
- mat.uniforms.uProgress.value = -1;
142
- }
143
- else {
144
- mat.uniforms.uProgress.value = ring.progress;
145
- }
146
- }
147
- else {
148
- ring.timer -= delta;
149
- if (ring.timer <= 0) {
150
- ring.active = true;
151
- ring.progress = 0;
152
- }
153
- }
154
- });
155
- if (animations?.length) {
156
- applyGenerativeAnimations(mats, animations, startValuesCacheRef.current, Date.now(), false);
224
+ // a standalone effect gets boundingRadius 1; guard against an empty host
225
+ const b = boundingRadius > 0 ? boundingRadius : 1;
226
+ const uniforms = useMemo(() => ({
227
+ uSpeed: { value: 1 },
228
+ uIntensity: { value: 1 },
229
+ uOpacity: { value: 1 },
230
+ uSmokeColor: { value: new THREE.Color() },
231
+ uWobble: { value: 0.5 },
232
+ uRingA: { value: Array.from({ length: RING_SLOTS }, () => new THREE.Vector4()) },
233
+ uRingB: { value: Array.from({ length: RING_SLOTS }, () => new THREE.Vector4()) },
234
+ // read by the simulation, not the shader
235
+ uRingCount: { value: 2 },
236
+ uExpansionRadius: { value: 3 },
237
+ uRiseSpeed: { value: 0.5 },
238
+ uIntervalSeconds: { value: 2.5 },
239
+ uThickness: { value: 0.18 },
240
+ }), []);
241
+ const materials = useMemo(() => [
242
+ new THREE.ShaderMaterial({ vertexShader: ringVert, fragmentShader: ringFrag, uniforms, ...PRE_NORMAL }),
243
+ ], [uniforms]);
244
+ useEffect(() => () => materials.forEach((m) => m.dispose()), [materials]);
245
+ const geometry = useMemo(() => {
246
+ const p = buildRingParticles();
247
+ const g = new THREE.BufferGeometry();
248
+ g.setAttribute('position', new THREE.BufferAttribute(new Float32Array(p.count * 3), 3));
249
+ g.setAttribute('aParticle', new THREE.BufferAttribute(p.aParticle, 4));
250
+ g.setAttribute('aSize', new THREE.BufferAttribute(p.aSize, 1));
251
+ return g;
252
+ }, []);
253
+ useEffect(() => () => geometry.dispose(), [geometry]);
254
+ const sim = useMemo(() => ({ state: createRingSim(), A: new Float32Array(16), B: new Float32Array(16), params: {} }), []);
255
+ useLayoutEffect(() => {
256
+ uniforms.uSpeed.value = speed;
257
+ uniforms.uIntensity.value = intensity;
258
+ uniforms.uOpacity.value = opacity;
259
+ uniforms.uSmokeColor.value.set(smokeColor);
260
+ uniforms.uWobble.value = wobble;
261
+ uniforms.uRingCount.value = ringCount;
262
+ uniforms.uExpansionRadius.value = expansionRadius;
263
+ uniforms.uRiseSpeed.value = riseSpeed;
264
+ uniforms.uIntervalSeconds.value = intervalSeconds;
265
+ uniforms.uThickness.value = thickness;
266
+ }, [uniforms, speed, intensity, opacity, smokeColor, wobble, ringCount, expansionRadius, riseSpeed, intervalSeconds, thickness]);
267
+ useFrame((_, delta) => {
268
+ if (animations?.length)
269
+ applyGenerativeAnimations(materials, animations, animCache.current, Date.now(), false);
270
+ const p = sim.params;
271
+ p.bound = b;
272
+ p.ringCount = uniforms.uRingCount.value;
273
+ p.expansionRadius = uniforms.uExpansionRadius.value;
274
+ p.riseSpeed = uniforms.uRiseSpeed.value;
275
+ p.intervalSeconds = uniforms.uIntervalSeconds.value;
276
+ p.thickness = uniforms.uThickness.value;
277
+ p.leapfrog = leapfrog;
278
+ stepRingSim(sim.state, Math.min(delta, 0.05) * uniforms.uSpeed.value, p, sim.A, sim.B);
279
+ for (let i = 0; i < RING_SLOTS; i++) {
280
+ uniforms.uRingA.value[i].fromArray(sim.A, i * 4);
281
+ uniforms.uRingB.value[i].fromArray(sim.B, i * 4);
157
282
  }
158
283
  });
159
- return (_jsx("group", { ref: rootRef, children: geos.map((geo, i) => (_jsx("points", { geometry: geo, material: mats[i] }, i))) }));
284
+ return (_jsx("group", { ref: rootRef, children: _jsx("points", { geometry: geometry, material: materials[0], frustumCulled: false }) }));
160
285
  };
161
286
  export default SmokeRingEffect;
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import type { AnimationOptions, StrangeAttractorConfig } from '@3driseai/3drise-core';
3
+ import type { ObjectReadyProps } from '../../types/objectReady';
4
+ export interface StrangeAttractorEffectProps extends ObjectReadyProps {
5
+ boundingRadius: number;
6
+ config: StrangeAttractorConfig;
7
+ animations?: AnimationOptions[];
8
+ }
9
+ declare const StrangeAttractorEffect: React.FC<StrangeAttractorEffectProps>;
10
+ export default StrangeAttractorEffect;
11
+ //# sourceMappingURL=StrangeAttractorEffect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StrangeAttractorEffect.d.ts","sourceRoot":"","sources":["../../../src/components/effects/StrangeAttractorEffect.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAGtF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,2BAA4B,SAAQ,gBAAgB;IACjE,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,sBAAsB,CAAC;IAC/B,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACnC;AA0KD,QAAA,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAAE,CAAC,2BAA2B,CA8DjE,CAAC;AACF,eAAe,sBAAsB,CAAC"}