@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.
- package/README.md +4 -3
- package/dist/components/ChladniGrid.d.ts +15 -0
- package/dist/components/ChladniGrid.d.ts.map +1 -0
- package/dist/components/ChladniGrid.js +62 -0
- package/dist/components/CustomPrimitive.d.ts +2 -0
- package/dist/components/CustomPrimitive.d.ts.map +1 -1
- package/dist/components/CustomPrimitive.js +2 -2
- package/dist/components/EffectsGenerator.d.ts.map +1 -1
- package/dist/components/EffectsGenerator.js +26 -2
- package/dist/components/FlowNetGrid.d.ts +15 -0
- package/dist/components/FlowNetGrid.d.ts.map +1 -0
- package/dist/components/FlowNetGrid.js +61 -0
- package/dist/components/Galaxy.d.ts.map +1 -1
- package/dist/components/Galaxy.js +2 -0
- package/dist/components/GirihGrid.d.ts +15 -0
- package/dist/components/GirihGrid.d.ts.map +1 -0
- package/dist/components/GirihGrid.js +62 -0
- package/dist/components/GravWaveGrid.d.ts +15 -0
- package/dist/components/GravWaveGrid.d.ts.map +1 -0
- package/dist/components/GravWaveGrid.js +62 -0
- package/dist/components/GridsGenerator.d.ts +2 -2
- package/dist/components/GridsGenerator.d.ts.map +1 -1
- package/dist/components/GridsGenerator.js +21 -0
- package/dist/components/HyperbolicGrid.d.ts +15 -0
- package/dist/components/HyperbolicGrid.d.ts.map +1 -0
- package/dist/components/HyperbolicGrid.js +61 -0
- package/dist/components/LayoutInstances.d.ts +13 -0
- package/dist/components/LayoutInstances.d.ts.map +1 -0
- package/dist/components/LayoutInstances.js +39 -0
- package/dist/components/ModelPrimitive.d.ts +2 -0
- package/dist/components/ModelPrimitive.d.ts.map +1 -1
- package/dist/components/ModelPrimitive.js +3 -3
- package/dist/components/ObjectNode.d.ts.map +1 -1
- package/dist/components/ObjectNode.js +44 -9
- package/dist/components/ParticlesGenerator.d.ts +10 -0
- package/dist/components/ParticlesGenerator.d.ts.map +1 -0
- package/dist/components/ParticlesGenerator.js +23 -0
- package/dist/components/ReactionDiffusionGrid.d.ts +15 -0
- package/dist/components/ReactionDiffusionGrid.d.ts.map +1 -0
- package/dist/components/ReactionDiffusionGrid.js +376 -0
- package/dist/components/WallpaperGrid.d.ts +15 -0
- package/dist/components/WallpaperGrid.d.ts.map +1 -0
- package/dist/components/WallpaperGrid.js +555 -0
- package/dist/components/cursor/CursorController.d.ts.map +1 -1
- package/dist/components/effects/BlackHoleEffect.d.ts +3 -3
- package/dist/components/effects/BlackHoleEffect.d.ts.map +1 -1
- package/dist/components/effects/BlackHoleEffect.js +293 -163
- package/dist/components/effects/DandelionSeedsEffect.d.ts +13 -0
- package/dist/components/effects/DandelionSeedsEffect.d.ts.map +1 -0
- package/dist/components/effects/DandelionSeedsEffect.js +510 -0
- package/dist/components/effects/FallingLeavesEffect.d.ts +13 -0
- package/dist/components/effects/FallingLeavesEffect.d.ts.map +1 -0
- package/dist/components/effects/FallingLeavesEffect.js +446 -0
- package/dist/components/effects/FieldLinesEffect.d.ts +11 -0
- package/dist/components/effects/FieldLinesEffect.d.ts.map +1 -0
- package/dist/components/effects/FieldLinesEffect.js +260 -0
- package/dist/components/effects/FireflySwarmEffect.d.ts +13 -0
- package/dist/components/effects/FireflySwarmEffect.d.ts.map +1 -0
- package/dist/components/effects/FireflySwarmEffect.js +408 -0
- package/dist/components/effects/GalaxyEffect.d.ts +13 -0
- package/dist/components/effects/GalaxyEffect.d.ts.map +1 -0
- package/dist/components/effects/GalaxyEffect.js +374 -0
- package/dist/components/effects/HarmonicShellEffect.d.ts +11 -0
- package/dist/components/effects/HarmonicShellEffect.d.ts.map +1 -0
- package/dist/components/effects/HarmonicShellEffect.js +209 -0
- package/dist/components/effects/IceCrystalsEffect.d.ts +3 -3
- package/dist/components/effects/IceCrystalsEffect.d.ts.map +1 -1
- package/dist/components/effects/IceCrystalsEffect.js +286 -166
- package/dist/components/effects/IceHaloEffect.d.ts +13 -0
- package/dist/components/effects/IceHaloEffect.d.ts.map +1 -0
- package/dist/components/effects/IceHaloEffect.js +306 -0
- package/dist/components/effects/LichtenbergEffect.d.ts +13 -0
- package/dist/components/effects/LichtenbergEffect.d.ts.map +1 -0
- package/dist/components/effects/LichtenbergEffect.js +517 -0
- package/dist/components/effects/MoleculesEffect.d.ts +3 -3
- package/dist/components/effects/MoleculesEffect.d.ts.map +1 -1
- package/dist/components/effects/MoleculesEffect.js +322 -164
- package/dist/components/effects/MurmurationEffect.d.ts +13 -0
- package/dist/components/effects/MurmurationEffect.d.ts.map +1 -0
- package/dist/components/effects/MurmurationEffect.js +539 -0
- package/dist/components/effects/PlanetaryRingsEffect.d.ts +13 -0
- package/dist/components/effects/PlanetaryRingsEffect.d.ts.map +1 -0
- package/dist/components/effects/PlanetaryRingsEffect.js +458 -0
- package/dist/components/effects/PortalEffect.d.ts +3 -3
- package/dist/components/effects/PortalEffect.d.ts.map +1 -1
- package/dist/components/effects/PortalEffect.js +240 -163
- package/dist/components/effects/ShockwaveEffect.d.ts +5 -3
- package/dist/components/effects/ShockwaveEffect.d.ts.map +1 -1
- package/dist/components/effects/ShockwaveEffect.js +538 -142
- package/dist/components/effects/SmokePlumeEffect.d.ts +3 -3
- package/dist/components/effects/SmokePlumeEffect.d.ts.map +1 -1
- package/dist/components/effects/SmokePlumeEffect.js +180 -116
- package/dist/components/effects/SmokeRingEffect.d.ts +3 -3
- package/dist/components/effects/SmokeRingEffect.d.ts.map +1 -1
- package/dist/components/effects/SmokeRingEffect.js +260 -135
- package/dist/components/effects/StrangeAttractorEffect.d.ts +11 -0
- package/dist/components/effects/StrangeAttractorEffect.d.ts.map +1 -0
- package/dist/components/effects/StrangeAttractorEffect.js +229 -0
- package/dist/components/effects/VolumetricFogEffect.d.ts +5 -3
- package/dist/components/effects/VolumetricFogEffect.d.ts.map +1 -1
- package/dist/components/effects/VolumetricFogEffect.js +211 -93
- package/dist/components/effects/WaterCausticsEffect.d.ts +13 -0
- package/dist/components/effects/WaterCausticsEffect.d.ts.map +1 -0
- package/dist/components/effects/WaterCausticsEffect.js +308 -0
- package/dist/components/effects/index.d.ts +9 -0
- package/dist/components/effects/index.d.ts.map +1 -1
- package/dist/components/effects/index.js +9 -0
- package/dist/components/index.d.ts +19 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +10 -0
- package/dist/components/particles/BioluminescentWakeParticles.d.ts +12 -0
- package/dist/components/particles/BioluminescentWakeParticles.d.ts.map +1 -0
- package/dist/components/particles/BioluminescentWakeParticles.js +1123 -0
- package/dist/components/particles/BrownianDustParticles.d.ts +12 -0
- package/dist/components/particles/BrownianDustParticles.d.ts.map +1 -0
- package/dist/components/particles/BrownianDustParticles.js +757 -0
- package/dist/components/particles/ElectronOrbitalsParticles.d.ts +12 -0
- package/dist/components/particles/ElectronOrbitalsParticles.d.ts.map +1 -0
- package/dist/components/particles/ElectronOrbitalsParticles.js +944 -0
- package/dist/components/particles/GrinderSparksParticles.d.ts +12 -0
- package/dist/components/particles/GrinderSparksParticles.d.ts.map +1 -0
- package/dist/components/particles/GrinderSparksParticles.js +744 -0
- package/dist/components/particles/TidalStreamParticles.d.ts +12 -0
- package/dist/components/particles/TidalStreamParticles.d.ts.map +1 -0
- package/dist/components/particles/TidalStreamParticles.js +788 -0
- package/dist/components/particles/index.d.ts +11 -0
- package/dist/components/particles/index.d.ts.map +1 -0
- package/dist/components/particles/index.js +6 -0
- package/dist/constants.d.ts +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +1 -1
- package/dist/hooks/useModelObject.d.ts +6 -1
- package/dist/hooks/useModelObject.d.ts.map +1 -1
- package/dist/hooks/useModelObject.js +19 -6
- package/dist/types/gridConfigs.d.ts +98 -0
- package/dist/types/gridConfigs.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/particleTrails.d.ts +70 -0
- package/dist/utils/particleTrails.d.ts.map +1 -0
- package/dist/utils/particleTrails.js +369 -0
- package/dist/utils/pointerField.d.ts +28 -0
- package/dist/utils/pointerField.d.ts.map +1 -0
- package/dist/utils/pointerField.js +103 -0
- 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"}
|