@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,446 @@
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
+ * Leaves drifting past the object, each one integrating the Andersen-Pesavento-Wang falling-plate equations, so they flutter, glide and tumble like real leaves.
9
+ *
10
+ * (m + m₁₁)u̇ = (m + m₂₂)ωv − ρΓv − m′g sinθ − F_u · Γ = −½C_T l uv/|V| + ½C_R l²ω · (I + I_a)ω̇ = (m₁₁ − m₂₂)uv − τ
11
+ */
12
+ // ---------------------------------------------------------------------------
13
+ // Andersen, Pesavento & Wang (JFM 2005): the quasi-steady equations of a rigid
14
+ // plate falling in a vertical plane. In the plate frame (u along the chord, v
15
+ // normal, omega the spin, theta the chord angle):
16
+ // (m + m11) u' = (m + m22) omega v - Gamma v - m' g sin(theta) - F_u
17
+ // (m + m22) v' = -(m + m11) omega u + Gamma u - m' g cos(theta) - F_v
18
+ // (I + Ia) omega' = (m11 - m22) u v - tau
19
+ // m11, m22 and Ia are the added mass and added moment of the surrounding fluid
20
+ // (that anisotropy, m11 != m22, is what makes the plate rotate at all);
21
+ // Gamma = -C_T l uv/|V| + C_R l^2 omega / 2 is the quasi-steady circulation, so
22
+ // the Kutta-Joukowski force rho Gamma x V is the lift; F = (A - B cos 2alpha)|V|(u,v)/2
23
+ // is the drag with its angle-of-attack dependence, and tau the dissipative torque.
24
+ // The dimensionless inertia I* decides the regime: light plates flutter
25
+ // (side-to-side glides with periodic reversals), heavy ones tumble (continuous
26
+ // rotation with a steady sideways drift). Both parameter sets below were checked
27
+ // numerically; the tumble knob is the share of leaves drawn from the tumbling one.
28
+ // ---------------------------------------------------------------------------
29
+ const LEAF_CT = 1.2;
30
+ const LEAF_CR = Math.PI;
31
+ const LEAF_A = 1.4;
32
+ const LEAF_B = 1.0;
33
+ const LEAF_MU1 = 0.2;
34
+ const LEAF_MU2 = 0.2;
35
+ const LEAF_DT = 0.03; // largest stable step of the plate equations
36
+ const LEAF_RATE_F = 10; // plate time units per second, fluttering leaves
37
+ const LEAF_RATE_T = 4; // ... and tumbling ones, so both descend at a similar pace
38
+ const LEAF_SUN = new THREE.Vector3(0.3853, 0.8807, 0.2752);
39
+ function mulberry32(seed) {
40
+ return () => {
41
+ seed |= 0;
42
+ seed = (seed + 0x6d2b79f5) | 0;
43
+ let r = Math.imul(seed ^ (seed >>> 15), 1 | seed);
44
+ r = (r + Math.imul(r ^ (r >>> 7), 61 | r)) ^ r;
45
+ return ((r ^ (r >>> 14)) >>> 0) / 4294967296;
46
+ };
47
+ }
48
+ /** Plate constants for an elliptic cross-section of density ratio rhoS and thickness ratio beta. */
49
+ function plateConsts(rhoS, beta) {
50
+ const a = 0.5, b = beta * a;
51
+ return {
52
+ l: 2 * a,
53
+ a,
54
+ m: rhoS * Math.PI * a * b,
55
+ mp: (rhoS - 1) * Math.PI * a * b,
56
+ m11: Math.PI * b * b,
57
+ m22: Math.PI * a * a,
58
+ Isum: 0.25 * rhoS * Math.PI * a * b * (a * a + b * b) + 0.125 * Math.PI * (a * a - b * b) * (a * a - b * b),
59
+ };
60
+ }
61
+ function createUniforms() {
62
+ return {
63
+ uTime: { value: 0 },
64
+ uSpeed: { value: 1 },
65
+ uIntensity: { value: 1 },
66
+ uOpacity: { value: 1 },
67
+ uColor: { value: new THREE.Color() },
68
+ uAutumnColor: { value: new THREE.Color() },
69
+ uLeafSize: { value: 1.3 },
70
+ uFallHeight: { value: 3 },
71
+ uSpread: { value: 2.2 },
72
+ uTumble: { value: 0.35 },
73
+ uWind: { value: 0.5 },
74
+ uBound: { value: 1 },
75
+ uSun: { value: LEAF_SUN.clone() },
76
+ };
77
+ }
78
+ function applyConfig(u, c, bound, env) {
79
+ u.uSpeed.value = c.speed;
80
+ u.uIntensity.value = c.intensity;
81
+ u.uOpacity.value = c.opacity;
82
+ u.uColor.value.set(c.color);
83
+ u.uAutumnColor.value.set(c.autumnColor);
84
+ u.uLeafSize.value = c.leafSize;
85
+ u.uFallHeight.value = c.fallHeight;
86
+ u.uSpread.value = c.spread;
87
+ u.uTumble.value = c.tumble;
88
+ u.uWind.value = c.wind;
89
+ u.uBound.value = bound;
90
+ }
91
+ const leafVert = /* glsl */ `
92
+ attribute vec3 aLeaf; // fade, colour mix, seed
93
+ varying vec2 vQ; // -1..1 across the chord, -1..1 from stem to tip
94
+ varying vec3 vN;
95
+ varying vec3 vView;
96
+ varying float vFade;
97
+ varying float vMix;
98
+ varying float vSeed;
99
+ void main(){
100
+ vQ = position.xy * 2.0;
101
+ vFade = aLeaf.x;
102
+ vMix = aLeaf.y;
103
+ vSeed = aLeaf.z;
104
+ // instanceMatrix carries the plate's frame: x = chord, y = span, z = normal
105
+ vec4 world = instanceMatrix * vec4(position, 1.0);
106
+ vN = normalize(mat3(instanceMatrix) * vec3(0.0, 0.0, 1.0));
107
+ vec4 mv = modelViewMatrix * world;
108
+ vView = mv.xyz;
109
+ gl_Position = projectionMatrix * mv;
110
+ }
111
+ `;
112
+ const leafFrag = /* glsl */ `
113
+ uniform vec3 uColor;
114
+ uniform vec3 uAutumnColor;
115
+ uniform vec3 uSun;
116
+ uniform float uIntensity;
117
+ uniform float uOpacity;
118
+ varying vec2 vQ;
119
+ varying vec3 vN;
120
+ varying vec3 vView;
121
+ varying float vFade;
122
+ varying float vMix;
123
+ varying float vSeed;
124
+
125
+ const float TAU = 6.28318530718;
126
+
127
+ // half width of the blade at t (0 stem, 1 tip): a lopsided oval with a serrated edge
128
+ float bladeW(float t, float seed){
129
+ float s = sin(3.14159265 * pow(clamp(t, 0.0, 1.0), 0.7));
130
+ float w = pow(s, 0.8) * (1.0 - 0.22 * t);
131
+ float serr = 1.0 + 0.055 * sin(t * TAU * 6.0 + 0.6 + seed * 3.0);
132
+ return w * serr * 0.94;
133
+ }
134
+
135
+ void main(){
136
+ float t = clamp(vQ.y * 0.5 + 0.5, 0.0, 1.0);
137
+ float x = abs(vQ.x);
138
+ float w = bladeW((t - 0.08) / 0.92, vSeed);
139
+ // anti-alias on the outline's own gradient, so the serrations stay clean
140
+ // wherever the edge runs nearly along the quad's axes
141
+ float f = x - w;
142
+ float fw = max(fwidth(f), 1e-4) * 0.8;
143
+ float ft = max(fwidth(t), 1e-4);
144
+ float blade = (1.0 - smoothstep(-fw, fw, f)) * smoothstep(0.075 - ft, 0.075 + ft, t);
145
+ float fs = max(fwidth(x), 1e-4) * 0.8;
146
+ float stem = (1.0 - smoothstep(-fs, fs, x - 0.032)) * (1.0 - smoothstep(0.02, 0.10, t));
147
+ float a = max(blade, stem) * vFade;
148
+ if (a < 0.02) discard;
149
+
150
+ // midrib and side veins: the light through them is blocked a little more
151
+ float rib = 1.0 - smoothstep(0.0, 0.055 + 0.04 * (1.0 - t), x);
152
+ float vc = fract((t * 5.5 - x * 0.9) * 1.0);
153
+ float veins = (1.0 - smoothstep(0.0, 0.16, min(vc, 1.0 - vc))) * smoothstep(0.03, 0.4, w - x) * 0.7;
154
+ float thickness = 0.55 + 0.45 * rib + 0.3 * veins;
155
+
156
+ vec3 base = mix(uColor, uAutumnColor, clamp(vMix, 0.0, 1.0));
157
+ base *= 0.82 + 0.36 * fract(vSeed * 7.3);
158
+
159
+ // two-sided lighting: the normal always faces the viewer
160
+ vec3 n = normalize(vN);
161
+ vec3 vdir = normalize(-vView);
162
+ vec3 nv = dot(n, vdir) < 0.0 ? -n : n;
163
+ float diff = max(dot(nv, uSun), 0.0);
164
+ // translucency: light coming through the blade from behind, warmer and softer
165
+ float back = max(dot(-nv, uSun), 0.0);
166
+ float trans = pow(back, 1.4) / thickness;
167
+ vec3 warm = mix(base, vec3(1.0, 0.72, 0.26), 0.45) * 1.25;
168
+ // sky fill from above, a dim warm bounce from the ground, plus a weak sheen
169
+ vec3 sky = vec3(0.42, 0.5, 0.64) * (0.55 + 0.45 * max(nv.y, 0.0))
170
+ + vec3(0.2, 0.15, 0.1) * max(-nv.y, 0.0);
171
+ vec3 h = normalize(uSun + vdir);
172
+ float sheen = pow(max(dot(nv, h), 0.0), 26.0) * 0.16;
173
+
174
+ vec3 col = base * (0.42 * sky + 0.95 * diff) + warm * trans * 0.6 + vec3(sheen);
175
+ // veins and midrib catch a little more light, the edge a little less
176
+ col *= 0.9 + 0.28 * rib + 0.16 * veins;
177
+ col *= mix(0.72, 1.0, smoothstep(0.0, 0.12, w - x));
178
+ col *= uIntensity;
179
+ float alpha = a * uOpacity;
180
+ gl_FragColor = vec4(col * alpha, alpha);
181
+ }
182
+ `;
183
+ function build(u, c, bound, env) {
184
+ const n = Math.round(Math.min(Math.max(c.leafCount, 4), 400));
185
+ const rand = mulberry32(4271);
186
+ // per-leaf state: plate equations (u, v, omega, theta) and world position
187
+ const su = new Float64Array(n);
188
+ const sv = new Float64Array(n);
189
+ const sw = new Float64Array(n);
190
+ const st = new Float64Array(n);
191
+ const px = new Float64Array(n);
192
+ const py = new Float64Array(n);
193
+ const pz = new Float64Array(n);
194
+ const gen = new Int32Array(n);
195
+ const isT = new Uint8Array(n);
196
+ const azim = new Float64Array(n);
197
+ const azOff = new Float64Array(n);
198
+ const side = new Float64Array(n);
199
+ const wob = new Float64Array(n * 2); // wobble frequency, phase
200
+ const cmix = new Float32Array(n);
201
+ const seed = new Float32Array(n);
202
+ const pl = [];
203
+ for (let i = 0; i < n; i++)
204
+ pl.push(plateConsts(3, 0.125));
205
+ const fade = new Float32Array(n);
206
+ const order = [];
207
+ for (let i = 0; i < n; i++)
208
+ order.push(i);
209
+ const depth = new Float32Array(n);
210
+ const leafData = new Float32Array(n * 3);
211
+ const geo = new THREE.PlaneGeometry(1, 1);
212
+ const attr = new THREE.InstancedBufferAttribute(leafData, 3);
213
+ attr.setUsage(THREE.DynamicDrawUsage);
214
+ geo.setAttribute('aLeaf', attr);
215
+ const mat = new THREE.ShaderMaterial({
216
+ vertexShader: leafVert, fragmentShader: leafFrag, uniforms: u,
217
+ transparent: true, depthWrite: false, premultipliedAlpha: true,
218
+ blending: THREE.NormalBlending, side: THREE.DoubleSide,
219
+ });
220
+ const mesh = new THREE.InstancedMesh(geo, mat, n);
221
+ mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
222
+ mesh.frustumCulled = false;
223
+ const group = new THREE.Group();
224
+ group.add(mesh);
225
+ const mtx = new THREE.Matrix4();
226
+ const chord = new THREE.Vector3();
227
+ const span = new THREE.Vector3();
228
+ const norm = new THREE.Vector3();
229
+ const pos = new THREE.Vector3();
230
+ const eDir = new THREE.Vector3();
231
+ const ePerp = new THREE.Vector3();
232
+ const k = [0, 0, 0, 0];
233
+ const k2 = [0, 0, 0, 0];
234
+ /** (Re)start leaf i at the top of the column; deterministic in (i, generation). */
235
+ const spawn = (i, first) => {
236
+ const r = mulberry32(i * 7919 + gen[i] * 104729 + 17);
237
+ const bnd = u.uBound.value;
238
+ const spread = Math.max(u.uSpread.value, 1.05);
239
+ const tumbling = r() < u.uTumble.value;
240
+ isT[i] = tumbling ? 1 : 0;
241
+ // fluttering plates are light, tumbling ones heavy: two verified parameter sets
242
+ pl[i] = tumbling ? plateConsts(6, 0.2) : plateConsts(2.4 + r() * 1.0, 0.10 + r() * 0.06);
243
+ const ring = 1.2 + (spread - 1.2) * Math.sqrt(r());
244
+ const ang = r() * Math.PI * 2;
245
+ px[i] = Math.cos(ang) * ring * bnd;
246
+ pz[i] = Math.sin(ang) * ring * bnd;
247
+ const top = bnd * u.uFallHeight.value;
248
+ py[i] = first ? -bnd + (top + bnd) * r() : top * (0.9 + 0.1 * r());
249
+ // the plane the leaf falls in is kept roughly tangential to the object, so a
250
+ // gliding tumbler circles it instead of shooting off into the distance
251
+ side[i] = r() < 0.5 ? -1 : 1;
252
+ azOff[i] = (r() - 0.5) * 0.7;
253
+ azim[i] = ang + side[i] * Math.PI * 0.5 + azOff[i];
254
+ su[i] = (r() - 0.5) * 0.2;
255
+ sv[i] = 0;
256
+ sw[i] = tumbling ? (r() < 0.5 ? -1 : 1) * (2 + r()) : (r() - 0.5) * 0.8;
257
+ st[i] = r() * Math.PI * 2;
258
+ wob[i * 2] = 0.5 + r() * 0.7;
259
+ wob[i * 2 + 1] = r() * Math.PI * 2;
260
+ cmix[i] = r();
261
+ seed[i] = r();
262
+ fade[i] = 0;
263
+ };
264
+ for (let i = 0; i < n; i++)
265
+ spawn(i, true);
266
+ /** One midpoint step of the plate equations for leaf i. */
267
+ const derivs = (i, uu, vv, ww, th, out) => {
268
+ const p = pl[i];
269
+ const V = Math.sqrt(uu * uu + vv * vv) + 1e-9;
270
+ const cos2a = (uu * uu - vv * vv) / (V * V);
271
+ const G = -0.5 * LEAF_CT * p.l * (uu * vv) / V + 0.5 * LEAF_CR * p.l * p.l * ww;
272
+ const cd = 0.5 * p.l * (LEAF_A - LEAF_B * cos2a) * V;
273
+ const tau = Math.PI * p.a * p.a * p.a * p.a * (LEAF_MU1 * V / p.a + LEAF_MU2 * Math.abs(ww)) * ww;
274
+ out[0] = ((p.m + p.m22) * ww * vv - G * vv - p.mp * Math.sin(th) - cd * uu) / (p.m + p.m11);
275
+ out[1] = (-(p.m + p.m11) * ww * uu + G * uu - p.mp * Math.cos(th) - cd * vv) / (p.m + p.m22);
276
+ out[2] = ((p.m11 - p.m22) * uu * vv - tau) / p.Isum;
277
+ out[3] = ww;
278
+ };
279
+ return {
280
+ object: group,
281
+ materials: [mat],
282
+ apply(c2) { },
283
+ update(dt, camera) {
284
+ u.uTime.value += dt * u.uSpeed.value;
285
+ const t = u.uTime.value;
286
+ const bnd = u.uBound.value;
287
+ const width = Math.max(u.uLeafSize.value, 0.01) * 0.16 * bnd; // the plate's chord, in world units
288
+ const length = width / 0.55;
289
+ const dtReal = Math.min(dt, 0.1) * Math.max(u.uSpeed.value, 0);
290
+ // the breeze is one big slow eddy: it gusts and swings round, so leaves
291
+ // get pushed about instead of being blown out of the scene for good
292
+ const gustA = t * 0.052 + 0.7 * Math.sin(t * 0.081);
293
+ const gustM = u.uWind.value * bnd * 0.24 * (0.7 + 0.5 * Math.sin(t * 0.23) + 0.2 * Math.sin(t * 0.41 + 1.1));
294
+ const gustX = Math.cos(gustA) * gustM;
295
+ const gustZ = Math.sin(gustA) * gustM;
296
+ const floorY = -bnd * 0.97;
297
+ const outR = bnd * (u.uSpread.value + 1.6);
298
+ const camPos = camera ? camera.position : null;
299
+ for (let i = 0; i < n; i++) {
300
+ const rate = isT[i] ? LEAF_RATE_T : LEAF_RATE_F;
301
+ let tsim = dtReal * rate;
302
+ const steps = Math.max(1, Math.min(8, Math.ceil(tsim / LEAF_DT)));
303
+ const h = tsim / steps;
304
+ let dxp = 0, dyp = 0;
305
+ for (let s = 0; s < steps; s++) {
306
+ derivs(i, su[i], sv[i], sw[i], st[i], k);
307
+ const hm = 0.5 * h;
308
+ derivs(i, su[i] + hm * k[0], sv[i] + hm * k[1], sw[i] + hm * k[2], st[i] + hm * k[3], k2);
309
+ su[i] += h * k2[0];
310
+ sv[i] += h * k2[1];
311
+ sw[i] += h * k2[2];
312
+ st[i] += h * k2[3];
313
+ // clamps: the model never needs these, but a bad config must not explode
314
+ if (su[i] > 6)
315
+ su[i] = 6;
316
+ else if (su[i] < -6)
317
+ su[i] = -6;
318
+ if (sv[i] > 6)
319
+ sv[i] = 6;
320
+ else if (sv[i] < -6)
321
+ sv[i] = -6;
322
+ if (sw[i] > 14)
323
+ sw[i] = 14;
324
+ else if (sw[i] < -14)
325
+ sw[i] = -14;
326
+ const cs = Math.cos(st[i]), sn = Math.sin(st[i]);
327
+ dxp += h * (su[i] * cs - sv[i] * sn);
328
+ dyp += h * (su[i] * sn + sv[i] * cs);
329
+ }
330
+ if (!isFinite(su[i] + sv[i] + sw[i] + st[i])) {
331
+ gen[i]++;
332
+ spawn(i, false);
333
+ }
334
+ // the falling plane keeps facing around the object, with a slow
335
+ // wander on top, so the leaves circle it instead of leaving
336
+ const hAng = Math.atan2(pz[i], px[i]);
337
+ azim[i] = hAng + side[i] * Math.PI * 0.5 + azOff[i] + 0.25 * Math.sin(t * 0.13 + wob[i * 2 + 1]);
338
+ const ca = Math.cos(azim[i]), sa = Math.sin(azim[i]);
339
+ eDir.set(ca, 0, sa);
340
+ ePerp.set(-sa, 0, ca);
341
+ px[i] += dxp * width * ca + gustX * dtReal;
342
+ pz[i] += dxp * width * sa + gustZ * dtReal;
343
+ py[i] += dyp * width;
344
+ // the leaf may not pass through the host
345
+ const rr = Math.sqrt(px[i] * px[i] + py[i] * py[i] + pz[i] * pz[i]);
346
+ const rmin = bnd * 1.04 + length * 0.35;
347
+ if (rr < rmin && rr > 1e-5) {
348
+ const f = rmin / rr;
349
+ px[i] *= f;
350
+ py[i] *= f;
351
+ pz[i] *= f;
352
+ // and slides off it instead of sticking
353
+ const nx = px[i] / rmin, ny = py[i] / rmin, nz = pz[i] / rmin;
354
+ const slide = dtReal * bnd * 0.35;
355
+ px[i] += slide * (-ny) * nx / Math.max(1 - ny * ny, 0.08);
356
+ pz[i] += slide * (-ny) * nz / Math.max(1 - ny * ny, 0.08);
357
+ }
358
+ const hr = Math.sqrt(px[i] * px[i] + pz[i] * pz[i]);
359
+ if (py[i] < floorY - bnd * 0.05 || hr > outR) {
360
+ gen[i]++;
361
+ spawn(i, false);
362
+ }
363
+ // fade in at the top, out just above the ground and at the far edge
364
+ const top = bnd * u.uFallHeight.value;
365
+ const fIn = Math.min(1, Math.max(0, (top + bnd * 0.05 - py[i]) / (bnd * 0.5)));
366
+ const fOut = Math.min(1, Math.max(0, (py[i] - floorY) / (bnd * 0.45)));
367
+ const fSide = 1 - Math.min(1, Math.max(0, (hr - outR * 0.78) / (outR * 0.22)));
368
+ fade[i] = Math.min(Math.min(fIn, fOut), fSide);
369
+ depth[i] = camPos ? -((px[i] - camPos.x) * (px[i] - camPos.x) + (py[i] - camPos.y) * (py[i] - camPos.y) + (pz[i] - camPos.z) * (pz[i] - camPos.z)) : -py[i];
370
+ }
371
+ // painter's order: furthest first, so the transparent blades layer correctly
372
+ for (let a = 1; a < n; a++) {
373
+ const v = order[a];
374
+ let b = a - 1;
375
+ while (b >= 0 && depth[order[b]] > depth[v]) {
376
+ order[b + 1] = order[b];
377
+ b--;
378
+ }
379
+ order[b + 1] = v;
380
+ }
381
+ for (let j = 0; j < n; j++) {
382
+ const i = order[j];
383
+ const ca = Math.cos(azim[i]), sa = Math.sin(azim[i]);
384
+ const cs = Math.cos(st[i]), sn = Math.sin(st[i]);
385
+ // chord lies in the falling plane at angle theta; span is the leaf's long axis
386
+ chord.set(ca * cs, sn, sa * cs);
387
+ span.set(-sa, 0, ca);
388
+ // a little out-of-plane wobble: roll about the chord
389
+ const roll = 0.3 * Math.sin(t * wob[i * 2] + wob[i * 2 + 1]);
390
+ const cr = Math.cos(roll), sr = Math.sin(roll);
391
+ norm.copy(chord).cross(span).normalize();
392
+ span.multiplyScalar(cr).addScaledVector(norm, sr).normalize();
393
+ norm.copy(chord).cross(span).normalize();
394
+ pos.set(px[i], py[i], pz[i]).addScaledVector(span, 0.12 * length * Math.sin(t * wob[i * 2] * 0.7 + wob[i * 2 + 1]));
395
+ mtx.makeBasis(chord.multiplyScalar(width), span.multiplyScalar(length), norm);
396
+ mtx.setPosition(pos);
397
+ mesh.setMatrixAt(j, mtx);
398
+ leafData[j * 3] = fade[i];
399
+ leafData[j * 3 + 1] = cmix[i];
400
+ leafData[j * 3 + 2] = seed[i];
401
+ }
402
+ mesh.instanceMatrix.needsUpdate = true;
403
+ attr.needsUpdate = true;
404
+ },
405
+ dispose() { geo.dispose(); mat.dispose(); },
406
+ };
407
+ }
408
+ /** Defaults for every knob, so an older saved config still renders. */
409
+ const DEFAULTS = {
410
+ intensity: 1,
411
+ speed: 1,
412
+ color: '#7f9b3f',
413
+ opacity: 1,
414
+ leafCount: 60,
415
+ leafSize: 1.3,
416
+ fallHeight: 3,
417
+ spread: 2.2,
418
+ tumble: 0.35,
419
+ wind: 0.5,
420
+ autumnColor: '#c4631f',
421
+ };
422
+ const FallingLeavesEffect = ({ boundingRadius, config, animations, hasHost = false, onObjectReady, onObjectDispose }) => {
423
+ const rootRef = useRef(null);
424
+ const animCache = useRef({});
425
+ useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
426
+ // a standalone effect gets boundingRadius 1
427
+ const b = boundingRadius > 0 ? boundingRadius : 1;
428
+ const cfg = useMemo(() => ({ ...DEFAULTS, ...config }), [config]);
429
+ const env = useMemo(() => ({ hasHost: hasHost }), [hasHost]);
430
+ // uniforms outlive the objects, so keyframe animations survive a rebuild
431
+ const uniforms = useMemo(() => createUniforms(), []);
432
+ // rebuilt only when the host size or a structural knob changes
433
+ const instance = useMemo(() => build(uniforms, { ...DEFAULTS, ...config }, b, env), [uniforms, b, env, cfg.leafCount]);
434
+ useEffect(() => () => instance.dispose(), [instance]);
435
+ useLayoutEffect(() => {
436
+ applyConfig(uniforms, cfg, b, env);
437
+ instance.apply(cfg);
438
+ }, [instance, uniforms, cfg, b, env]);
439
+ useFrame((state, delta) => {
440
+ instance.update(Math.min(delta, 0.1), state.camera);
441
+ if (animations?.length)
442
+ applyGenerativeAnimations(instance.materials, animations, animCache.current, Date.now(), false);
443
+ });
444
+ return (_jsx("group", { ref: rootRef, children: _jsx("primitive", { object: instance.object }) }));
445
+ };
446
+ export default FallingLeavesEffect;
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import type { AnimationOptions, FieldLinesConfig } from '@3driseai/3drise-core';
3
+ import type { ObjectReadyProps } from '../../types/objectReady';
4
+ export interface FieldLinesEffectProps extends ObjectReadyProps {
5
+ boundingRadius: number;
6
+ config: FieldLinesConfig;
7
+ animations?: AnimationOptions[];
8
+ }
9
+ declare const FieldLinesEffect: React.FC<FieldLinesEffectProps>;
10
+ export default FieldLinesEffect;
11
+ //# sourceMappingURL=FieldLinesEffect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FieldLinesEffect.d.ts","sourceRoot":"","sources":["../../../src/components/effects/FieldLinesEffect.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAGhF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,qBAAsB,SAAQ,gBAAgB;IAC3D,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACnC;AAqND,QAAA,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CA2DrD,CAAC;AACF,eAAe,gBAAgB,CAAC"}