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