@3driseai/3drise-viewer 0.1.16 → 0.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/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 +36 -8
- 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 +16 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +8 -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/package.json +2 -2
|
@@ -1,162 +1,558 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
2
|
-
import {
|
|
3
|
-
import { useFrame } from '@react-three/fiber';
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useLayoutEffect, useMemo, useRef } from 'react';
|
|
4
3
|
import * as THREE from 'three';
|
|
4
|
+
import { useFrame } from '@react-three/fiber';
|
|
5
5
|
import { applyGenerativeAnimations } from '../../utils/handleGenerativeEffectsAnimations.js';
|
|
6
6
|
import { useObjectReady } from '../../hooks/useObjectReady.js';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
7
|
+
/**
|
|
8
|
+
* A Sedov–Taylor blast wave: a razor-bright front over a compressed shell that bursts out, slows and cools, raising dust and a brief condensation dome. Use for explosions, impacts, sci-fi pulses.
|
|
9
|
+
*
|
|
10
|
+
* R ∝ t^(2/5) · ρ₂/ρ₁ = (γ+1)/(γ−1) = 4 · ρ(r) ≈ 4(r/R)^9 · front heat ∝ Ṙ² · grain kick at t₀ = R⁻¹(r₀)
|
|
11
|
+
*/
|
|
12
|
+
const NOISE3 = /* glsl */ `
|
|
13
|
+
float hash13(vec3 p3){
|
|
14
|
+
p3 = fract(p3 * 0.1031);
|
|
15
|
+
p3 += dot(p3, p3.zyx + 31.32);
|
|
16
|
+
return fract((p3.x + p3.y) * p3.z);
|
|
17
|
+
}
|
|
18
|
+
float vnoise(vec3 p){
|
|
19
|
+
vec3 i = floor(p);
|
|
20
|
+
vec3 f = fract(p);
|
|
21
|
+
f = f * f * f * (f * (f * 6.0 - 15.0) + 10.0);
|
|
22
|
+
float n000 = hash13(i);
|
|
23
|
+
float n100 = hash13(i + vec3(1.0, 0.0, 0.0));
|
|
24
|
+
float n010 = hash13(i + vec3(0.0, 1.0, 0.0));
|
|
25
|
+
float n110 = hash13(i + vec3(1.0, 1.0, 0.0));
|
|
26
|
+
float n001 = hash13(i + vec3(0.0, 0.0, 1.0));
|
|
27
|
+
float n101 = hash13(i + vec3(1.0, 0.0, 1.0));
|
|
28
|
+
float n011 = hash13(i + vec3(0.0, 1.0, 1.0));
|
|
29
|
+
float n111 = hash13(i + vec3(1.0, 1.0, 1.0));
|
|
30
|
+
return mix(mix(mix(n000, n100, f.x), mix(n010, n110, f.x), f.y),
|
|
31
|
+
mix(mix(n001, n101, f.x), mix(n011, n111, f.x), f.y), f.z);
|
|
32
|
+
}
|
|
33
|
+
// low-frequency fBm; the rotation between octaves hides the lattice
|
|
34
|
+
const mat3 FBM_ROT = mat3(0.00, 0.80, 0.60, -0.80, 0.36, -0.48, -0.60, -0.48, 0.64);
|
|
35
|
+
float fbm3(vec3 p, int octaves){
|
|
36
|
+
float a = 0.5, s = 0.0;
|
|
37
|
+
for (int i = 0; i < 5; i++){
|
|
38
|
+
if (i >= octaves) break;
|
|
39
|
+
s += a * vnoise(p);
|
|
40
|
+
p = FBM_ROT * p * 2.02;
|
|
41
|
+
a *= 0.5;
|
|
42
|
+
}
|
|
43
|
+
return s;
|
|
44
|
+
}
|
|
45
|
+
`;
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Sedov–Taylor blast wave.
|
|
48
|
+
// A point explosion of energy E in gas of density rho has only one length
|
|
49
|
+
// scale, R = (E t^2 / rho)^(1/5), so the front bursts out fast and then
|
|
50
|
+
// coasts: R ~ t^(2/5), speed dR/dt ~ t^(-3/5). The front is a strong shock:
|
|
51
|
+
// the gas jumps to (gamma+1)/(gamma-1) = 4 times its density (gamma = 5/3)
|
|
52
|
+
// and nearly all swept-up mass sits in a thin shell, rho(r) ~ 4 (r/R)^9,
|
|
53
|
+
// leaving a hot rarefied interior. Post-shock temperature goes as the front
|
|
54
|
+
// speed squared, so the front starts blue-white and cools as it slows. A thin
|
|
55
|
+
// decelerating shell is overstable (Vishniac): low-order lobes grow with age.
|
|
56
|
+
// Every slot carries (tau, R/Rmax, dR/dtau, fade) and (seed, flash, heat, life)
|
|
57
|
+
// from the CPU clock, so the shaders only draw the state.
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
const waveCommon = /* glsl */ `
|
|
60
|
+
uniform float uTime;
|
|
61
|
+
uniform float uBound;
|
|
62
|
+
uniform float uExpansionRadius;
|
|
63
|
+
uniform float uThickness;
|
|
64
|
+
uniform float uInstability;
|
|
65
|
+
uniform float uDeceleration;
|
|
66
|
+
uniform float uShape;
|
|
67
|
+
uniform vec4 uWaveA[4]; // tau (0..1), R / Rmax, dR/dtau, fade
|
|
68
|
+
uniform vec4 uWaveB[4]; // seed, birth flash, heat (0..1), life in seconds
|
|
69
|
+
${NOISE3}
|
|
22
70
|
|
|
23
|
-
|
|
71
|
+
// Vishniac lobes on a ring: azimuthal modes l = 3..6, amplitude growing with age
|
|
72
|
+
float ringLobes(float th, float seed, float tau){
|
|
73
|
+
float s = 0.0;
|
|
74
|
+
for (int l = 3; l <= 6; l++){
|
|
75
|
+
float fl = float(l);
|
|
76
|
+
s += sin(fl * th + seed * fl * 2.39 + uTime * 0.04 * (7.0 - fl)) / (fl - 1.5);
|
|
77
|
+
}
|
|
78
|
+
return s * uInstability * 0.1 * pow(tau, 1.3);
|
|
79
|
+
}
|
|
80
|
+
// the same on a sphere: low-frequency noise on the direction
|
|
81
|
+
float shellLobes(vec3 dir, float seed, float tau){
|
|
82
|
+
float n = vnoise(dir * 1.7 + vec3(seed * 17.0, seed * 5.0 + uTime * 0.03, 3.0));
|
|
83
|
+
n += 0.5 * vnoise(dir * 3.1 + vec3(seed * 9.0, 7.0, uTime * 0.02));
|
|
84
|
+
return (n - 0.75) * uInstability * 0.16 * pow(tau, 1.3);
|
|
85
|
+
}
|
|
86
|
+
// hot front colour: blue-white while the shock is strong, cooling to the rim colour
|
|
87
|
+
vec3 frontColour(vec3 rim, float heat){
|
|
88
|
+
return mix(rim, vec3(0.8, 0.9, 1.0), heat) * (0.55 + 2.1 * heat * heat);
|
|
24
89
|
}
|
|
25
90
|
`;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
uniform
|
|
29
|
-
uniform
|
|
91
|
+
// Ground mode: one disc in the XZ plane that sums all slots.
|
|
92
|
+
const discVert = /* glsl */ `
|
|
93
|
+
uniform float uExpansionRadius;
|
|
94
|
+
uniform float uBound;
|
|
95
|
+
varying vec3 vLocal;
|
|
96
|
+
void main(){
|
|
97
|
+
vec3 p = vec3(position.x, 0.0, -position.y) * uExpansionRadius * uBound * 1.25;
|
|
98
|
+
vLocal = p;
|
|
99
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(p, 1.0);
|
|
100
|
+
}
|
|
101
|
+
`;
|
|
102
|
+
const discFrag = /* glsl */ `
|
|
103
|
+
uniform vec3 uColor;
|
|
104
|
+
uniform vec3 uRimColor;
|
|
105
|
+
uniform float uIntensity;
|
|
30
106
|
uniform float uOpacity;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
107
|
+
${waveCommon}
|
|
108
|
+
varying vec3 vLocal;
|
|
109
|
+
void main(){
|
|
110
|
+
float r = length(vLocal.xz);
|
|
111
|
+
float th = atan(vLocal.z, vLocal.x);
|
|
112
|
+
float Rmax = uExpansionRadius * uBound;
|
|
113
|
+
float aa = fwidth(r);
|
|
114
|
+
vec3 col = vec3(0.0);
|
|
115
|
+
for (int i = 0; i < 4; i++){
|
|
116
|
+
vec4 A = uWaveA[i];
|
|
117
|
+
if (A.w <= 0.0) continue;
|
|
118
|
+
vec4 B = uWaveB[i];
|
|
119
|
+
float Re = A.y * Rmax * (1.0 + ringLobes(th, B.x, A.x));
|
|
120
|
+
float d = r - Re;
|
|
121
|
+
float w = max(uThickness * uBound * 0.5, aa * 0.8);
|
|
122
|
+
// front: razor leading edge (the gas ahead is untouched), slightly softer behind the jump
|
|
123
|
+
float lead = exp(-d * d / (w * w * 0.45));
|
|
124
|
+
float trail = exp(-d * d / (w * w * 2.2));
|
|
125
|
+
float front = mix(trail, lead, smoothstep(-aa, aa, d));
|
|
126
|
+
float behind = 1.0 - smoothstep(-w, w * 0.5, d);
|
|
127
|
+
// compressed shell rho/4 = (r/R)^9 and the rarefied hot interior
|
|
128
|
+
float x = clamp(r / max(Re, 1e-4), 0.0, 1.0);
|
|
129
|
+
float shell = pow(x, 9.0) * behind;
|
|
130
|
+
float interior = x * x * behind;
|
|
131
|
+
// filaments along the front, fixed to the gas (they only change with age)
|
|
132
|
+
float fil = 0.7 + 0.6 * vnoise(vec3(cos(th) * 6.0, sin(th) * 6.0, B.x * 13.0 + A.x * 1.2));
|
|
133
|
+
vec3 hot = frontColour(uRimColor, B.z);
|
|
134
|
+
vec3 c = hot * front * fil;
|
|
135
|
+
c += mix(uRimColor, hot, 0.5) * shell * (0.3 + 0.7 * B.z) * fil;
|
|
136
|
+
c += mix(uRimColor, uColor, 0.5) * interior * 0.05 * B.z;
|
|
137
|
+
// birth flash: the front and a ground glow burn in the flash colour
|
|
138
|
+
c += uColor * B.y * (front * 2.5 + 1.1 * exp(-r * r / (uBound * uBound * 1.6)));
|
|
139
|
+
col += c * A.w;
|
|
140
|
+
}
|
|
141
|
+
col *= uIntensity * uOpacity;
|
|
142
|
+
if (max(col.r, max(col.g, col.b)) < 0.002) discard;
|
|
143
|
+
gl_FragColor = vec4(col, 1.0);
|
|
144
|
+
}
|
|
145
|
+
`;
|
|
146
|
+
// Sphere mode (and the condensation dome): an instanced unit sphere per slot.
|
|
147
|
+
const sphereVert = /* glsl */ `
|
|
148
|
+
attribute float aSlot;
|
|
149
|
+
uniform float uHaze;
|
|
150
|
+
${waveCommon}
|
|
151
|
+
varying vec3 vN;
|
|
152
|
+
varying vec3 vView;
|
|
153
|
+
varying vec3 vDir;
|
|
154
|
+
varying vec4 vA;
|
|
155
|
+
varying vec4 vB;
|
|
156
|
+
void main(){
|
|
157
|
+
int i = int(aSlot + 0.5);
|
|
158
|
+
vA = uWaveA[i];
|
|
159
|
+
vB = uWaveB[i];
|
|
160
|
+
vec3 dir = normalize(position);
|
|
161
|
+
vDir = dir;
|
|
162
|
+
float R = vA.y * uExpansionRadius * uBound * mix(1.0, 0.86, uHaze);
|
|
163
|
+
vec3 p = dir * R * (1.0 + shellLobes(dir, vB.x, vA.x));
|
|
164
|
+
if (vA.w <= 0.0) p = vec3(0.0);
|
|
165
|
+
vec4 wp = modelMatrix * vec4(p, 1.0);
|
|
166
|
+
vN = normalize(mat3(modelMatrix) * dir);
|
|
167
|
+
vView = cameraPosition - wp.xyz;
|
|
168
|
+
gl_Position = projectionMatrix * viewMatrix * wp;
|
|
58
169
|
}
|
|
59
170
|
`;
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
171
|
+
// Emission of a thin spherical shell seen at impact parameter b: the chord through
|
|
172
|
+
// the shell between radii Ri and Ro. It peaks just inside the silhouette, which is
|
|
173
|
+
// the physically exact version of a Fresnel edge.
|
|
174
|
+
const shellFrag = /* glsl */ `
|
|
175
|
+
uniform vec3 uColor;
|
|
176
|
+
uniform vec3 uRimColor;
|
|
177
|
+
uniform float uIntensity;
|
|
64
178
|
uniform float uOpacity;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
179
|
+
uniform float uExpansionRadius;
|
|
180
|
+
uniform float uBound;
|
|
181
|
+
uniform float uThickness;
|
|
182
|
+
varying vec3 vN;
|
|
183
|
+
varying vec3 vView;
|
|
184
|
+
varying vec3 vDir;
|
|
185
|
+
varying vec4 vA;
|
|
186
|
+
varying vec4 vB;
|
|
187
|
+
${NOISE3}
|
|
188
|
+
float chord(float ro, float ri, float b){
|
|
189
|
+
return 2.0 * (sqrt(max(ro * ro - b * b, 0.0)) - sqrt(max(ri * ri - b * b, 0.0)));
|
|
190
|
+
}
|
|
191
|
+
void main(){
|
|
192
|
+
if (vA.w <= 0.0) discard;
|
|
193
|
+
float mu = clamp(abs(dot(normalize(vN), normalize(vView))), 0.0, 1.0);
|
|
194
|
+
float R = max(vA.y * uExpansionRadius * uBound, 1e-3);
|
|
195
|
+
float b = R * sqrt(1.0 - mu * mu);
|
|
196
|
+
// no fwidth here: the chord profile is already smooth in b, and fwidth(b)
|
|
197
|
+
// diverges at the centre of the disc where b = R*sqrt(1 - mu*mu)
|
|
198
|
+
float w = clamp(uThickness * uBound * 0.5, R * 0.002, R * 0.2);
|
|
199
|
+
float front = chord(R, R - w, b) / (2.0 * sqrt(2.0 * R * w - w * w));
|
|
200
|
+
// compressed shell rho/4 ~ (r/R)^9: three thin slabs, weighted by that profile
|
|
201
|
+
float r1 = R * 0.965, r2 = R * 0.9, r3 = R * 0.8;
|
|
202
|
+
float shell = 0.35 * chord(R - w, r1, b) / (2.0 * sqrt(max(R * R - r1 * r1, 1e-6)))
|
|
203
|
+
+ 0.09 * chord(r1, r2, b) / (2.0 * sqrt(max(R * R - r2 * r2, 1e-6)))
|
|
204
|
+
+ 0.02 * chord(r2, r3, b) / (2.0 * sqrt(max(R * R - r3 * r3, 1e-6)));
|
|
205
|
+
// filaments fixed to the gas: they only change with age, never per frame
|
|
206
|
+
float fil = 0.7 + 0.6 * vnoise(vDir * 4.5 + vec3(vB.x * 13.0, vA.x * 1.2, 2.0));
|
|
207
|
+
vec3 hot = mix(uRimColor, vec3(0.8, 0.9, 1.0), vB.z) * (0.55 + 2.1 * vB.z * vB.z);
|
|
208
|
+
// a hot plasma radiates as density squared (emission measure), so square the
|
|
209
|
+
// integrated chords: the limb keeps its value and the disc goes dark
|
|
210
|
+
vec3 c = hot * front * front * 2.4 * fil;
|
|
211
|
+
c += mix(uRimColor, hot, 0.5) * shell * shell * (0.9 + 2.5 * vB.z) * fil;
|
|
212
|
+
// birth flash: the young fireball glows through
|
|
213
|
+
c += uColor * vB.y * (front * 2.5 + 0.9 * mu * mu);
|
|
214
|
+
c *= vA.w * uIntensity * uOpacity;
|
|
215
|
+
if (max(c.r, max(c.g, c.b)) < 0.002) discard;
|
|
216
|
+
gl_FragColor = vec4(c, 1.0);
|
|
77
217
|
}
|
|
78
218
|
`;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
219
|
+
// Wilson cloud: the rarefaction behind the front cools humid air below its dew point
|
|
220
|
+
// for a moment. The haze forms just behind the shock, then evaporates from the thin
|
|
221
|
+
// parts first (rising coverage threshold) as the pressure recovers.
|
|
222
|
+
const hazeFrag = /* glsl */ `
|
|
223
|
+
uniform vec3 uColor;
|
|
224
|
+
uniform float uOpacity;
|
|
225
|
+
uniform float uCondensation;
|
|
226
|
+
uniform float uShape;
|
|
227
|
+
varying vec3 vN;
|
|
228
|
+
varying vec3 vView;
|
|
229
|
+
varying vec3 vDir;
|
|
230
|
+
varying vec4 vA;
|
|
231
|
+
varying vec4 vB;
|
|
232
|
+
${NOISE3}
|
|
233
|
+
void main(){
|
|
234
|
+
float tau = vA.x;
|
|
235
|
+
float life = uCondensation * smoothstep(0.02, 0.09, tau) * (1.0 - smoothstep(0.14, 0.42, tau));
|
|
236
|
+
if (vA.w <= 0.0 || life < 0.002) discard;
|
|
237
|
+
float ground = 1.0 - step(0.5, uShape);
|
|
238
|
+
if (ground > 0.5 && vDir.y < -0.01) discard;
|
|
239
|
+
float n = fbm3(vDir * 2.3 + vec3(vB.x * 11.0, 1.0, vB.x * 5.0), 3);
|
|
240
|
+
float th = mix(0.3, 0.62, smoothstep(0.06, 0.42, tau));
|
|
241
|
+
float dens = smoothstep(th, th + 0.2, n);
|
|
242
|
+
float mu = clamp(abs(dot(normalize(vN), normalize(vView))), 0.0, 1.0);
|
|
243
|
+
float limb = min(1.0 / max(mu, 0.2), 3.0) * mix(0.45, 0.3, step(0.5, uShape));
|
|
244
|
+
float a = life * dens * limb * 0.3 * uOpacity * vA.w;
|
|
245
|
+
// the dome sits on the ground: fade its skirt in over a short height
|
|
246
|
+
a *= mix(1.0, smoothstep(-0.01, 0.12, vDir.y), ground);
|
|
247
|
+
if (a < 0.003) discard;
|
|
248
|
+
vec3 col = vec3(0.8, 0.84, 0.9) * (0.55 + 0.45 * clamp(vDir.y, 0.0, 1.0)) + uColor * vB.y * 0.6;
|
|
249
|
+
gl_FragColor = vec4(col * a, a);
|
|
84
250
|
}
|
|
85
251
|
`;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
252
|
+
// Dust skirt: a grain at r0 is hit when R(t0) = r0, i.e. tau0 = r0^(1/p). It is thrown
|
|
253
|
+
// up and out with a velocity proportional to the front speed at that moment, then
|
|
254
|
+
// follows a ballistic path with linear drag and settles; fine grains hang longer.
|
|
255
|
+
const dustVert = /* glsl */ `
|
|
256
|
+
attribute vec4 aDust; // slot, r0 (0..1), azimuth, visibility rank
|
|
257
|
+
attribute vec4 aDust2; // rand, rand, cos(polar), fine (0/1)
|
|
258
|
+
uniform float uDust;
|
|
259
|
+
uniform vec3 uRimColor;
|
|
260
|
+
${waveCommon}
|
|
261
|
+
varying float vAlpha;
|
|
262
|
+
varying vec3 vCol;
|
|
263
|
+
varying float vSoft;
|
|
264
|
+
void main(){
|
|
265
|
+
int i = int(aDust.x + 0.5);
|
|
266
|
+
vec4 A = uWaveA[i];
|
|
267
|
+
vec4 B = uWaveB[i];
|
|
268
|
+
float p = mix(1.0, 0.4, uDeceleration);
|
|
269
|
+
float Rmax = uExpansionRadius * uBound;
|
|
270
|
+
float r0min = min(1.05 / max(uExpansionRadius, 1.1), 0.9);
|
|
271
|
+
float r0 = sqrt(mix(r0min * r0min, 0.94, aDust.y));
|
|
272
|
+
float tau0 = pow(r0, 1.0 / p);
|
|
273
|
+
float L = max(B.w, 0.1);
|
|
274
|
+
float s = (A.x - tau0) * L;
|
|
275
|
+
float fine = aDust2.w;
|
|
276
|
+
float v0 = min(p * pow(max(tau0, 1e-3), p - 1.0), 3.0) * Rmax / L;
|
|
277
|
+
float az = aDust.z;
|
|
278
|
+
vec3 pos;
|
|
279
|
+
if (uShape < 0.5){
|
|
280
|
+
float k = mix(1.4, 2.6, fine);
|
|
281
|
+
float g = mix(2.0, 0.4, fine) * uBound;
|
|
282
|
+
float vx = v0 * (0.12 + 0.3 * aDust2.x);
|
|
283
|
+
float vy = v0 * (0.3 + 1.0 * aDust2.y * aDust2.y) * mix(1.0, 0.7, fine);
|
|
284
|
+
float e = (1.0 - exp(-k * max(s, 0.0))) / k;
|
|
285
|
+
float rh = r0 * Rmax + vx * e;
|
|
286
|
+
float y = max((vy + g / k) * e - g * max(s, 0.0) / k, 0.0);
|
|
287
|
+
pos = vec3(cos(az) * rh, y, sin(az) * rh);
|
|
288
|
+
} else {
|
|
289
|
+
// snowplough: the grain is swept into the shell and rides just behind the front
|
|
290
|
+
float R = A.y * Rmax;
|
|
291
|
+
float sp = sqrt(max(1.0 - aDust2.z * aDust2.z, 0.0));
|
|
292
|
+
vec3 dir = vec3(sp * cos(az), aDust2.z, sp * sin(az));
|
|
293
|
+
float rr = max(r0 * Rmax, R * (0.8 + 0.14 * aDust2.x));
|
|
294
|
+
pos = dir * rr;
|
|
295
|
+
}
|
|
296
|
+
float on = step(0.0, s) * step(aDust.w, uDust) * A.w;
|
|
297
|
+
float fade = smoothstep(0.0, 0.1, s) * exp(-s / (L * mix(0.2, 0.36, fine)));
|
|
298
|
+
vAlpha = on * fade * mix(0.55, 0.26, fine) * (0.5 + 0.5 * uDust);
|
|
299
|
+
vSoft = fine;
|
|
300
|
+
// dusty brown, lit for a moment by the hot gas that just passed
|
|
301
|
+
vec3 dustC = mix(vec3(0.54, 0.42, 0.31), vec3(0.66, 0.58, 0.5), fine * 0.6 + aDust2.x * 0.3);
|
|
302
|
+
vCol = dustC + uRimColor * exp(-s * 4.0) * B.z * 1.2;
|
|
303
|
+
vec4 mv = modelViewMatrix * vec4(pos, 1.0);
|
|
304
|
+
float size = mix(0.9 + 1.1 * aDust2.y, 6.0 + 5.0 * aDust2.x, fine) * (1.0 + 0.8 * fine * min(s / L, 1.0));
|
|
305
|
+
gl_PointSize = vAlpha > 0.001 ? size * (18.0 * uBound / max(-mv.z, 0.05)) : 0.0;
|
|
306
|
+
gl_Position = projectionMatrix * mv;
|
|
307
|
+
}
|
|
308
|
+
`;
|
|
309
|
+
const dustFrag = /* glsl */ `
|
|
310
|
+
uniform float uOpacity;
|
|
311
|
+
varying float vAlpha;
|
|
312
|
+
varying vec3 vCol;
|
|
313
|
+
varying float vSoft;
|
|
314
|
+
void main(){
|
|
315
|
+
float d = length(gl_PointCoord - 0.5) * 2.0;
|
|
316
|
+
if (d > 1.0 || vAlpha < 0.001) discard;
|
|
317
|
+
float g = mix(exp(-d * d * 5.0), exp(-d * d * 2.5) * (1.0 - d), vSoft) * (1.0 - smoothstep(0.75, 1.0, d));
|
|
318
|
+
float a = g * vAlpha * uOpacity;
|
|
319
|
+
if (a < 0.002) discard;
|
|
320
|
+
gl_FragColor = vec4(vCol * a, a);
|
|
321
|
+
}
|
|
322
|
+
`;
|
|
323
|
+
function mulberry32(seed) {
|
|
324
|
+
return () => {
|
|
325
|
+
seed |= 0;
|
|
326
|
+
seed = (seed + 0x6d2b79f5) | 0;
|
|
327
|
+
let r = Math.imul(seed ^ (seed >>> 15), 1 | seed);
|
|
328
|
+
r = (r + Math.imul(r ^ (r >>> 7), 61 | r)) ^ r;
|
|
329
|
+
return ((r ^ (r >>> 14)) >>> 0) / 4294967296;
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
function smooth01(e0, e1, x) {
|
|
333
|
+
const t = Math.min(Math.max((x - e0) / (e1 - e0), 0), 1);
|
|
334
|
+
return t * t * (3 - 2 * t);
|
|
335
|
+
}
|
|
336
|
+
const DUST_PER_SLOT = 1600;
|
|
337
|
+
function buildDustGeometry() {
|
|
338
|
+
const n = DUST_PER_SLOT * 4;
|
|
339
|
+
const rand = mulberry32(31);
|
|
340
|
+
const a1 = new Float32Array(n * 4);
|
|
341
|
+
const a2 = new Float32Array(n * 4);
|
|
342
|
+
for (let i = 0; i < n; i++) {
|
|
343
|
+
a1[i * 4] = i % 4;
|
|
344
|
+
a1[i * 4 + 1] = rand();
|
|
345
|
+
a1[i * 4 + 2] = rand() * Math.PI * 2;
|
|
346
|
+
a1[i * 4 + 3] = rand() * 0.999;
|
|
347
|
+
a2[i * 4] = rand();
|
|
348
|
+
a2[i * 4 + 1] = rand();
|
|
349
|
+
a2[i * 4 + 2] = rand() * 2 - 1;
|
|
350
|
+
a2[i * 4 + 3] = rand() < 0.55 ? 1 : 0;
|
|
351
|
+
}
|
|
352
|
+
const geo = new THREE.BufferGeometry();
|
|
353
|
+
geo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(n * 3), 3));
|
|
354
|
+
geo.setAttribute('aDust', new THREE.BufferAttribute(a1, 4));
|
|
355
|
+
geo.setAttribute('aDust2', new THREE.BufferAttribute(a2, 4));
|
|
356
|
+
return geo;
|
|
357
|
+
}
|
|
358
|
+
function buildShellGeometry() {
|
|
359
|
+
const sph = new THREE.SphereGeometry(1, 72, 48);
|
|
360
|
+
const geo = new THREE.InstancedBufferGeometry();
|
|
361
|
+
geo.setIndex(sph.getIndex());
|
|
362
|
+
geo.setAttribute('position', sph.getAttribute('position'));
|
|
363
|
+
geo.setAttribute('normal', sph.getAttribute('normal'));
|
|
364
|
+
geo.setAttribute('aSlot', new THREE.InstancedBufferAttribute(new Float32Array([0, 1, 2, 3]), 1));
|
|
365
|
+
geo.instanceCount = 4;
|
|
366
|
+
sph.dispose();
|
|
367
|
+
return geo;
|
|
368
|
+
}
|
|
369
|
+
function createUniforms() {
|
|
370
|
+
return {
|
|
371
|
+
uTime: { value: 0 },
|
|
372
|
+
uSpeed: { value: 1 },
|
|
373
|
+
uIntensity: { value: 1 },
|
|
374
|
+
uOpacity: { value: 0.8 },
|
|
375
|
+
uColor: { value: new THREE.Color('#ffffff') },
|
|
376
|
+
uRimColor: { value: new THREE.Color('#88ccff') },
|
|
377
|
+
uRingCount: { value: 2 },
|
|
378
|
+
uExpansionRadius: { value: 3 },
|
|
379
|
+
uThickness: { value: 0.04 },
|
|
380
|
+
uIntervalSeconds: { value: 2 },
|
|
381
|
+
uShape: { value: 0 },
|
|
382
|
+
uDeceleration: { value: 0.85 },
|
|
383
|
+
uInstability: { value: 0.4 },
|
|
384
|
+
uDust: { value: 0.5 },
|
|
385
|
+
uCondensation: { value: 0.3 },
|
|
386
|
+
uBound: { value: 1 },
|
|
387
|
+
uWaveA: { value: [new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4()] },
|
|
388
|
+
uWaveB: { value: [new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4(), new THREE.Vector4()] },
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
function applyConfig(u, c, bound, env) {
|
|
392
|
+
u.uSpeed.value = c.speed;
|
|
393
|
+
u.uIntensity.value = c.intensity;
|
|
394
|
+
u.uOpacity.value = c.opacity;
|
|
395
|
+
u.uColor.value.set(c.color);
|
|
396
|
+
u.uRimColor.value.set(c.rimColor);
|
|
397
|
+
u.uRingCount.value = c.ringCount;
|
|
398
|
+
u.uExpansionRadius.value = c.expansionRadius;
|
|
399
|
+
u.uThickness.value = c.thickness;
|
|
400
|
+
u.uIntervalSeconds.value = c.intervalSeconds;
|
|
401
|
+
u.uShape.value = c.shape;
|
|
402
|
+
u.uDeceleration.value = c.deceleration;
|
|
403
|
+
u.uInstability.value = c.instability;
|
|
404
|
+
u.uDust.value = c.dust;
|
|
405
|
+
u.uCondensation.value = c.condensation;
|
|
406
|
+
u.uBound.value = bound;
|
|
407
|
+
}
|
|
408
|
+
function build(u, c, bound, env) {
|
|
409
|
+
const add = { transparent: true, depthWrite: false, blending: THREE.AdditiveBlending };
|
|
410
|
+
const premul = { transparent: true, depthWrite: false, blending: THREE.NormalBlending, premultipliedAlpha: true };
|
|
411
|
+
const discMat = new THREE.ShaderMaterial({ vertexShader: discVert, fragmentShader: discFrag, uniforms: u, side: THREE.DoubleSide, ...add });
|
|
412
|
+
const shellU = { ...u, uHaze: { value: 0 } };
|
|
413
|
+
const hazeU = { ...u, uHaze: { value: 1 } };
|
|
414
|
+
const shellMat = new THREE.ShaderMaterial({ vertexShader: sphereVert, fragmentShader: shellFrag, uniforms: shellU, ...add });
|
|
415
|
+
const hazeMat = new THREE.ShaderMaterial({ vertexShader: sphereVert, fragmentShader: hazeFrag, uniforms: hazeU, ...premul });
|
|
416
|
+
const dustMat = new THREE.ShaderMaterial({ vertexShader: dustVert, fragmentShader: dustFrag, uniforms: u, ...premul });
|
|
417
|
+
const quad = new THREE.PlaneGeometry(2, 2);
|
|
418
|
+
const shellGeo = buildShellGeometry();
|
|
419
|
+
const dustGeo = buildDustGeometry();
|
|
420
|
+
const disc = new THREE.Mesh(quad, discMat);
|
|
421
|
+
const shell = new THREE.Mesh(shellGeo, shellMat);
|
|
422
|
+
const haze = new THREE.Mesh(shellGeo, hazeMat);
|
|
423
|
+
const dust = new THREE.Points(dustGeo, dustMat);
|
|
424
|
+
disc.frustumCulled = false;
|
|
425
|
+
shell.frustumCulled = false;
|
|
426
|
+
haze.frustumCulled = false;
|
|
427
|
+
dust.frustumCulled = false;
|
|
428
|
+
disc.renderOrder = 0;
|
|
429
|
+
shell.renderOrder = 0;
|
|
430
|
+
haze.renderOrder = 1;
|
|
431
|
+
dust.renderOrder = 2;
|
|
432
|
+
const group = new THREE.Group();
|
|
433
|
+
group.add(disc, shell, haze, dust);
|
|
434
|
+
// CPU clock: one launch every intervalSeconds, each front lives ringCount intervals
|
|
435
|
+
const ages = new Float32Array(4);
|
|
436
|
+
const seeds = new Float32Array(4);
|
|
437
|
+
const alive = [false, false, false, false];
|
|
438
|
+
let clock = 0;
|
|
439
|
+
let first = true;
|
|
440
|
+
let launches = 0;
|
|
441
|
+
function setVisibility() {
|
|
442
|
+
const sphere = u.uShape.value >= 0.5;
|
|
443
|
+
disc.visible = !sphere;
|
|
444
|
+
shell.visible = sphere;
|
|
445
|
+
haze.visible = u.uCondensation.value > 0.001;
|
|
446
|
+
dust.visible = u.uDust.value > 0.001;
|
|
447
|
+
}
|
|
448
|
+
return {
|
|
449
|
+
object: group,
|
|
450
|
+
materials: [discMat, shellMat, hazeMat, dustMat],
|
|
451
|
+
apply(c2) {
|
|
452
|
+
setVisibility();
|
|
126
453
|
},
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (state_.cooldown > 0) {
|
|
141
|
-
state_.cooldown -= delta;
|
|
142
|
-
mat.uniforms.uProgress.value = 1;
|
|
143
|
-
fMat.uniforms.uProgress.value = 1;
|
|
144
|
-
continue;
|
|
454
|
+
update(dt, camera) {
|
|
455
|
+
const dts = dt * u.uSpeed.value;
|
|
456
|
+
u.uTime.value += dts;
|
|
457
|
+
const n = Math.min(Math.max(Math.round(u.uRingCount.value), 1), 4);
|
|
458
|
+
const interval = Math.max(0.05, u.uIntervalSeconds.value);
|
|
459
|
+
const life = interval * n;
|
|
460
|
+
const p = 1 - 0.6 * Math.min(Math.max(u.uDeceleration.value, 0), 1);
|
|
461
|
+
for (let i = 0; i < 4; i++) {
|
|
462
|
+
if (!alive[i])
|
|
463
|
+
continue;
|
|
464
|
+
ages[i] += dts;
|
|
465
|
+
if (ages[i] >= life || i >= n)
|
|
466
|
+
alive[i] = false;
|
|
145
467
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
468
|
+
clock += dts;
|
|
469
|
+
if (first || clock >= interval) {
|
|
470
|
+
clock = first ? 0 : Math.min(clock - interval, interval);
|
|
471
|
+
first = false;
|
|
472
|
+
let slot = -1;
|
|
473
|
+
for (let i = 0; i < n && slot < 0; i++)
|
|
474
|
+
if (!alive[i])
|
|
475
|
+
slot = i;
|
|
476
|
+
if (slot < 0) {
|
|
477
|
+
slot = 0;
|
|
478
|
+
for (let i = 1; i < n; i++)
|
|
479
|
+
if (ages[i] > ages[slot])
|
|
480
|
+
slot = i;
|
|
481
|
+
}
|
|
482
|
+
alive[slot] = true;
|
|
483
|
+
ages[slot] = 0;
|
|
484
|
+
seeds[slot] = (launches * 0.6180339887 + 0.137) % 1;
|
|
485
|
+
launches++;
|
|
153
486
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
487
|
+
for (let i = 0; i < 4; i++) {
|
|
488
|
+
const A = u.uWaveA.value[i];
|
|
489
|
+
const B = u.uWaveB.value[i];
|
|
490
|
+
if (!alive[i]) {
|
|
491
|
+
A.set(0, 0, 0, 0);
|
|
492
|
+
B.set(0, 0, 0, life);
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
const tau = Math.min(ages[i] / life, 1);
|
|
496
|
+
const f = Math.pow(tau, p);
|
|
497
|
+
const v = Math.min(p * Math.pow(Math.max(tau, 1e-3), p - 1), 6);
|
|
498
|
+
const heat = (v * v) / (v * v + 1.2);
|
|
499
|
+
const fade = 1 - smooth01(0.55, 1, tau);
|
|
500
|
+
const flash = Math.exp(-ages[i] / 0.16);
|
|
501
|
+
A.set(tau, f, v, fade);
|
|
502
|
+
B.set(seeds[i], flash, heat, life);
|
|
503
|
+
}
|
|
504
|
+
setVisibility();
|
|
505
|
+
},
|
|
506
|
+
dispose() {
|
|
507
|
+
quad.dispose();
|
|
508
|
+
shellGeo.dispose();
|
|
509
|
+
dustGeo.dispose();
|
|
510
|
+
discMat.dispose();
|
|
511
|
+
shellMat.dispose();
|
|
512
|
+
hazeMat.dispose();
|
|
513
|
+
dustMat.dispose();
|
|
514
|
+
},
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
/** Defaults for every knob, so an older saved config still renders. */
|
|
518
|
+
const DEFAULTS = {
|
|
519
|
+
intensity: 1,
|
|
520
|
+
speed: 1,
|
|
521
|
+
color: '#ffffff',
|
|
522
|
+
opacity: 0.8,
|
|
523
|
+
ringCount: 2,
|
|
524
|
+
expansionRadius: 3,
|
|
525
|
+
thickness: 0.04,
|
|
526
|
+
intervalSeconds: 2,
|
|
527
|
+
rimColor: '#88ccff',
|
|
528
|
+
shape: 0,
|
|
529
|
+
deceleration: 0.85,
|
|
530
|
+
instability: 0.4,
|
|
531
|
+
dust: 0.5,
|
|
532
|
+
condensation: 0.3,
|
|
533
|
+
};
|
|
534
|
+
const ShockwaveEffect = ({ boundingRadius, config, animations, hasHost = false, onObjectReady, onObjectDispose }) => {
|
|
535
|
+
const rootRef = useRef(null);
|
|
536
|
+
const animCache = useRef({});
|
|
537
|
+
useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
|
|
538
|
+
// a standalone effect gets boundingRadius 1
|
|
539
|
+
const b = boundingRadius > 0 ? boundingRadius : 1;
|
|
540
|
+
const cfg = useMemo(() => ({ ...DEFAULTS, ...config }), [config]);
|
|
541
|
+
const env = useMemo(() => ({ hasHost: hasHost }), [hasHost]);
|
|
542
|
+
// uniforms outlive the objects, so keyframe animations survive a rebuild
|
|
543
|
+
const uniforms = useMemo(() => createUniforms(), []);
|
|
544
|
+
// rebuilt only when the host size or a structural knob changes
|
|
545
|
+
const instance = useMemo(() => build(uniforms, { ...DEFAULTS, ...config }, b, env), [uniforms, b, env]);
|
|
546
|
+
useEffect(() => () => instance.dispose(), [instance]);
|
|
547
|
+
useLayoutEffect(() => {
|
|
548
|
+
applyConfig(uniforms, cfg, b, env);
|
|
549
|
+
instance.apply(cfg);
|
|
550
|
+
}, [instance, uniforms, cfg, b, env]);
|
|
551
|
+
useFrame((state, delta) => {
|
|
552
|
+
instance.update(Math.min(delta, 0.1), state.camera);
|
|
553
|
+
if (animations?.length)
|
|
554
|
+
applyGenerativeAnimations(instance.materials, animations, animCache.current, Date.now(), false);
|
|
158
555
|
});
|
|
159
|
-
|
|
160
|
-
return (_jsx("group", { ref: rootRef, children: Array.from({ length: ringCount }, (_, i) => (_jsxs("group", { children: [_jsx("mesh", { ref: el => { meshRefs.current[i] = el; }, material: materials[i], rotation: [-Math.PI / 2, 0, 0], children: _jsx("torusGeometry", { args: [radius, thickness * intensity, 10, 80] }) }), _jsx("mesh", { ref: el => { flashRefs.current[i] = el; }, material: flashMaterials[i], rotation: [-Math.PI / 2, 0, 0], children: _jsx("circleGeometry", { args: [flashR, 48] }) })] }, i))) }));
|
|
556
|
+
return (_jsx("group", { ref: rootRef, children: _jsx("primitive", { object: instance.object }) }));
|
|
161
557
|
};
|
|
162
558
|
export default ShockwaveEffect;
|