@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
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } 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
|
+
* Glowing trails of a strange attractor (Lorenz, Aizawa or Thomas), integrated
|
|
9
|
+
* once on the CPU with RK4. The strands start 0.001 apart and visibly part
|
|
10
|
+
* company as a hot comet head runs along them: sensitive dependence on
|
|
11
|
+
* initial conditions, made visible.
|
|
12
|
+
*/
|
|
13
|
+
const ATTRACTOR_SAMPLES = 1400;
|
|
14
|
+
function buildAttractor(system, strands, chaos) {
|
|
15
|
+
const sys = Math.round(Math.min(Math.max(system, 0), 2));
|
|
16
|
+
const count = Math.round(Math.min(Math.max(strands, 1), 12));
|
|
17
|
+
const k = Math.min(Math.max(chaos, 0), 1);
|
|
18
|
+
let dt, sub, start, zUp, f;
|
|
19
|
+
if (sys === 0) {
|
|
20
|
+
// Lorenz; chaos scales rho around the textbook 28
|
|
21
|
+
const s = 10, r = 28 * (0.85 + 0.3 * k), b = 8 / 3;
|
|
22
|
+
dt = 0.005;
|
|
23
|
+
sub = 3;
|
|
24
|
+
start = [0.1, 0, 20];
|
|
25
|
+
zUp = true;
|
|
26
|
+
f = (x, y, z, o) => { o[0] = s * (y - x); o[1] = x * (r - z) - y; o[2] = x * y - b * z; };
|
|
27
|
+
}
|
|
28
|
+
else if (sys === 1) {
|
|
29
|
+
// Aizawa
|
|
30
|
+
const a = 0.95 * (0.9 + 0.2 * k), b = 0.7, c = 0.6, d = 3.5, e = 0.25, g = 0.1;
|
|
31
|
+
dt = 0.01;
|
|
32
|
+
sub = 3;
|
|
33
|
+
start = [0.1, 0, 0];
|
|
34
|
+
zUp = true;
|
|
35
|
+
f = (x, y, z, o) => {
|
|
36
|
+
o[0] = (z - b) * x - d * y;
|
|
37
|
+
o[1] = d * x + (z - b) * y;
|
|
38
|
+
o[2] = c + a * z - (z * z * z) / 3 - (x * x + y * y) * (1 + e * z) + g * z * x * x * x;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// Thomas' cyclically symmetric attractor
|
|
43
|
+
const b = 0.208186 * (1.15 - 0.3 * k);
|
|
44
|
+
dt = 0.05;
|
|
45
|
+
sub = 2;
|
|
46
|
+
start = [1.1, 1.1, -0.01];
|
|
47
|
+
zUp = false;
|
|
48
|
+
f = (x, y, z, o) => { o[0] = Math.sin(y) - b * x; o[1] = Math.sin(z) - b * y; o[2] = Math.sin(x) - b * z; };
|
|
49
|
+
}
|
|
50
|
+
const k1 = [0, 0, 0], k2 = [0, 0, 0], k3 = [0, 0, 0], k4 = [0, 0, 0];
|
|
51
|
+
const rk4 = (p) => {
|
|
52
|
+
f(p[0], p[1], p[2], k1);
|
|
53
|
+
f(p[0] + k1[0] * dt / 2, p[1] + k1[1] * dt / 2, p[2] + k1[2] * dt / 2, k2);
|
|
54
|
+
f(p[0] + k2[0] * dt / 2, p[1] + k2[1] * dt / 2, p[2] + k2[2] * dt / 2, k3);
|
|
55
|
+
f(p[0] + k3[0] * dt, p[1] + k3[1] * dt, p[2] + k3[2] * dt, k4);
|
|
56
|
+
for (let i = 0; i < 3; i++)
|
|
57
|
+
p[i] += (dt / 6) * (k1[i] + 2 * k2[i] + 2 * k3[i] + k4[i]);
|
|
58
|
+
};
|
|
59
|
+
// settle onto the attractor first, then fan the strands out from there
|
|
60
|
+
const settled = start.slice();
|
|
61
|
+
for (let i = 0; i < 3000; i++)
|
|
62
|
+
rk4(settled);
|
|
63
|
+
const N = ATTRACTOR_SAMPLES;
|
|
64
|
+
const positions = new Float32Array(count * N * 3);
|
|
65
|
+
const t = new Float32Array(count * N);
|
|
66
|
+
const min = [Infinity, Infinity, Infinity], max = [-Infinity, -Infinity, -Infinity];
|
|
67
|
+
for (let sI = 0; sI < count; sI++) {
|
|
68
|
+
const p = [settled[0] + sI * 1e-3, settled[1] + sI * 5e-4, settled[2] + sI * 2.5e-4];
|
|
69
|
+
for (let i = 0; i < N; i++) {
|
|
70
|
+
for (let j = 0; j < sub; j++)
|
|
71
|
+
rk4(p);
|
|
72
|
+
const o = (sI * N + i) * 3;
|
|
73
|
+
// Lorenz and Aizawa spin about z: stand that axis up as world Y
|
|
74
|
+
const X = p[0], Y = zUp ? p[2] : p[1], Z = zUp ? p[1] : p[2];
|
|
75
|
+
positions[o] = X;
|
|
76
|
+
positions[o + 1] = Y;
|
|
77
|
+
positions[o + 2] = Z;
|
|
78
|
+
min[0] = Math.min(min[0], X);
|
|
79
|
+
max[0] = Math.max(max[0], X);
|
|
80
|
+
min[1] = Math.min(min[1], Y);
|
|
81
|
+
max[1] = Math.max(max[1], Y);
|
|
82
|
+
min[2] = Math.min(min[2], Z);
|
|
83
|
+
max[2] = Math.max(max[2], Z);
|
|
84
|
+
t[sI * N + i] = i / (N - 1);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// normalise into a unit sphere; the shader scales by bound * scale
|
|
88
|
+
const c = [(min[0] + max[0]) / 2, (min[1] + max[1]) / 2, (min[2] + max[2]) / 2];
|
|
89
|
+
const half = Math.max(max[0] - min[0], max[1] - min[1], max[2] - min[2]) / 2 || 1;
|
|
90
|
+
for (let i = 0; i < positions.length; i += 3) {
|
|
91
|
+
positions[i] = (positions[i] - c[0]) / half;
|
|
92
|
+
positions[i + 1] = (positions[i + 1] - c[1]) / half;
|
|
93
|
+
positions[i + 2] = (positions[i + 2] - c[2]) / half;
|
|
94
|
+
}
|
|
95
|
+
// segments within each strand, never across strands
|
|
96
|
+
const index = new Uint32Array(count * (N - 1) * 2);
|
|
97
|
+
let w = 0;
|
|
98
|
+
for (let sI = 0; sI < count; sI++) {
|
|
99
|
+
for (let i = 0; i < N - 1; i++) {
|
|
100
|
+
index[w++] = sI * N + i;
|
|
101
|
+
index[w++] = sI * N + i + 1;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const position = new THREE.BufferAttribute(positions, 3);
|
|
105
|
+
const aT = new THREE.BufferAttribute(t, 1);
|
|
106
|
+
const points = new THREE.BufferGeometry();
|
|
107
|
+
points.setAttribute('position', position);
|
|
108
|
+
points.setAttribute('aT', aT);
|
|
109
|
+
const lines = new THREE.BufferGeometry();
|
|
110
|
+
lines.setAttribute('position', position);
|
|
111
|
+
lines.setAttribute('aT', aT);
|
|
112
|
+
lines.setIndex(new THREE.BufferAttribute(index, 1));
|
|
113
|
+
return { points, lines };
|
|
114
|
+
}
|
|
115
|
+
const lineVert = /* glsl */ `
|
|
116
|
+
attribute float aT;
|
|
117
|
+
uniform float uTime;
|
|
118
|
+
uniform float uTrailLength;
|
|
119
|
+
uniform float uScale;
|
|
120
|
+
uniform float uBound;
|
|
121
|
+
varying float vTrail;
|
|
122
|
+
|
|
123
|
+
void main(){
|
|
124
|
+
// every strand's head sits at the same parameter, so strands that started
|
|
125
|
+
// 0.001 apart visibly part company as the head runs along them
|
|
126
|
+
float head = fract(uTime * 0.035);
|
|
127
|
+
float behind = fract(head - aT);
|
|
128
|
+
vTrail = exp(-behind / max(uTrailLength, 1e-3));
|
|
129
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position * uScale * uBound, 1.0);
|
|
130
|
+
}
|
|
131
|
+
`;
|
|
132
|
+
const lineFrag = /* glsl */ `
|
|
133
|
+
uniform vec3 uColor;
|
|
134
|
+
uniform vec3 uHeadColor;
|
|
135
|
+
uniform float uOpacity;
|
|
136
|
+
uniform float uIntensity;
|
|
137
|
+
uniform float uGhostOpacity;
|
|
138
|
+
varying float vTrail;
|
|
139
|
+
|
|
140
|
+
void main(){
|
|
141
|
+
// the fresh end of the trail runs hot, the old end cools to the base colour
|
|
142
|
+
vec3 col = mix(uColor, uHeadColor, smoothstep(0.35, 1.0, vTrail));
|
|
143
|
+
float a = (uGhostOpacity + vTrail * 0.9) * uOpacity;
|
|
144
|
+
if (a < 0.003) discard;
|
|
145
|
+
gl_FragColor = vec4(col * uIntensity * (0.6 + vTrail), a);
|
|
146
|
+
}
|
|
147
|
+
`;
|
|
148
|
+
const pointVert = /* glsl */ `
|
|
149
|
+
attribute float aT;
|
|
150
|
+
uniform float uTime;
|
|
151
|
+
uniform float uTrailLength;
|
|
152
|
+
uniform float uScale;
|
|
153
|
+
uniform float uBound;
|
|
154
|
+
uniform float uParticleSize;
|
|
155
|
+
uniform float uIntensity;
|
|
156
|
+
varying float vTrail;
|
|
157
|
+
|
|
158
|
+
void main(){
|
|
159
|
+
float head = fract(uTime * 0.035);
|
|
160
|
+
float behind = fract(head - aT);
|
|
161
|
+
vTrail = exp(-behind / max(uTrailLength, 1e-3));
|
|
162
|
+
float s = vTrail * vTrail * vTrail;
|
|
163
|
+
vec4 mv = modelViewMatrix * vec4(position * uScale * uBound, 1.0);
|
|
164
|
+
gl_PointSize = s < 0.02 ? 0.0 : uParticleSize * uIntensity * s * (12.0 * uBound / max(-mv.z, 1e-3));
|
|
165
|
+
gl_Position = projectionMatrix * mv;
|
|
166
|
+
}
|
|
167
|
+
`;
|
|
168
|
+
const pointFrag = /* glsl */ `
|
|
169
|
+
uniform vec3 uHeadColor;
|
|
170
|
+
uniform float uOpacity;
|
|
171
|
+
varying float vTrail;
|
|
172
|
+
|
|
173
|
+
void main(){
|
|
174
|
+
float d = length(gl_PointCoord - 0.5);
|
|
175
|
+
if (d > 0.5) discard;
|
|
176
|
+
float core = smoothstep(0.5, 0.0, d);
|
|
177
|
+
gl_FragColor = vec4(uHeadColor, core * core * vTrail * uOpacity);
|
|
178
|
+
}
|
|
179
|
+
`;
|
|
180
|
+
const StrangeAttractorEffect = ({ boundingRadius, config, animations, onObjectReady, onObjectDispose }) => {
|
|
181
|
+
const { intensity, speed, color, opacity, system = 0, strands = 6, chaos = 0.5, trailLength = 0.18, scale = 1.4, ghostOpacity = 0.07, particleSize = 2.5, headColor = '#fff1c9', } = config;
|
|
182
|
+
const rootRef = useRef(null);
|
|
183
|
+
const spinRef = useRef(null);
|
|
184
|
+
const animCache = useRef({});
|
|
185
|
+
useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
|
|
186
|
+
// one uniform set, shared by the trails and the head beads
|
|
187
|
+
const uniforms = useMemo(() => ({
|
|
188
|
+
uTime: { value: 0 },
|
|
189
|
+
uSpeed: { value: 1 },
|
|
190
|
+
uIntensity: { value: 1 },
|
|
191
|
+
uOpacity: { value: 1 },
|
|
192
|
+
uColor: { value: new THREE.Color() },
|
|
193
|
+
uHeadColor: { value: new THREE.Color() },
|
|
194
|
+
uTrailLength: { value: 0.18 },
|
|
195
|
+
uScale: { value: 1.4 },
|
|
196
|
+
uBound: { value: 1 },
|
|
197
|
+
uGhostOpacity: { value: 0.07 },
|
|
198
|
+
uParticleSize: { value: 2.5 },
|
|
199
|
+
}), []);
|
|
200
|
+
const materials = useMemo(() => [
|
|
201
|
+
new THREE.ShaderMaterial({ vertexShader: lineVert, fragmentShader: lineFrag, uniforms, transparent: true, depthWrite: false, blending: THREE.AdditiveBlending }),
|
|
202
|
+
new THREE.ShaderMaterial({ vertexShader: pointVert, fragmentShader: pointFrag, uniforms, transparent: true, depthWrite: false, blending: THREE.AdditiveBlending }),
|
|
203
|
+
], [uniforms]);
|
|
204
|
+
useEffect(() => () => materials.forEach((m) => m.dispose()), [materials]);
|
|
205
|
+
// integration only reruns when the trajectory itself changes
|
|
206
|
+
const geometry = useMemo(() => buildAttractor(system, strands, chaos), [system, strands, chaos]);
|
|
207
|
+
useEffect(() => () => { geometry.points.dispose(); geometry.lines.dispose(); }, [geometry]);
|
|
208
|
+
useLayoutEffect(() => {
|
|
209
|
+
uniforms.uSpeed.value = speed;
|
|
210
|
+
uniforms.uIntensity.value = intensity;
|
|
211
|
+
uniforms.uOpacity.value = opacity;
|
|
212
|
+
uniforms.uColor.value.set(color);
|
|
213
|
+
uniforms.uHeadColor.value.set(headColor);
|
|
214
|
+
uniforms.uTrailLength.value = trailLength;
|
|
215
|
+
uniforms.uScale.value = scale;
|
|
216
|
+
uniforms.uBound.value = boundingRadius;
|
|
217
|
+
uniforms.uGhostOpacity.value = ghostOpacity;
|
|
218
|
+
uniforms.uParticleSize.value = particleSize;
|
|
219
|
+
}, [uniforms, speed, intensity, opacity, color, headColor, trailLength, scale, boundingRadius, ghostOpacity, particleSize]);
|
|
220
|
+
useFrame((_, delta) => {
|
|
221
|
+
uniforms.uTime.value += delta * uniforms.uSpeed.value;
|
|
222
|
+
if (spinRef.current)
|
|
223
|
+
spinRef.current.rotation.y += delta * 0.1 * uniforms.uSpeed.value;
|
|
224
|
+
if (animations?.length)
|
|
225
|
+
applyGenerativeAnimations(materials, animations, animCache.current, Date.now(), false);
|
|
226
|
+
});
|
|
227
|
+
return (_jsx("group", { ref: rootRef, children: _jsxs("group", { ref: spinRef, children: [_jsx("lineSegments", { geometry: geometry.lines, material: materials[0], frustumCulled: false }), _jsx("points", { geometry: geometry.points, material: materials[1], frustumCulled: false })] }) }));
|
|
228
|
+
};
|
|
229
|
+
export default StrangeAttractorEffect;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import type {
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { AnimationOptions, VolumetricFogConfig } from '@3driseai/3drise-core';
|
|
3
3
|
import type { ObjectReadyProps } from '../../types/objectReady';
|
|
4
|
-
interface VolumetricFogEffectProps extends ObjectReadyProps {
|
|
4
|
+
export interface VolumetricFogEffectProps extends ObjectReadyProps {
|
|
5
5
|
boundingRadius: number;
|
|
6
6
|
config: VolumetricFogConfig;
|
|
7
7
|
animations?: AnimationOptions[];
|
|
8
|
+
/** True when the fog surrounds a host object; the host's bounding sphere then blocks the fog behind it. */
|
|
9
|
+
hasHost?: boolean;
|
|
8
10
|
}
|
|
9
11
|
declare const VolumetricFogEffect: React.FC<VolumetricFogEffectProps>;
|
|
10
12
|
export default VolumetricFogEffect;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VolumetricFogEffect.d.ts","sourceRoot":"","sources":["../../../src/components/effects/VolumetricFogEffect.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"VolumetricFogEffect.d.ts","sourceRoot":"","sources":["../../../src/components/effects/VolumetricFogEffect.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsD,MAAM,OAAO,CAAC;AAG3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAGnF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,wBAAyB,SAAQ,gBAAgB;IAC9D,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAChC,2GAA2G;IAC3G,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAgKD,QAAA,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CAAC,wBAAwB,CAmF3D,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
|
|
@@ -1,114 +1,232 @@
|
|
|
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
|
+
* Ray-marched ground fog around the host.
|
|
9
|
+
*
|
|
10
|
+
* A box proxy is shaded per pixel: the view ray is marched through a
|
|
11
|
+
* domain-warped fBm density that thins with height, light is attenuated with
|
|
12
|
+
* Beer-Lambert extinction, each sample casts a short shadow ray toward the
|
|
13
|
+
* light, and a Henyey-Greenstein phase makes the fog glow when you look
|
|
14
|
+
* toward the light. Interleaved gradient noise jitters the samples so a few
|
|
15
|
+
* dozen steps look smooth.
|
|
16
|
+
*
|
|
17
|
+
* `particleCount` is kept for compatibility and now sets the quality: the march
|
|
18
|
+
* takes half that many steps.
|
|
19
|
+
*/
|
|
20
|
+
const PRE_NORMAL = { transparent: true, depthWrite: false, blending: THREE.NormalBlending, premultipliedAlpha: true };
|
|
8
21
|
const fogVert = /* glsl */ `
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
varying vec3 vLocal;
|
|
23
|
+
varying vec3 vCamLocal;
|
|
24
|
+
void main(){
|
|
25
|
+
vLocal = position;
|
|
26
|
+
vCamLocal = (inverse(modelMatrix) * vec4(cameraPosition, 1.0)).xyz;
|
|
27
|
+
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
30
|
+
const fogFrag = /* glsl */ `
|
|
31
|
+
uniform float uTime;
|
|
32
|
+
uniform vec3 uBoxMin;
|
|
33
|
+
uniform vec3 uBoxMax;
|
|
34
|
+
uniform float uSteps;
|
|
35
|
+
uniform float uDensity;
|
|
36
|
+
uniform float uDriftSpeed;
|
|
37
|
+
uniform float uNoiseScale;
|
|
38
|
+
uniform float uAnisotropy;
|
|
39
|
+
uniform vec3 uFogColor;
|
|
40
|
+
uniform vec3 uColor; // light colour
|
|
41
|
+
uniform float uOpacity;
|
|
42
|
+
uniform float uIntensity;
|
|
43
|
+
uniform float uHostRadius; // 0 when there is no host object to hide behind
|
|
44
|
+
varying vec3 vLocal;
|
|
45
|
+
varying vec3 vCamLocal;
|
|
22
46
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
47
|
+
|
|
48
|
+
float hash13(vec3 p3){
|
|
49
|
+
p3 = fract(p3 * 0.1031);
|
|
50
|
+
p3 += dot(p3, p3.zyx + 31.32);
|
|
51
|
+
return fract((p3.x + p3.y) * p3.z);
|
|
52
|
+
}
|
|
53
|
+
float vnoise(vec3 p){
|
|
54
|
+
vec3 i = floor(p);
|
|
55
|
+
vec3 f = fract(p);
|
|
56
|
+
f = f * f * f * (f * (f * 6.0 - 15.0) + 10.0);
|
|
57
|
+
float n000 = hash13(i);
|
|
58
|
+
float n100 = hash13(i + vec3(1.0, 0.0, 0.0));
|
|
59
|
+
float n010 = hash13(i + vec3(0.0, 1.0, 0.0));
|
|
60
|
+
float n110 = hash13(i + vec3(1.0, 1.0, 0.0));
|
|
61
|
+
float n001 = hash13(i + vec3(0.0, 0.0, 1.0));
|
|
62
|
+
float n101 = hash13(i + vec3(1.0, 0.0, 1.0));
|
|
63
|
+
float n011 = hash13(i + vec3(0.0, 1.0, 1.0));
|
|
64
|
+
float n111 = hash13(i + vec3(1.0, 1.0, 1.0));
|
|
65
|
+
return mix(mix(mix(n000, n100, f.x), mix(n010, n110, f.x), f.y),
|
|
66
|
+
mix(mix(n001, n101, f.x), mix(n011, n111, f.x), f.y), f.z);
|
|
67
|
+
}
|
|
68
|
+
// low-frequency fBm; the rotation between octaves hides the lattice
|
|
69
|
+
const mat3 FBM_ROT = mat3(0.00, 0.80, 0.60, -0.80, 0.36, -0.48, -0.60, -0.48, 0.64);
|
|
70
|
+
float fbm3(vec3 p, int octaves){
|
|
71
|
+
float a = 0.5, s = 0.0;
|
|
72
|
+
for (int i = 0; i < 5; i++){
|
|
73
|
+
if (i >= octaves) break;
|
|
74
|
+
s += a * vnoise(p);
|
|
75
|
+
p = FBM_ROT * p * 2.02;
|
|
76
|
+
a *= 0.5;
|
|
77
|
+
}
|
|
78
|
+
return s;
|
|
79
|
+
}
|
|
28
80
|
|
|
29
|
-
vec3 pos = aCenter + vec3(dx, dy, dz);
|
|
30
81
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
82
|
+
float fogDensity(vec3 p){
|
|
83
|
+
vec3 size = uBoxMax - uBoxMin;
|
|
84
|
+
float h = clamp((p.y - uBoxMin.y) / size.y, 0.0, 1.0);
|
|
85
|
+
// exponential height falloff, softened where it meets the ground
|
|
86
|
+
float layer = exp(-h * 2.4) * smoothstep(0.0, 0.06, h) * (1.0 - smoothstep(0.55, 1.0, h));
|
|
87
|
+
vec2 qc = (p.xz - 0.5 * (uBoxMin.xz + uBoxMax.xz)) / (0.5 * size.xz);
|
|
88
|
+
float radial = 1.0 - smoothstep(0.5, 1.0, length(qc));
|
|
89
|
+
if (layer * radial < 0.001) return 0.0;
|
|
90
|
+
float t = uTime * uDriftSpeed;
|
|
91
|
+
vec3 q = vec3(qc.x, h * size.y / (0.5 * size.x), qc.y) * uNoiseScale;
|
|
92
|
+
// domain warp gives rolling, folded banks instead of blobs
|
|
93
|
+
vec3 w = vec3(fbm3(q * 0.6 + vec3(t * 0.07, 0.0, 0.0), 2), fbm3(q * 0.6 + vec3(5.2, t * 0.05, 1.3), 2), 0.0);
|
|
94
|
+
float n = fbm3(q + 1.4 * w + vec3(t * 0.12, -t * 0.03, t * 0.08), 4);
|
|
95
|
+
return max(n - 0.26, 0.0) * 7.0 * layer * radial * uDensity;
|
|
96
|
+
}
|
|
34
97
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
98
|
+
float phaseHG(float cosT, float g){
|
|
99
|
+
float g2 = g * g;
|
|
100
|
+
return (1.0 - g2) / (12.566 * pow(1.0 + g2 - 2.0 * g * cosT, 1.5));
|
|
38
101
|
}
|
|
39
|
-
`;
|
|
40
|
-
const fogFrag = /* glsl */ `
|
|
41
|
-
uniform vec3 uFogColor;
|
|
42
|
-
uniform float uDensity;
|
|
43
|
-
varying float vAlpha;
|
|
44
102
|
|
|
45
|
-
void main()
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
103
|
+
void main(){
|
|
104
|
+
vec3 ro = vCamLocal;
|
|
105
|
+
vec3 rd = normalize(vLocal - vCamLocal);
|
|
106
|
+
vec3 inv = 1.0 / rd;
|
|
107
|
+
vec3 t0 = (uBoxMin - ro) * inv, t1 = (uBoxMax - ro) * inv;
|
|
108
|
+
vec3 tmn = min(t0, t1), tmx = max(t0, t1);
|
|
109
|
+
float tn = max(max(max(tmn.x, tmn.y), tmn.z), 0.0);
|
|
110
|
+
float tf = min(min(tmx.x, tmx.y), tmx.z);
|
|
111
|
+
// The host sits inside the fog; stop each ray where it meets the host's
|
|
112
|
+
// bounding sphere so fog behind the object isn't painted over it.
|
|
113
|
+
if (uHostRadius > 0.0){
|
|
114
|
+
float b = dot(ro, rd);
|
|
115
|
+
float c = dot(ro, ro) - uHostRadius * uHostRadius;
|
|
116
|
+
float disc = b * b - c;
|
|
117
|
+
if (disc > 0.0){
|
|
118
|
+
float th = -b - sqrt(disc);
|
|
119
|
+
if (th > 0.0) tf = min(tf, th);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (tf <= tn) discard;
|
|
49
123
|
|
|
50
|
-
|
|
51
|
-
float
|
|
124
|
+
float steps = clamp(floor(uSteps + 0.5), 12.0, 64.0);
|
|
125
|
+
float dt = (tf - tn) / steps;
|
|
126
|
+
// static interleaved-gradient dither: smooth banding without flicker
|
|
127
|
+
float jitter = fract(52.9829189 * fract(dot(gl_FragCoord.xy, vec2(0.06711056, 0.00583715))));
|
|
128
|
+
float t = tn + dt * jitter;
|
|
52
129
|
|
|
53
|
-
|
|
130
|
+
vec3 L = normalize(vec3(0.45, 1.0, 0.3));
|
|
131
|
+
float phase = phaseHG(dot(rd, L), uAnisotropy);
|
|
132
|
+
float sigma = 5.0 / max(uBoxMax.x - uBoxMin.x, 1e-3); // extinction scaled to the box
|
|
133
|
+
float shadowLen = (uBoxMax.y - uBoxMin.y) * 0.3;
|
|
134
|
+
|
|
135
|
+
vec3 col = vec3(0.0);
|
|
136
|
+
float T = 1.0;
|
|
137
|
+
for (int i = 0; i < 64; i++){
|
|
138
|
+
if (float(i) >= steps) break;
|
|
139
|
+
vec3 p = ro + rd * t;
|
|
140
|
+
float dens = fogDensity(p);
|
|
141
|
+
if (dens > 0.002){
|
|
142
|
+
// one shadow tap toward the light; Beer-Lambert for both paths
|
|
143
|
+
float dl = fogDensity(p + L * shadowLen);
|
|
144
|
+
float lightT = exp(-dl * sigma * shadowLen * 1.5);
|
|
145
|
+
vec3 S = uFogColor * (0.55 + uColor * lightT * phase * 9.0);
|
|
146
|
+
float ext = dens * sigma * dt;
|
|
147
|
+
float att = exp(-ext);
|
|
148
|
+
col += T * S * (1.0 - att);
|
|
149
|
+
T *= att;
|
|
150
|
+
if (T < 0.02) break;
|
|
151
|
+
}
|
|
152
|
+
t += dt;
|
|
153
|
+
}
|
|
154
|
+
float alpha = (1.0 - T) * uOpacity;
|
|
155
|
+
gl_FragColor = vec4(col * uIntensity * uOpacity, alpha);
|
|
54
156
|
}
|
|
55
157
|
`;
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
158
|
+
const _inv = new THREE.Matrix4();
|
|
159
|
+
const _cam = new THREE.Vector3();
|
|
160
|
+
const VolumetricFogEffect = ({ boundingRadius, config, animations, hasHost = false, onObjectReady, onObjectDispose }) => {
|
|
161
|
+
const { intensity = 1, speed = 0.4, color = '#fff1dc', opacity = 0.9, particleCount = 60, fogRadius = 1.8, driftSpeed = 0.3, fogColor = '#c9d2dc', density = 1, layerHeight = 1.2, anisotropy = 0.55, noiseScale = 1.5, } = config;
|
|
59
162
|
const rootRef = useRef(null);
|
|
163
|
+
const meshRef = useRef(null);
|
|
164
|
+
const insideRef = useRef(false);
|
|
165
|
+
const animCache = useRef({});
|
|
60
166
|
useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
g
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
167
|
+
// a standalone effect gets boundingRadius 1; guard against an empty host
|
|
168
|
+
const b = boundingRadius > 0 ? boundingRadius : 1;
|
|
169
|
+
const R = b * fogRadius;
|
|
170
|
+
const H = b * layerHeight;
|
|
171
|
+
const y0 = -b;
|
|
172
|
+
const uniforms = useMemo(() => ({
|
|
173
|
+
uTime: { value: 0 },
|
|
174
|
+
uSpeed: { value: 1 },
|
|
175
|
+
uIntensity: { value: 1 },
|
|
176
|
+
uOpacity: { value: 1 },
|
|
177
|
+
uColor: { value: new THREE.Color() },
|
|
178
|
+
uFogColor: { value: new THREE.Color() },
|
|
179
|
+
uDensity: { value: 1 },
|
|
180
|
+
uDriftSpeed: { value: 0.3 },
|
|
181
|
+
uNoiseScale: { value: 1.5 },
|
|
182
|
+
uAnisotropy: { value: 0.55 },
|
|
183
|
+
uSteps: { value: 30 },
|
|
184
|
+
uBoxMin: { value: new THREE.Vector3() },
|
|
185
|
+
uBoxMax: { value: new THREE.Vector3() },
|
|
186
|
+
uHostRadius: { value: 0 },
|
|
187
|
+
}), []);
|
|
188
|
+
const materials = useMemo(() => [
|
|
189
|
+
new THREE.ShaderMaterial({ vertexShader: fogVert, fragmentShader: fogFrag, uniforms, ...PRE_NORMAL }),
|
|
190
|
+
], [uniforms]);
|
|
191
|
+
useEffect(() => () => materials.forEach((m) => m.dispose()), [materials]);
|
|
192
|
+
const geometry = useMemo(() => {
|
|
193
|
+
const g = new THREE.BoxGeometry(2 * R, H, 2 * R);
|
|
194
|
+
g.translate(0, y0 + H / 2, 0);
|
|
195
|
+
return g;
|
|
196
|
+
}, [R, H, y0]);
|
|
197
|
+
useEffect(() => () => geometry.dispose(), [geometry]);
|
|
198
|
+
const box = useMemo(() => new THREE.Box3(new THREE.Vector3(-R, y0, -R), new THREE.Vector3(R, y0 + H, R)), [R, H, y0]);
|
|
199
|
+
useLayoutEffect(() => {
|
|
200
|
+
uniforms.uSpeed.value = speed;
|
|
201
|
+
uniforms.uIntensity.value = intensity;
|
|
202
|
+
uniforms.uOpacity.value = opacity;
|
|
203
|
+
uniforms.uColor.value.set(color);
|
|
204
|
+
uniforms.uFogColor.value.set(fogColor);
|
|
205
|
+
uniforms.uDensity.value = density;
|
|
206
|
+
uniforms.uDriftSpeed.value = driftSpeed;
|
|
207
|
+
uniforms.uNoiseScale.value = noiseScale;
|
|
208
|
+
uniforms.uAnisotropy.value = anisotropy;
|
|
209
|
+
uniforms.uSteps.value = Math.round(Math.min(Math.max(particleCount, 24), 128) * 0.5);
|
|
210
|
+
uniforms.uBoxMin.value.copy(box.min);
|
|
211
|
+
uniforms.uBoxMax.value.copy(box.max);
|
|
212
|
+
uniforms.uHostRadius.value = hasHost ? b * 0.97 : 0;
|
|
213
|
+
}, [uniforms, speed, intensity, opacity, color, fogColor, density, driftSpeed, noiseScale, anisotropy, particleCount, box, hasHost, b]);
|
|
214
|
+
useFrame((state, delta) => {
|
|
215
|
+
uniforms.uTime.value += delta * uniforms.uSpeed.value * 2.5;
|
|
216
|
+
const mesh = meshRef.current;
|
|
217
|
+
if (mesh) {
|
|
218
|
+
mesh.updateWorldMatrix(true, false);
|
|
219
|
+
_inv.copy(mesh.matrixWorld).invert();
|
|
220
|
+
const inside = box.containsPoint(_cam.copy(state.camera.position).applyMatrix4(_inv));
|
|
221
|
+
if (inside !== insideRef.current) {
|
|
222
|
+
insideRef.current = inside;
|
|
223
|
+
materials[0].side = inside ? THREE.BackSide : THREE.FrontSide;
|
|
224
|
+
materials[0].needsUpdate = true;
|
|
225
|
+
}
|
|
110
226
|
}
|
|
227
|
+
if (animations?.length)
|
|
228
|
+
applyGenerativeAnimations(materials, animations, animCache.current, Date.now(), false);
|
|
111
229
|
});
|
|
112
|
-
return _jsx("
|
|
230
|
+
return (_jsx("group", { ref: rootRef, children: _jsx("mesh", { ref: meshRef, geometry: geometry, material: materials[0] }) }));
|
|
113
231
|
};
|
|
114
232
|
export default VolumetricFogEffect;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { AnimationOptions, WaterCausticsConfig } from '@3driseai/3drise-core';
|
|
3
|
+
import type { ObjectReadyProps } from '../../types/objectReady';
|
|
4
|
+
export interface WaterCausticsEffectProps extends ObjectReadyProps {
|
|
5
|
+
boundingRadius: number;
|
|
6
|
+
config: WaterCausticsConfig;
|
|
7
|
+
animations?: AnimationOptions[];
|
|
8
|
+
/** True when the effect is attached to an object, so it can react to the host. */
|
|
9
|
+
hasHost?: boolean;
|
|
10
|
+
}
|
|
11
|
+
declare const WaterCausticsEffect: React.FC<WaterCausticsEffectProps>;
|
|
12
|
+
export default WaterCausticsEffect;
|
|
13
|
+
//# sourceMappingURL=WaterCausticsEffect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WaterCausticsEffect.d.ts","sourceRoot":"","sources":["../../../src/components/effects/WaterCausticsEffect.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsD,MAAM,OAAO,CAAC;AAG3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAGnF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,wBAAyB,SAAQ,gBAAgB;IAC9D,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAChC,kFAAkF;IAClF,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAkSD,QAAA,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CAAC,wBAAwB,CA+B3D,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
|