@3driseai/3drise-viewer 0.1.16 → 0.1.18

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 (145) 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/Galaxy.d.ts.map +1 -1
  14. package/dist/components/Galaxy.js +2 -0
  15. package/dist/components/GirihGrid.d.ts +15 -0
  16. package/dist/components/GirihGrid.d.ts.map +1 -0
  17. package/dist/components/GirihGrid.js +62 -0
  18. package/dist/components/GravWaveGrid.d.ts +15 -0
  19. package/dist/components/GravWaveGrid.d.ts.map +1 -0
  20. package/dist/components/GravWaveGrid.js +62 -0
  21. package/dist/components/GridsGenerator.d.ts +2 -2
  22. package/dist/components/GridsGenerator.d.ts.map +1 -1
  23. package/dist/components/GridsGenerator.js +21 -0
  24. package/dist/components/HyperbolicGrid.d.ts +15 -0
  25. package/dist/components/HyperbolicGrid.d.ts.map +1 -0
  26. package/dist/components/HyperbolicGrid.js +61 -0
  27. package/dist/components/LayoutInstances.d.ts +13 -0
  28. package/dist/components/LayoutInstances.d.ts.map +1 -0
  29. package/dist/components/LayoutInstances.js +39 -0
  30. package/dist/components/ModelPrimitive.d.ts +2 -0
  31. package/dist/components/ModelPrimitive.d.ts.map +1 -1
  32. package/dist/components/ModelPrimitive.js +3 -3
  33. package/dist/components/ObjectNode.d.ts.map +1 -1
  34. package/dist/components/ObjectNode.js +44 -9
  35. package/dist/components/ParticlesGenerator.d.ts +10 -0
  36. package/dist/components/ParticlesGenerator.d.ts.map +1 -0
  37. package/dist/components/ParticlesGenerator.js +23 -0
  38. package/dist/components/ReactionDiffusionGrid.d.ts +15 -0
  39. package/dist/components/ReactionDiffusionGrid.d.ts.map +1 -0
  40. package/dist/components/ReactionDiffusionGrid.js +376 -0
  41. package/dist/components/WallpaperGrid.d.ts +15 -0
  42. package/dist/components/WallpaperGrid.d.ts.map +1 -0
  43. package/dist/components/WallpaperGrid.js +555 -0
  44. package/dist/components/cursor/CursorController.d.ts.map +1 -1
  45. package/dist/components/effects/BlackHoleEffect.d.ts +3 -3
  46. package/dist/components/effects/BlackHoleEffect.d.ts.map +1 -1
  47. package/dist/components/effects/BlackHoleEffect.js +293 -163
  48. package/dist/components/effects/DandelionSeedsEffect.d.ts +13 -0
  49. package/dist/components/effects/DandelionSeedsEffect.d.ts.map +1 -0
  50. package/dist/components/effects/DandelionSeedsEffect.js +510 -0
  51. package/dist/components/effects/FallingLeavesEffect.d.ts +13 -0
  52. package/dist/components/effects/FallingLeavesEffect.d.ts.map +1 -0
  53. package/dist/components/effects/FallingLeavesEffect.js +446 -0
  54. package/dist/components/effects/FieldLinesEffect.d.ts +11 -0
  55. package/dist/components/effects/FieldLinesEffect.d.ts.map +1 -0
  56. package/dist/components/effects/FieldLinesEffect.js +260 -0
  57. package/dist/components/effects/FireflySwarmEffect.d.ts +13 -0
  58. package/dist/components/effects/FireflySwarmEffect.d.ts.map +1 -0
  59. package/dist/components/effects/FireflySwarmEffect.js +408 -0
  60. package/dist/components/effects/GalaxyEffect.d.ts +13 -0
  61. package/dist/components/effects/GalaxyEffect.d.ts.map +1 -0
  62. package/dist/components/effects/GalaxyEffect.js +374 -0
  63. package/dist/components/effects/HarmonicShellEffect.d.ts +11 -0
  64. package/dist/components/effects/HarmonicShellEffect.d.ts.map +1 -0
  65. package/dist/components/effects/HarmonicShellEffect.js +209 -0
  66. package/dist/components/effects/IceCrystalsEffect.d.ts +3 -3
  67. package/dist/components/effects/IceCrystalsEffect.d.ts.map +1 -1
  68. package/dist/components/effects/IceCrystalsEffect.js +286 -166
  69. package/dist/components/effects/IceHaloEffect.d.ts +13 -0
  70. package/dist/components/effects/IceHaloEffect.d.ts.map +1 -0
  71. package/dist/components/effects/IceHaloEffect.js +306 -0
  72. package/dist/components/effects/LichtenbergEffect.d.ts +13 -0
  73. package/dist/components/effects/LichtenbergEffect.d.ts.map +1 -0
  74. package/dist/components/effects/LichtenbergEffect.js +517 -0
  75. package/dist/components/effects/MoleculesEffect.d.ts +3 -3
  76. package/dist/components/effects/MoleculesEffect.d.ts.map +1 -1
  77. package/dist/components/effects/MoleculesEffect.js +322 -164
  78. package/dist/components/effects/MurmurationEffect.d.ts +13 -0
  79. package/dist/components/effects/MurmurationEffect.d.ts.map +1 -0
  80. package/dist/components/effects/MurmurationEffect.js +539 -0
  81. package/dist/components/effects/PlanetaryRingsEffect.d.ts +13 -0
  82. package/dist/components/effects/PlanetaryRingsEffect.d.ts.map +1 -0
  83. package/dist/components/effects/PlanetaryRingsEffect.js +458 -0
  84. package/dist/components/effects/PortalEffect.d.ts +3 -3
  85. package/dist/components/effects/PortalEffect.d.ts.map +1 -1
  86. package/dist/components/effects/PortalEffect.js +240 -163
  87. package/dist/components/effects/ShockwaveEffect.d.ts +5 -3
  88. package/dist/components/effects/ShockwaveEffect.d.ts.map +1 -1
  89. package/dist/components/effects/ShockwaveEffect.js +538 -142
  90. package/dist/components/effects/SmokePlumeEffect.d.ts +3 -3
  91. package/dist/components/effects/SmokePlumeEffect.d.ts.map +1 -1
  92. package/dist/components/effects/SmokePlumeEffect.js +180 -116
  93. package/dist/components/effects/SmokeRingEffect.d.ts +3 -3
  94. package/dist/components/effects/SmokeRingEffect.d.ts.map +1 -1
  95. package/dist/components/effects/SmokeRingEffect.js +260 -135
  96. package/dist/components/effects/StrangeAttractorEffect.d.ts +11 -0
  97. package/dist/components/effects/StrangeAttractorEffect.d.ts.map +1 -0
  98. package/dist/components/effects/StrangeAttractorEffect.js +229 -0
  99. package/dist/components/effects/VolumetricFogEffect.d.ts +5 -3
  100. package/dist/components/effects/VolumetricFogEffect.d.ts.map +1 -1
  101. package/dist/components/effects/VolumetricFogEffect.js +211 -93
  102. package/dist/components/effects/WaterCausticsEffect.d.ts +13 -0
  103. package/dist/components/effects/WaterCausticsEffect.d.ts.map +1 -0
  104. package/dist/components/effects/WaterCausticsEffect.js +308 -0
  105. package/dist/components/effects/index.d.ts +9 -0
  106. package/dist/components/effects/index.d.ts.map +1 -1
  107. package/dist/components/effects/index.js +9 -0
  108. package/dist/components/index.d.ts +19 -0
  109. package/dist/components/index.d.ts.map +1 -1
  110. package/dist/components/index.js +10 -0
  111. package/dist/components/particles/BioluminescentWakeParticles.d.ts +12 -0
  112. package/dist/components/particles/BioluminescentWakeParticles.d.ts.map +1 -0
  113. package/dist/components/particles/BioluminescentWakeParticles.js +1123 -0
  114. package/dist/components/particles/BrownianDustParticles.d.ts +12 -0
  115. package/dist/components/particles/BrownianDustParticles.d.ts.map +1 -0
  116. package/dist/components/particles/BrownianDustParticles.js +757 -0
  117. package/dist/components/particles/ElectronOrbitalsParticles.d.ts +12 -0
  118. package/dist/components/particles/ElectronOrbitalsParticles.d.ts.map +1 -0
  119. package/dist/components/particles/ElectronOrbitalsParticles.js +944 -0
  120. package/dist/components/particles/GrinderSparksParticles.d.ts +12 -0
  121. package/dist/components/particles/GrinderSparksParticles.d.ts.map +1 -0
  122. package/dist/components/particles/GrinderSparksParticles.js +744 -0
  123. package/dist/components/particles/TidalStreamParticles.d.ts +12 -0
  124. package/dist/components/particles/TidalStreamParticles.d.ts.map +1 -0
  125. package/dist/components/particles/TidalStreamParticles.js +788 -0
  126. package/dist/components/particles/index.d.ts +11 -0
  127. package/dist/components/particles/index.d.ts.map +1 -0
  128. package/dist/components/particles/index.js +6 -0
  129. package/dist/constants.d.ts +1 -1
  130. package/dist/constants.d.ts.map +1 -1
  131. package/dist/constants.js +1 -1
  132. package/dist/hooks/useModelObject.d.ts +6 -1
  133. package/dist/hooks/useModelObject.d.ts.map +1 -1
  134. package/dist/hooks/useModelObject.js +19 -6
  135. package/dist/types/gridConfigs.d.ts +98 -0
  136. package/dist/types/gridConfigs.d.ts.map +1 -1
  137. package/dist/types/index.d.ts +1 -1
  138. package/dist/types/index.d.ts.map +1 -1
  139. package/dist/utils/particleTrails.d.ts +70 -0
  140. package/dist/utils/particleTrails.d.ts.map +1 -0
  141. package/dist/utils/particleTrails.js +369 -0
  142. package/dist/utils/pointerField.d.ts +28 -0
  143. package/dist/utils/pointerField.d.ts.map +1 -0
  144. package/dist/utils/pointerField.js +103 -0
  145. package/package.json +2 -2
@@ -0,0 +1,510 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useLayoutEffect, useMemo, useRef } from 'react';
3
+ import * as THREE from 'three';
4
+ import { useFrame } from '@react-three/fiber';
5
+ import { applyGenerativeAnimations } from '../../utils/handleGenerativeEffectsAnimations.js';
6
+ import { useObjectReady } from '../../hooks/useObjectReady.js';
7
+ /**
8
+ * Dandelion seeds let go of the head one by one and drift away: a high-drag pappus gives them a slow terminal fall, and curl-noise wind with updrafts carries them. Use for meadow, summer, wish, gentle scenes.
9
+ *
10
+ * dv/dt = (u_wind + u_turb + lift·ŷ − v_settle·ŷ − v)/τ · u = ∇×A (divergence-free) · potential flow around the head · pappus aligns with −v_rel
11
+ */
12
+ // ---------------------------------------------------------------------------
13
+ // A dandelion seed is a tiny achene under a pappus - an umbrella of ~100 fine
14
+ // filaments. The pappus has an enormous drag-to-mass ratio (it even holds a
15
+ // separated vortex ring above itself), so the seed reaches its terminal speed
16
+ // in a fraction of a second and then simply goes where the air goes:
17
+ // dv/dt = (u_air - v_settle * yhat - v) / tau, tau ~ 0.3 s
18
+ // The air is a mean wind plus a divergence-free swirl (the analytic curl of a
19
+ // trig vector potential, so gusts rotate and never compress) plus a slow
20
+ // updraft. Around the head the mean wind follows the potential flow past a
21
+ // sphere, u = U(1 + a^3/2r^3) - 3a^3/2r^5 (U.r) r, which is exactly tangent at
22
+ // the surface - so seeds slide around the head instead of into it.
23
+ // Drag also aligns the pappus with the relative airflow, so each seed hangs
24
+ // its achene downwind-down and tips as gusts hit it. Seeds are pulled loose
25
+ // one at a time (sooner in stronger wind), fade out far downwind, and are
26
+ // replaced on the same spot on the head.
27
+ // ---------------------------------------------------------------------------
28
+ const vert = /* glsl */ `
29
+ // position = (s along the part, side of the ribbon, filament angle)
30
+ attribute float aPart; // 0 pappus filament, 1 beak, 2 achene
31
+ attribute vec4 aSeedA; // hub position xyz, alpha w
32
+ attribute vec4 aSeedB; // pappus axis xyz, roll w
33
+ attribute vec2 aVar; // per-seed size factor, per-seed shape hash
34
+ uniform float uSeedSize;
35
+ uniform float uBound;
36
+ uniform float uViewH;
37
+ varying float vAcross;
38
+ varying float vFade;
39
+ varying float vPart;
40
+ varying float vAlpha;
41
+
42
+ void main(){
43
+ vec3 C = aSeedA.xyz;
44
+ vec3 A = normalize(aSeedB.xyz + vec3(0.0, 1e-5, 0.0));
45
+ vec3 ref = abs(A.y) < 0.9 ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);
46
+ vec3 e1 = normalize(cross(A, ref));
47
+ vec3 e2 = cross(A, e1);
48
+ float phi = position.z + aSeedB.w;
49
+ vec3 d = cos(phi) * e1 + sin(phi) * e2;
50
+
51
+ float R = 0.16 * uSeedSize * uBound * aVar.x; // pappus radius
52
+ float s = position.x;
53
+ vec3 P;
54
+ vec3 T;
55
+ float hw;
56
+ float fade;
57
+ if (aPart < 0.5){
58
+ // one bristle: a nearly straight ray from the hub on a cone of half-angle
59
+ // th, with a little gravity sag, so the pappus reads as a starburst whose
60
+ // spread of angles gives the shallow dome
61
+ float h = fract(sin((position.z + aVar.y * 7.3) * 31.7182) * 43758.5453);
62
+ float th = 0.72 + 0.62 * h;
63
+ float len = R / max(sin(th), 0.45) * (0.82 + 0.3 * h);
64
+ float sag = 0.14 * s * s;
65
+ P = C + len * (s * sin(th) * d + (s * cos(th) - sag) * A);
66
+ T = normalize(sin(th) * d + (cos(th) - 0.28 * s) * A);
67
+ hw = R * (0.013 + 0.013 * s); // feathery toward the tip
68
+ fade = (1.0 - 0.35 * s) * (1.0 - smoothstep(0.7, 1.0, s) * 0.8);
69
+ } else if (aPart < 1.5){
70
+ // the beak: a thin stalk hanging from the hub down the axis
71
+ P = C - A * (1.45 * R * s);
72
+ T = A;
73
+ hw = R * 0.011;
74
+ fade = 0.55;
75
+ } else {
76
+ // the achene: a small spindle at the bottom of the beak
77
+ float t0 = 1.45 * R;
78
+ P = C - A * (t0 + 0.55 * R * s);
79
+ T = A;
80
+ hw = R * 0.05 * sin(3.14159 * (0.12 + 0.82 * s));
81
+ fade = 1.0;
82
+ }
83
+
84
+ vec4 mv = modelViewMatrix * vec4(P, 1.0);
85
+ float pxPerUnit = projectionMatrix[1][1] * uViewH / max(-mv.z, 0.05);
86
+ float minHW = 0.8 / max(pxPerUnit, 1e-4); // keep every ribbon ~1.6 px wide
87
+ float w = max(hw, minHW);
88
+ // give back in alpha what a widened ribbon gains in area
89
+ vAlpha = aSeedA.w * clamp(hw / max(minHW, 1e-6), 0.4, 1.0);
90
+ vec3 vd = normalize(cameraPosition - P);
91
+ vec3 off = cross(T, vd);
92
+ float ol = length(off);
93
+ off = ol > 1e-5 ? off / ol : e1;
94
+ P += off * (position.y * w);
95
+
96
+ vAcross = position.y;
97
+ vFade = fade;
98
+ vPart = aPart;
99
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(P, 1.0);
100
+ }
101
+ `;
102
+ const frag = /* glsl */ `
103
+ uniform vec3 uColor;
104
+ uniform vec3 uStemColor;
105
+ uniform float uIntensity;
106
+ uniform float uOpacity;
107
+ varying float vAcross;
108
+ varying float vFade;
109
+ varying float vPart;
110
+ varying float vAlpha;
111
+
112
+ void main(){
113
+ // soft across the ribbon: a parabolic profile, so no hard edges anywhere
114
+ float a = vAlpha * vFade * (1.0 - vAcross * vAcross) * uOpacity;
115
+ if (a < 0.003) discard;
116
+ vec3 col = vPart < 0.5 ? uColor : uStemColor * (vPart > 1.5 ? 1.0 : 0.62);
117
+ gl_FragColor = vec4(col * uIntensity, a * (vPart < 0.5 ? 0.45 : 0.9));
118
+ }
119
+ `;
120
+ function mulberry32(seed) {
121
+ return () => {
122
+ seed |= 0;
123
+ seed = (seed + 0x6d2b79f5) | 0;
124
+ let r = Math.imul(seed ^ (seed >>> 15), 1 | seed);
125
+ r = (r + Math.imul(r ^ (r >>> 7), 61 | r)) ^ r;
126
+ return ((r ^ (r >>> 14)) >>> 0) / 4294967296;
127
+ };
128
+ }
129
+ /** deterministic 0..1 value for seed i on its cycle c */
130
+ function hash2(i, c) {
131
+ let h = Math.imul(i + 1, 374761393) ^ Math.imul(c + 1, 668265263);
132
+ h = Math.imul(h ^ (h >>> 13), 1274126177);
133
+ return ((h ^ (h >>> 16)) >>> 0) / 4294967296;
134
+ }
135
+ /** the ribbon template: 32 pappus filaments, the beak and the achene */
136
+ function buildTemplate() {
137
+ const F = 32;
138
+ const NODE = 4;
139
+ const data = [];
140
+ const part = [];
141
+ const index = [];
142
+ let base = 0;
143
+ const strip = (nodes, phi, p) => {
144
+ for (let k = 0; k < nodes; k++) {
145
+ const s = k / (nodes - 1);
146
+ data.push(s, -1, phi);
147
+ part.push(p);
148
+ data.push(s, 1, phi);
149
+ part.push(p);
150
+ }
151
+ for (let k = 0; k < nodes - 1; k++) {
152
+ const a = base + k * 2;
153
+ index.push(a, a + 1, a + 2, a + 1, a + 3, a + 2);
154
+ }
155
+ base += nodes * 2;
156
+ };
157
+ for (let f = 0; f < F; f++) {
158
+ const j = (((f * 2654435761) >>> 0) / 4294967296 - 0.5) * 0.55;
159
+ strip(NODE, ((f + j) / F) * Math.PI * 2, 0);
160
+ }
161
+ strip(3, 0, 1);
162
+ strip(4, 0, 2);
163
+ const geo = new THREE.InstancedBufferGeometry();
164
+ geo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(data), 3));
165
+ geo.setAttribute('aPart', new THREE.BufferAttribute(new Float32Array(part), 1));
166
+ geo.setIndex(index);
167
+ return geo;
168
+ }
169
+ function createUniforms() {
170
+ return {
171
+ uTime: { value: 0 },
172
+ uSpeed: { value: 1 },
173
+ uIntensity: { value: 1 },
174
+ uOpacity: { value: 1 },
175
+ uColor: { value: new THREE.Color() },
176
+ uStemColor: { value: new THREE.Color() },
177
+ uWindSpeed: { value: 0.55 },
178
+ uWindDirection: { value: 20 },
179
+ uTurbulence: { value: 0.8 },
180
+ uSeedSize: { value: 1 },
181
+ uLift: { value: 1 },
182
+ uBound: { value: 1 },
183
+ uViewH: { value: 800 },
184
+ };
185
+ }
186
+ function applyConfig(u, c, bound, env) {
187
+ u.uSpeed.value = c.speed;
188
+ u.uIntensity.value = c.intensity;
189
+ u.uOpacity.value = c.opacity;
190
+ u.uColor.value.set(c.color);
191
+ u.uStemColor.value.set(c.stemColor);
192
+ u.uWindSpeed.value = c.windSpeed;
193
+ u.uWindDirection.value = c.windDirection;
194
+ u.uTurbulence.value = c.turbulence;
195
+ u.uSeedSize.value = c.seedSize;
196
+ u.uLift.value = c.lift;
197
+ u.uBound.value = bound;
198
+ }
199
+ function build(u, c, bound, env) {
200
+ const n = Math.round(Math.min(Math.max(c.seedCount, 10), 260));
201
+ const hasHost = !!(env && env.hasHost);
202
+ const b = bound;
203
+ const SIM_DT = 1 / 30;
204
+ const TAU_V = 0.32; // velocity relaxation time of a pappus, seconds
205
+ const SETTLE = 0.12; // terminal settling speed, in bound units per second
206
+ const FADE_R = 6.5; // seeds fade out beyond this radius
207
+ const LIFE = 22; // ... or after this long in the air
208
+ const rand = mulberry32(90210);
209
+ const slot = new Float32Array(n * 3); // attachment direction on the head
210
+ const pos = new Float32Array(n * 3);
211
+ const vel = new Float32Array(n * 3);
212
+ const axis = new Float32Array(n * 3);
213
+ const timer = new Float32Array(n);
214
+ const wait = new Float32Array(n);
215
+ const cycle = new Int32Array(n);
216
+ const mode = new Int32Array(n); // 0 attached, 1 flying, 2 fading
217
+ const roll = new Float32Array(n);
218
+ const spin = new Float32Array(n);
219
+ const varr = new Float32Array(n * 2); // size factor, shape hash
220
+ const settleF = new Float32Array(n); // per-seed terminal fall speed
221
+ const tilt = new Float32Array(n * 3); // small permanent tilt of the pappus
222
+ const seedA = new Float32Array(n * 4);
223
+ const seedB = new Float32Array(n * 4);
224
+ const air = new Float32Array(3);
225
+ // attachment slots: a Fibonacci spiral over the upper hemisphere of the head
226
+ const GOLD = Math.PI * (3 - Math.sqrt(5));
227
+ for (let i = 0; i < n; i++) {
228
+ const t = (i + 0.5) / n;
229
+ const y = -0.12 + 1.1 * t; // upper hemisphere, a little below the equator
230
+ const r = Math.sqrt(Math.max(1 - y * y, 0.02));
231
+ const a = i * GOLD;
232
+ slot[i * 3] = r * Math.cos(a);
233
+ slot[i * 3 + 1] = y;
234
+ slot[i * 3 + 2] = r * Math.sin(a);
235
+ axis[i * 3] = slot[i * 3];
236
+ axis[i * 3 + 1] = slot[i * 3 + 1];
237
+ axis[i * 3 + 2] = slot[i * 3 + 2];
238
+ cycle[i] = 0;
239
+ mode[i] = 0;
240
+ // staggered: every seed is already part way through its wait
241
+ wait[i] = 4 + 11 * hash2(i, 0);
242
+ timer[i] = wait[i] * rand();
243
+ roll[i] = rand() * Math.PI * 2;
244
+ varr[i * 2] = 0.78 + 0.44 * rand();
245
+ varr[i * 2 + 1] = rand();
246
+ // real pappi differ in drag, so seeds sink at slightly different rates
247
+ settleF[i] = SETTLE * b * (0.72 + 0.6 * rand());
248
+ // and none is perfectly symmetric: each flies with a small fixed lean
249
+ const ta = rand() * Math.PI * 2, tm = 0.07 + 0.2 * rand();
250
+ tilt[i * 3] = Math.cos(ta) * tm;
251
+ tilt[i * 3 + 1] = 0;
252
+ tilt[i * 3 + 2] = Math.sin(ta) * tm;
253
+ spin[i] = (0.1 + 0.25 * rand()) * (rand() < 0.5 ? -1 : 1);
254
+ seedA[i * 4 + 3] = 0;
255
+ }
256
+ const geo = buildTemplate();
257
+ const aAttr = new THREE.InstancedBufferAttribute(seedA, 4);
258
+ const bAttr = new THREE.InstancedBufferAttribute(seedB, 4);
259
+ aAttr.setUsage(THREE.DynamicDrawUsage);
260
+ bAttr.setUsage(THREE.DynamicDrawUsage);
261
+ geo.setAttribute('aSeedA', aAttr);
262
+ geo.setAttribute('aSeedB', bAttr);
263
+ geo.setAttribute('aVar', new THREE.InstancedBufferAttribute(varr, 2));
264
+ geo.instanceCount = n;
265
+ const mat = new THREE.ShaderMaterial({
266
+ vertexShader: vert, fragmentShader: frag, uniforms: u,
267
+ transparent: true, depthWrite: false, blending: THREE.AdditiveBlending,
268
+ side: THREE.DoubleSide,
269
+ });
270
+ const mesh = new THREE.Mesh(geo, mat);
271
+ mesh.frustumCulled = false;
272
+ const vp = new THREE.Vector2();
273
+ mesh.onBeforeRender = (renderer) => {
274
+ renderer.getDrawingBufferSize(vp);
275
+ u.uViewH.value = vp.y * 0.5;
276
+ };
277
+ const group = new THREE.Group();
278
+ group.add(mesh);
279
+ let simTime = 0;
280
+ let acc = 0;
281
+ /** air velocity at (x,y,z): mean wind around the head + curl gusts + updraft */
282
+ function airAt(x, y, z, t) {
283
+ const ws = Math.max(u.uWindSpeed.value, 0) * b;
284
+ const ang = (u.uWindDirection.value * Math.PI) / 180;
285
+ let wx = Math.cos(ang) * ws;
286
+ let wz = Math.sin(ang) * ws;
287
+ let wy = 0;
288
+ if (hasHost) {
289
+ // potential flow past a sphere: tangent at the surface, uniform far away
290
+ const aH = 1.12 * b;
291
+ const r2 = x * x + y * y + z * z;
292
+ const r = Math.sqrt(r2) + 1e-5;
293
+ if (r < 8 * aH) {
294
+ const rr = Math.max(r, aH);
295
+ const k = (aH * aH * aH) / (2 * rr * rr * rr);
296
+ const dotp = (wx * x + wz * z) / (rr * rr);
297
+ const px = wx * (1 + k) - 3 * k * dotp * x;
298
+ const py = -3 * k * dotp * y;
299
+ const pz = wz * (1 + k) - 3 * k * dotp * z;
300
+ wx = px;
301
+ wy = py;
302
+ wz = pz;
303
+ }
304
+ }
305
+ // Divergence-free gusts: the analytic curl of a trig vector potential,
306
+ // sampled in coordinates carried along by the mean wind (Taylor's frozen
307
+ // turbulence). Advecting the pattern matters: a gust field pinned to
308
+ // space would have fixed stagnation points that collect seeds.
309
+ const ax0 = x - Math.cos(ang) * ws * t;
310
+ const az0 = z - Math.sin(ang) * ws * t;
311
+ const g = Math.max(u.uTurbulence.value, 0) * (0.4 * ws + 0.05 * b);
312
+ const k1 = 0.62 / b;
313
+ const p1 = 0.21 * t, p2 = 0.17 * t + 2.1, p3 = 0.13 * t + 4.7;
314
+ wx += g * (Math.sin(k1 * az0 + p1) + 0.6 * Math.cos(k1 * y * 1.3 + p3));
315
+ wy += g * (0.7 * Math.sin(k1 * ax0 + p2) + 0.7 * Math.cos(k1 * az0 * 1.2 + p1));
316
+ wz += g * (0.6 * Math.sin(k1 * y + p3) + Math.cos(k1 * ax0 * 1.1 + p2));
317
+ // slow, wide thermals, also carried along with the wind
318
+ const lift = Math.max(u.uLift.value, 0) * SETTLE * b;
319
+ wy += lift * (1.0 + 0.9 * Math.sin(0.42 * ax0 / b + 0.15 * t) * Math.cos(0.36 * az0 / b - 0.11 * t));
320
+ air[0] = wx;
321
+ air[1] = wy;
322
+ air[2] = wz;
323
+ }
324
+ function hubRadius() {
325
+ return b + 1.45 * 0.16 * Math.max(u.uSeedSize.value, 0.1) * b;
326
+ }
327
+ function respawn(i) {
328
+ cycle[i]++;
329
+ mode[i] = 0;
330
+ timer[i] = 0;
331
+ wait[i] = 4 + 11 * hash2(i, cycle[i]);
332
+ roll[i] = hash2(i, cycle[i] + 7919) * Math.PI * 2;
333
+ }
334
+ function step(h) {
335
+ const t = simTime;
336
+ const hubR = hubRadius();
337
+ const ws = Math.max(u.uWindSpeed.value, 0);
338
+ const fadeR = FADE_R * b;
339
+ for (let i = 0; i < n; i++) {
340
+ const i3 = i * 3, i4 = i * 4;
341
+ roll[i] += h * spin[i];
342
+ timer[i] += h;
343
+ if (mode[i] === 0) {
344
+ // still on the head: sit on the slot, fade in, and wait for a gust
345
+ const sx = slot[i3], sy = slot[i3 + 1], sz = slot[i3 + 2];
346
+ pos[i3] = sx * hubR;
347
+ pos[i3 + 1] = sy * hubR;
348
+ pos[i3 + 2] = sz * hubR;
349
+ axis[i3] = sx;
350
+ axis[i3 + 1] = sy;
351
+ axis[i3 + 2] = sz;
352
+ seedA[i4 + 3] = Math.min(timer[i] / 1.6, 1);
353
+ // stronger wind pulls seeds loose sooner
354
+ if (timer[i] > wait[i] * (0.45 / Math.max(ws, 0.06))) {
355
+ mode[i] = 1;
356
+ timer[i] = 0;
357
+ airAt(pos[i3], pos[i3 + 1], pos[i3 + 2], t);
358
+ vel[i3] = air[0] * 0.2;
359
+ vel[i3 + 1] = air[1] * 0.2;
360
+ vel[i3 + 2] = air[2] * 0.2;
361
+ }
362
+ continue;
363
+ }
364
+ let x = pos[i3], y = pos[i3 + 1], z = pos[i3 + 2];
365
+ airAt(x, y, z, t);
366
+ // relax toward the air's velocity minus the slow terminal fall
367
+ const tx = air[0], ty = air[1] - settleF[i], tz = air[2];
368
+ const k = Math.min(h / TAU_V, 1);
369
+ let vx = vel[i3] + (tx - vel[i3]) * k;
370
+ let vy = vel[i3 + 1] + (ty - vel[i3 + 1]) * k;
371
+ let vz = vel[i3 + 2] + (tz - vel[i3 + 2]) * k;
372
+ x += vx * h;
373
+ y += vy * h;
374
+ z += vz * h;
375
+ // the head is solid: slide around it
376
+ if (hasHost) {
377
+ const d = Math.sqrt(x * x + y * y + z * z);
378
+ const lim = hubR * 0.99;
379
+ if (d < lim && d > 1e-5) {
380
+ const f = lim / d;
381
+ x *= f;
382
+ y *= f;
383
+ z *= f;
384
+ const vr = (vx * x + vy * y + vz * z) / (lim * lim);
385
+ vx -= vr * x;
386
+ vy -= vr * y;
387
+ vz -= vr * z;
388
+ }
389
+ }
390
+ vel[i3] = vx;
391
+ vel[i3 + 1] = vy;
392
+ vel[i3 + 2] = vz;
393
+ pos[i3] = x;
394
+ pos[i3 + 1] = y;
395
+ pos[i3 + 2] = z;
396
+ // drag turns the pappus into the relative airflow (achene downwind-down)
397
+ const rx = air[0] - vx, ry = air[1] - vy, rz = air[2] - vz;
398
+ let ax = rx + tilt[i3] * settleF[i] * 4, ay = ry + settleF[i] * 0.6, az = rz + tilt[i3 + 2] * settleF[i] * 4;
399
+ const am = Math.sqrt(ax * ax + ay * ay + az * az);
400
+ if (am < 1e-4) {
401
+ ax = 0;
402
+ ay = 1;
403
+ az = 0;
404
+ }
405
+ else {
406
+ ax /= am;
407
+ ay /= am;
408
+ az /= am;
409
+ }
410
+ const ka = Math.min(h / 0.35, 1);
411
+ let nx = axis[i3] + (ax - axis[i3]) * ka;
412
+ let ny = axis[i3 + 1] + (ay - axis[i3 + 1]) * ka;
413
+ let nz = axis[i3 + 2] + (az - axis[i3 + 2]) * ka;
414
+ const nm = Math.sqrt(nx * nx + ny * ny + nz * nz) + 1e-6;
415
+ axis[i3] = nx / nm;
416
+ axis[i3 + 1] = ny / nm;
417
+ axis[i3 + 2] = nz / nm;
418
+ const dist = Math.sqrt(x * x + y * y + z * z);
419
+ if (mode[i] === 1) {
420
+ seedA[i4 + 3] = 1;
421
+ if (dist > fadeR || timer[i] > LIFE) {
422
+ mode[i] = 2;
423
+ timer[i] = 0;
424
+ }
425
+ }
426
+ else {
427
+ const f = 1 - timer[i] / 1.4;
428
+ seedA[i4 + 3] = Math.max(f, 0);
429
+ if (f <= 0)
430
+ respawn(i);
431
+ }
432
+ }
433
+ }
434
+ function writeInstances() {
435
+ for (let i = 0; i < n; i++) {
436
+ const i3 = i * 3, i4 = i * 4;
437
+ seedA[i4] = pos[i3];
438
+ seedA[i4 + 1] = pos[i3 + 1];
439
+ seedA[i4 + 2] = pos[i3 + 2];
440
+ seedB[i4] = axis[i3];
441
+ seedB[i4 + 1] = axis[i3 + 1];
442
+ seedB[i4 + 2] = axis[i3 + 2];
443
+ seedB[i4 + 3] = roll[i];
444
+ }
445
+ aAttr.needsUpdate = true;
446
+ bAttr.needsUpdate = true;
447
+ }
448
+ step(0);
449
+ writeInstances();
450
+ return {
451
+ object: group,
452
+ materials: [mat],
453
+ apply(c2) { },
454
+ update(dt, camera) {
455
+ const h = Math.min(Math.max(dt, 0), 0.1) * Math.max(u.uSpeed.value, 0);
456
+ u.uTime.value += h;
457
+ acc += h;
458
+ let guard = 0;
459
+ while (acc >= SIM_DT && guard < 6) {
460
+ step(SIM_DT);
461
+ simTime += SIM_DT;
462
+ acc -= SIM_DT;
463
+ guard++;
464
+ }
465
+ if (guard >= 6)
466
+ acc = 0;
467
+ writeInstances();
468
+ },
469
+ dispose() { geo.dispose(); mat.dispose(); },
470
+ };
471
+ }
472
+ /** Defaults for every knob, so an older saved config still renders. */
473
+ const DEFAULTS = {
474
+ intensity: 1,
475
+ speed: 1,
476
+ color: '#ffeccf',
477
+ opacity: 1,
478
+ seedCount: 90,
479
+ windSpeed: 0.55,
480
+ windDirection: 20,
481
+ turbulence: 0.8,
482
+ seedSize: 1,
483
+ lift: 1,
484
+ stemColor: '#d8b183',
485
+ };
486
+ const DandelionSeedsEffect = ({ boundingRadius, config, animations, hasHost = false, onObjectReady, onObjectDispose }) => {
487
+ const rootRef = useRef(null);
488
+ const animCache = useRef({});
489
+ useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
490
+ // a standalone effect gets boundingRadius 1
491
+ const b = boundingRadius > 0 ? boundingRadius : 1;
492
+ const cfg = useMemo(() => ({ ...DEFAULTS, ...config }), [config]);
493
+ const env = useMemo(() => ({ hasHost: hasHost }), [hasHost]);
494
+ // uniforms outlive the objects, so keyframe animations survive a rebuild
495
+ const uniforms = useMemo(() => createUniforms(), []);
496
+ // rebuilt only when the host size or a structural knob changes
497
+ const instance = useMemo(() => build(uniforms, { ...DEFAULTS, ...config }, b, env), [uniforms, b, env, cfg.seedCount]);
498
+ useEffect(() => () => instance.dispose(), [instance]);
499
+ useLayoutEffect(() => {
500
+ applyConfig(uniforms, cfg, b, env);
501
+ instance.apply(cfg);
502
+ }, [instance, uniforms, cfg, b, env]);
503
+ useFrame((state, delta) => {
504
+ instance.update(Math.min(delta, 0.1), state.camera);
505
+ if (animations?.length)
506
+ applyGenerativeAnimations(instance.materials, animations, animCache.current, Date.now(), false);
507
+ });
508
+ return (_jsx("group", { ref: rootRef, children: _jsx("primitive", { object: instance.object }) }));
509
+ };
510
+ export default DandelionSeedsEffect;
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import type { AnimationOptions, FallingLeavesConfig } from '@3driseai/3drise-core';
3
+ import type { ObjectReadyProps } from '../../types/objectReady';
4
+ export interface FallingLeavesEffectProps extends ObjectReadyProps {
5
+ boundingRadius: number;
6
+ config: FallingLeavesConfig;
7
+ animations?: AnimationOptions[];
8
+ /** True when the effect is attached to an object, so it can react to the host. */
9
+ hasHost?: boolean;
10
+ }
11
+ declare const FallingLeavesEffect: React.FC<FallingLeavesEffectProps>;
12
+ export default FallingLeavesEffect;
13
+ //# sourceMappingURL=FallingLeavesEffect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FallingLeavesEffect.d.ts","sourceRoot":"","sources":["../../../src/components/effects/FallingLeavesEffect.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsD,MAAM,OAAO,CAAC;AAG3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAGnF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,wBAAyB,SAAQ,gBAAgB;IAC9D,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAChC,kFAAkF;IAClF,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAwZD,QAAA,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CAAC,wBAAwB,CA+B3D,CAAC;AAEF,eAAe,mBAAmB,CAAC"}