@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,192 +1,312 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } 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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Six-fold snow crystals growing as frost on the host.
|
|
9
|
+
*
|
|
10
|
+
* Each crystal is a flat plate, leaned off the surface like hoar frost. The
|
|
11
|
+
* fragment shader folds the plate into one 30-degree wedge (D6 symmetry) and
|
|
12
|
+
* draws a hexagonal plate or a dendrite with 60-degree side branches, blended
|
|
13
|
+
* by `habit`. Crystals grow like diffusion (R ~ sqrt(t)), hold, then
|
|
14
|
+
* sublimate, each on its own clock. Facets glint as they turn, and a cloud of
|
|
15
|
+
* diamond dust drifts around the host.
|
|
16
|
+
*/
|
|
17
|
+
const ADD = { transparent: true, depthWrite: false, blending: THREE.AdditiveBlending };
|
|
18
|
+
const DUST_COUNT = 220;
|
|
19
|
+
function mulberry32(seed) {
|
|
20
|
+
return () => {
|
|
21
|
+
seed |= 0;
|
|
22
|
+
seed = (seed + 0x6d2b79f5) | 0;
|
|
23
|
+
let r = Math.imul(seed ^ (seed >>> 15), 1 | seed);
|
|
24
|
+
r = (r + Math.imul(r ^ (r >>> 7), 61 | r)) ^ r;
|
|
25
|
+
return ((r ^ (r >>> 14)) >>> 0) / 4294967296;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function cross(a, b) { return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]]; }
|
|
29
|
+
function add(a, b) { return [a[0] + b[0], a[1] + b[1], a[2] + b[2]]; }
|
|
30
|
+
function scale(a, s) { return [a[0] * s, a[1] * s, a[2] * s]; }
|
|
31
|
+
function norm(a) { const l = Math.hypot(a[0], a[1], a[2]) || 1; return [a[0] / l, a[1] / l, a[2] / l]; }
|
|
32
|
+
/** Crystal transforms and seeds for n plates on a sphere of radius `bound`. */
|
|
33
|
+
function layoutIceCrystals(count, bound, size) {
|
|
34
|
+
const n = Math.round(Math.min(Math.max(count, 10), 60));
|
|
35
|
+
const rand = mulberry32(5);
|
|
36
|
+
const matrices = new Float32Array(n * 16);
|
|
37
|
+
const seeds = new Float32Array(n * 3);
|
|
38
|
+
const golden = Math.PI * (3 - Math.sqrt(5));
|
|
39
|
+
for (let i = 0; i < n; i++) {
|
|
40
|
+
const y = 1 - ((i + 0.5) / n) * 2;
|
|
41
|
+
const rad = Math.sqrt(1 - y * y);
|
|
42
|
+
const ph = i * golden;
|
|
43
|
+
const nrm = [Math.cos(ph) * rad, y, Math.sin(ph) * rad];
|
|
44
|
+
// a random tangent, then lean the plate 35-65 degrees off the surface like hoar frost
|
|
45
|
+
const up = Math.abs(nrm[1]) > 0.9 ? [1, 0, 0] : [0, 1, 0];
|
|
46
|
+
let t = cross(up, nrm);
|
|
47
|
+
t = norm(t);
|
|
48
|
+
const b = cross(nrm, t);
|
|
49
|
+
const spin = rand() * Math.PI * 2;
|
|
50
|
+
const tan = norm(add(scale(t, Math.cos(spin)), scale(b, Math.sin(spin))));
|
|
51
|
+
const lean = (35 + rand() * 30) * Math.PI / 180;
|
|
52
|
+
// plate normal: tilt the surface normal toward the tangent
|
|
53
|
+
const pn = norm(add(scale(nrm, Math.cos(lean)), scale(tan, Math.sin(lean))));
|
|
54
|
+
const px = norm(cross(pn, tan).map((v) => -v));
|
|
55
|
+
const py = cross(pn, px);
|
|
56
|
+
const s = bound * size * 0.32 * (0.6 + 0.8 * rand());
|
|
57
|
+
const center = add(scale(nrm, bound * 1.01 + s * 0.45 * Math.sin(lean)), scale(tan, s * 0.45 * Math.cos(lean)));
|
|
58
|
+
const m = [
|
|
59
|
+
px[0] * s, px[1] * s, px[2] * s, 0,
|
|
60
|
+
py[0] * s, py[1] * s, py[2] * s, 0,
|
|
61
|
+
pn[0] * s, pn[1] * s, pn[2] * s, 0,
|
|
62
|
+
center[0], center[1], center[2], 1,
|
|
63
|
+
];
|
|
64
|
+
matrices.set(m, i * 16);
|
|
65
|
+
seeds[i * 3] = rand();
|
|
66
|
+
seeds[i * 3 + 1] = rand();
|
|
67
|
+
seeds[i * 3 + 2] = rand();
|
|
68
|
+
}
|
|
69
|
+
return { count: n, matrices, seeds };
|
|
70
|
+
}
|
|
71
|
+
function seedTriples(count, seed) {
|
|
72
|
+
const rand = mulberry32(seed);
|
|
73
|
+
const out = new Float32Array(count * 3);
|
|
74
|
+
for (let i = 0; i < out.length; i++)
|
|
75
|
+
out[i] = rand();
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
const iceVert = /* glsl */ `
|
|
79
|
+
attribute vec3 aSeed; // x: shape seed, y: phase, z: size jitter
|
|
80
|
+
uniform float uTime;
|
|
81
|
+
uniform float uPulseSpeed;
|
|
82
|
+
varying vec2 vUv;
|
|
83
|
+
varying vec3 vNormalW;
|
|
84
|
+
varying vec3 vViewW;
|
|
85
|
+
varying vec3 vTangentW;
|
|
86
|
+
varying vec3 vSeed;
|
|
87
|
+
varying float vGrowth;
|
|
88
|
+
void main(){
|
|
89
|
+
vUv = uv * 2.0 - 1.0;
|
|
90
|
+
vSeed = aSeed;
|
|
91
|
+
// Grow, hold, sublimate. Diffusion-limited growth: radius ~ sqrt(t).
|
|
92
|
+
float cyc = fract(uTime * uPulseSpeed * 0.06 + aSeed.y);
|
|
93
|
+
float grow = sqrt(clamp(cyc / 0.55, 0.0, 1.0));
|
|
94
|
+
float fade = 1.0 - smoothstep(0.82, 1.0, cyc);
|
|
95
|
+
vGrowth = grow * fade;
|
|
96
|
+
mat4 m = modelMatrix * instanceMatrix;
|
|
97
|
+
vec4 wp = m * vec4(position, 1.0);
|
|
98
|
+
vNormalW = normalize(mat3(m) * vec3(0.0, 0.0, 1.0));
|
|
99
|
+
vTangentW = normalize(mat3(m) * vec3(1.0, 0.0, 0.0));
|
|
100
|
+
vViewW = normalize(cameraPosition - wp.xyz);
|
|
101
|
+
gl_Position = projectionMatrix * viewMatrix * wp;
|
|
15
102
|
}
|
|
16
103
|
`;
|
|
17
|
-
const
|
|
104
|
+
const iceFrag = /* glsl */ `
|
|
18
105
|
uniform vec3 uIceColor;
|
|
106
|
+
uniform float uHabit;
|
|
107
|
+
uniform float uBranches;
|
|
108
|
+
uniform float uSparkle;
|
|
19
109
|
uniform float uOpacity;
|
|
20
110
|
uniform float uIntensity;
|
|
21
|
-
varying
|
|
22
|
-
varying vec3
|
|
111
|
+
varying vec2 vUv;
|
|
112
|
+
varying vec3 vNormalW;
|
|
113
|
+
varying vec3 vViewW;
|
|
114
|
+
varying vec3 vTangentW;
|
|
115
|
+
varying vec3 vSeed;
|
|
116
|
+
varying float vGrowth;
|
|
117
|
+
|
|
118
|
+
const float PI = 3.14159265359;
|
|
119
|
+
|
|
120
|
+
float h1(float n){ return fract(sin(n * 12.9898) * 43758.5453); }
|
|
121
|
+
float sdSeg(vec2 p, vec2 a, vec2 b){
|
|
122
|
+
vec2 pa = p - a, ba = b - a;
|
|
123
|
+
float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
|
|
124
|
+
return length(pa - ba * h);
|
|
125
|
+
}
|
|
126
|
+
float sdHexagon(vec2 p, float r){
|
|
127
|
+
const vec3 k = vec3(-0.866025404, 0.5, 0.577350269);
|
|
128
|
+
p = abs(p);
|
|
129
|
+
p -= 2.0 * min(dot(k.xy, p), 0.0) * k.xy;
|
|
130
|
+
p -= vec2(clamp(p.x, -k.z * r, k.z * r), r);
|
|
131
|
+
return length(p) * sign(p.y);
|
|
132
|
+
}
|
|
133
|
+
vec3 hue(float h){ return clamp(abs(mod(h * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0); }
|
|
23
134
|
|
|
24
|
-
void main()
|
|
25
|
-
|
|
26
|
-
|
|
135
|
+
void main(){
|
|
136
|
+
float R = vGrowth * 0.92;
|
|
137
|
+
if (R < 0.01) discard;
|
|
138
|
+
vec2 p = vUv;
|
|
139
|
+
float r = length(p);
|
|
140
|
+
if (r > 1.0) discard;
|
|
27
141
|
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
|
|
142
|
+
// Six-fold symmetry: fold into a 30 degree wedge with the arm along +x.
|
|
143
|
+
float a = atan(p.y, p.x);
|
|
144
|
+
a = abs(mod(a + PI / 6.0, PI / 3.0) - PI / 6.0);
|
|
145
|
+
vec2 q = r * vec2(cos(a), sin(a));
|
|
146
|
+
float sector = floor(mod(atan(p.y, p.x) + PI / 6.0, 2.0 * PI) / (PI / 3.0));
|
|
31
147
|
|
|
32
|
-
//
|
|
33
|
-
|
|
148
|
+
// Dendrite: a main arm with side branches at 60 degrees. Branches nearer
|
|
149
|
+
// the centre started earlier and are longer (tip-splitting instability).
|
|
150
|
+
float d = sdSeg(q, vec2(0.0), vec2(R, 0.0));
|
|
151
|
+
float nb = clamp(floor(uBranches + 0.5), 0.0, 7.0);
|
|
152
|
+
for (int i = 0; i < 7; i++){
|
|
153
|
+
float fi = float(i);
|
|
154
|
+
if (fi >= nb) break;
|
|
155
|
+
float xi = R * (0.16 + 0.74 * (fi + 0.5) / max(nb, 1.0)) + (h1(vSeed.x * 7.0 + fi) - 0.5) * 0.04;
|
|
156
|
+
float li = (R - xi) * uHabit * (0.55 + 0.45 * h1(vSeed.x * 13.0 + fi));
|
|
157
|
+
vec2 b0 = vec2(xi, 0.0);
|
|
158
|
+
vec2 b1 = b0 + li * vec2(0.5, 0.8660254);
|
|
159
|
+
d = min(d, sdSeg(q, b0, b1));
|
|
160
|
+
// third-order ticks on long branches
|
|
161
|
+
if (uHabit > 0.5 && li > 0.12){
|
|
162
|
+
vec2 m = mix(b0, b1, 0.55);
|
|
163
|
+
d = min(d, sdSeg(q, m, m + li * 0.3 * (uHabit - 0.4) * vec2(1.0, 0.0)));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// Plate habit: a hexagonal plate with a ridge, shrinking as dendrites take over.
|
|
167
|
+
float plateR = R * mix(0.62, 0.16, uHabit);
|
|
168
|
+
float plate = sdHexagon(p, plateR);
|
|
169
|
+
float ridge = abs(sdHexagon(p, plateR * 0.55));
|
|
34
170
|
|
|
35
|
-
|
|
36
|
-
float
|
|
171
|
+
float w = R * 0.032 + 0.006;
|
|
172
|
+
float fw = fwidth(d) + 1e-4;
|
|
173
|
+
float arms = 1.0 - smoothstep(w - fw, w + fw, d);
|
|
174
|
+
float fwp = fwidth(plate) + 1e-4;
|
|
175
|
+
float plateFill = 1.0 - smoothstep(-fwp, fwp, plate);
|
|
176
|
+
float plateLines = (1.0 - smoothstep(0.0, fwp * 1.5, abs(plate))) + (1.0 - smoothstep(0.0, fwp * 1.5, ridge)) * plateFill;
|
|
37
177
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
178
|
+
float ice = max(arms, plateFill * 0.35);
|
|
179
|
+
if (ice + plateLines < 0.01) discard;
|
|
180
|
+
|
|
181
|
+
// Each sector is a slightly different facet: it glints only when the
|
|
182
|
+
// reflected light lines up with the eye, so sparkles follow the view.
|
|
183
|
+
vec3 N = normalize(vNormalW);
|
|
184
|
+
vec3 T = normalize(vTangentW);
|
|
185
|
+
vec3 B = cross(N, T);
|
|
186
|
+
float sa = (h1(vSeed.x * 3.0 + sector) - 0.5) * 0.7;
|
|
187
|
+
float sb = (h1(vSeed.x * 5.0 + sector) - 0.5) * 0.7;
|
|
188
|
+
vec3 Nf = normalize(N + T * sa + B * sb);
|
|
189
|
+
vec3 V = normalize(vViewW);
|
|
190
|
+
vec3 L = normalize(vec3(0.35, 1.0, 0.45));
|
|
191
|
+
vec3 H = normalize(L + V);
|
|
192
|
+
float glint = pow(max(abs(dot(Nf, H)), 0.0), 90.0) * uSparkle * 3.0;
|
|
193
|
+
float ndv = abs(dot(N, V));
|
|
194
|
+
float fres = pow(1.0 - ndv, 2.0);
|
|
195
|
+
|
|
196
|
+
// thin edges split light into colour
|
|
197
|
+
float edge = arms * (1.0 - smoothstep(0.0, w, d));
|
|
198
|
+
vec3 disp = hue(fract(d * 9.0 + ndv * 1.3 + vSeed.x)) * edge * 0.35;
|
|
199
|
+
|
|
200
|
+
vec3 col = uIceColor * (0.35 + 0.65 * fres) * ice
|
|
201
|
+
+ uIceColor * plateLines * 0.6
|
|
202
|
+
+ disp
|
|
203
|
+
+ vec3(1.0) * glint * ice;
|
|
204
|
+
float alpha = clamp((ice * (0.35 + 0.45 * fres) + plateLines * 0.4 + glint) * uOpacity, 0.0, 1.0);
|
|
205
|
+
gl_FragColor = vec4(col * uIntensity, alpha);
|
|
41
206
|
}
|
|
42
207
|
`;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
float
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
208
|
+
const dustVert = /* glsl */ `
|
|
209
|
+
attribute vec3 aSeed;
|
|
210
|
+
uniform float uTime;
|
|
211
|
+
uniform float uBound;
|
|
212
|
+
uniform float uIntensity;
|
|
213
|
+
varying float vGlint;
|
|
214
|
+
varying float vFade;
|
|
215
|
+
void main(){
|
|
216
|
+
// diamond dust: tiny plates drifting down in a slow spiral
|
|
217
|
+
float fall = fract(aSeed.x - uTime * 0.018);
|
|
218
|
+
float y = (fall - 0.5) * 3.2 * uBound;
|
|
219
|
+
float ang = aSeed.y * 6.2832 + uTime * 0.08;
|
|
220
|
+
float rad = uBound * (1.15 + 0.9 * aSeed.z);
|
|
221
|
+
vec3 pos = vec3(cos(ang) * rad, y, sin(ang) * rad);
|
|
222
|
+
vec4 wp = modelMatrix * vec4(pos, 1.0);
|
|
223
|
+
// each plate tumbles slowly; it flashes when its face mirrors light into the eye
|
|
224
|
+
float tum = uTime * 0.35 + aSeed.z * 20.0;
|
|
225
|
+
vec3 n = normalize(vec3(sin(tum), cos(tum * 0.7 + aSeed.y * 9.0), sin(tum * 0.5 + 1.3)));
|
|
226
|
+
vec3 V = normalize(cameraPosition - wp.xyz);
|
|
227
|
+
vec3 L = normalize(vec3(0.35, 1.0, 0.45));
|
|
228
|
+
vGlint = pow(abs(dot(n, normalize(L + V))), 60.0);
|
|
229
|
+
vFade = smoothstep(0.0, 0.12, fall) * (1.0 - smoothstep(0.88, 1.0, fall));
|
|
230
|
+
vec4 mv = viewMatrix * wp;
|
|
231
|
+
gl_PointSize = (1.5 + 7.0 * vGlint) * uIntensity * (6.0 * uBound / max(-mv.z, 1e-3));
|
|
232
|
+
gl_Position = projectionMatrix * mv;
|
|
59
233
|
}
|
|
60
234
|
`;
|
|
61
|
-
const
|
|
235
|
+
const dustFrag = /* glsl */ `
|
|
236
|
+
uniform vec3 uIceColor;
|
|
62
237
|
uniform float uOpacity;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
238
|
+
uniform float uSparkle;
|
|
239
|
+
varying float vGlint;
|
|
240
|
+
varying float vFade;
|
|
241
|
+
void main(){
|
|
242
|
+
vec2 c = gl_PointCoord - 0.5;
|
|
243
|
+
float d = length(c);
|
|
244
|
+
if (d > 0.5) discard;
|
|
245
|
+
// a soft core plus a faint four-point star when it flashes
|
|
246
|
+
float core = smoothstep(0.5, 0.0, d);
|
|
247
|
+
float star = (exp(-abs(c.x) * 30.0) * exp(-c.y * c.y * 40.0) + exp(-abs(c.y) * 30.0) * exp(-c.x * c.x * 40.0)) * vGlint;
|
|
248
|
+
float a = (core * core * (0.25 + vGlint * uSparkle) + star * uSparkle) * vFade * uOpacity;
|
|
249
|
+
gl_FragColor = vec4(mix(uIceColor, vec3(1.0), 0.6 + 0.4 * vGlint), a);
|
|
69
250
|
}
|
|
70
251
|
`;
|
|
71
252
|
const IceCrystalsEffect = ({ boundingRadius, config, animations, onObjectReady, onObjectDispose }) => {
|
|
72
|
-
const { spikeCount, spikeLength, frostEnabled, iceColor, pulseSpeed,
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
useObjectReady(
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
|
|
102
|
-
// Position spikes on Fibonacci sphere, pointing radially outward
|
|
103
|
-
const tmpPos = new THREE.Vector3();
|
|
104
|
-
const tmpQuat = new THREE.Quaternion();
|
|
105
|
-
const tmpMat = new THREE.Matrix4();
|
|
106
|
-
const UP = new THREE.Vector3(0, 1, 0);
|
|
107
|
-
const goldenAngle = Math.PI * (3 - Math.sqrt(5));
|
|
108
|
-
for (let i = 0; i < N; i++) {
|
|
109
|
-
const y = 1 - (i / (N - 1)) * 2;
|
|
110
|
-
const rad = Math.sqrt(1 - y * y) * r;
|
|
111
|
-
const phi = i * goldenAngle;
|
|
112
|
-
tmpPos.set(Math.cos(phi) * rad, y * r, Math.sin(phi) * rad);
|
|
113
|
-
// Quaternion: rotate Y-up to radial direction
|
|
114
|
-
const dir = tmpPos.clone().normalize();
|
|
115
|
-
tmpQuat.setFromUnitVectors(UP, dir);
|
|
116
|
-
// Scale Y randomly for variety
|
|
117
|
-
const scaleY = 0.6 + Math.random() * 0.8;
|
|
118
|
-
tmpMat.compose(tmpPos, tmpQuat, new THREE.Vector3(1, scaleY, 1));
|
|
119
|
-
mesh.setMatrixAt(i, tmpMat);
|
|
120
|
-
}
|
|
253
|
+
const { intensity = 1, speed = 0.5, opacity = 0.9, spikeCount = 30, spikeLength = 1, frostEnabled = true, iceColor = '#9fd8ff', pulseSpeed = 0.4, habit = 0.8, branches = 5, sparkle = 1, } = config;
|
|
254
|
+
const rootRef = useRef(null);
|
|
255
|
+
const animCache = useRef({});
|
|
256
|
+
useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
|
|
257
|
+
// a standalone effect gets boundingRadius 1; guard against an empty host
|
|
258
|
+
const b = boundingRadius > 0 ? boundingRadius : 1;
|
|
259
|
+
const uniforms = useMemo(() => ({
|
|
260
|
+
uTime: { value: 0 },
|
|
261
|
+
uSpeed: { value: 1 },
|
|
262
|
+
uBound: { value: 1 },
|
|
263
|
+
uIntensity: { value: 1 },
|
|
264
|
+
uOpacity: { value: 1 },
|
|
265
|
+
uIceColor: { value: new THREE.Color() },
|
|
266
|
+
uPulseSpeed: { value: 0.4 },
|
|
267
|
+
uHabit: { value: 0.8 },
|
|
268
|
+
uBranches: { value: 5 },
|
|
269
|
+
uSparkle: { value: 1 },
|
|
270
|
+
}), []);
|
|
271
|
+
const materials = useMemo(() => [
|
|
272
|
+
new THREE.ShaderMaterial({ vertexShader: iceVert, fragmentShader: iceFrag, uniforms, ...ADD, side: THREE.DoubleSide }),
|
|
273
|
+
new THREE.ShaderMaterial({ vertexShader: dustVert, fragmentShader: dustFrag, uniforms, ...ADD }),
|
|
274
|
+
], [uniforms]);
|
|
275
|
+
useEffect(() => () => materials.forEach((m) => m.dispose()), [materials]);
|
|
276
|
+
const crystals = useMemo(() => {
|
|
277
|
+
const layout = layoutIceCrystals(spikeCount, b, spikeLength);
|
|
278
|
+
const plate = new THREE.PlaneGeometry(2, 2);
|
|
279
|
+
plate.setAttribute('aSeed', new THREE.InstancedBufferAttribute(layout.seeds, 3));
|
|
280
|
+
const mesh = new THREE.InstancedMesh(plate, materials[0], layout.count);
|
|
281
|
+
mesh.instanceMatrix.array.set(layout.matrices);
|
|
121
282
|
mesh.instanceMatrix.needsUpdate = true;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
uOpacity: { value: opacity * 0.55 },
|
|
149
|
-
},
|
|
150
|
-
transparent: true, depthWrite: false, blending: THREE.AdditiveBlending,
|
|
151
|
-
});
|
|
152
|
-
return { spikeMesh: mesh, spikeMat: sMat, frostGeo: fGeo, frostMat: fMat };
|
|
153
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
154
|
-
}, [N, r, spikeLength, iceColor, opacity, intensity]);
|
|
155
|
-
// Pulse scale animation on spikes
|
|
156
|
-
const pulseMats = useRef([]);
|
|
157
|
-
useMemo(() => {
|
|
158
|
-
const tmpMat = new THREE.Matrix4();
|
|
159
|
-
const tmpPos = new THREE.Vector3();
|
|
160
|
-
const tmpQuat = new THREE.Quaternion();
|
|
161
|
-
const tmpScale = new THREE.Vector3();
|
|
162
|
-
pulseMats.current = Array.from({ length: N }, (_, i) => {
|
|
163
|
-
spikeMesh.getMatrixAt(i, tmpMat);
|
|
164
|
-
return tmpMat.clone();
|
|
165
|
-
});
|
|
166
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
167
|
-
}, [spikeMesh, N]);
|
|
168
|
-
useFrame((state, delta) => {
|
|
169
|
-
timeRef.current += delta;
|
|
170
|
-
const t = timeRef.current;
|
|
171
|
-
frostMat.uniforms.u_time.value = t;
|
|
172
|
-
// Subtle spike pulse: scale Y slightly up/down per spike
|
|
173
|
-
const tmpMat = new THREE.Matrix4();
|
|
174
|
-
const tmpPos = new THREE.Vector3();
|
|
175
|
-
const tmpQuat = new THREE.Quaternion();
|
|
176
|
-
const tmpScale = new THREE.Vector3();
|
|
177
|
-
for (let i = 0; i < N; i++) {
|
|
178
|
-
const base = pulseMats.current[i];
|
|
179
|
-
base.decompose(tmpPos, tmpQuat, tmpScale);
|
|
180
|
-
const pulse = 0.88 + 0.12 * Math.sin(t * pulseSpeed * 1.8 + i * 0.47);
|
|
181
|
-
tmpScale.y = pulse;
|
|
182
|
-
tmpMat.compose(tmpPos, tmpQuat, tmpScale);
|
|
183
|
-
spikeMesh.setMatrixAt(i, tmpMat);
|
|
184
|
-
}
|
|
185
|
-
spikeMesh.instanceMatrix.needsUpdate = true;
|
|
186
|
-
if (animations?.length) {
|
|
187
|
-
applyGenerativeAnimations([spikeMat, frostMat], animations, startValuesCacheRef.current, Date.now(), false);
|
|
188
|
-
}
|
|
283
|
+
mesh.frustumCulled = false;
|
|
284
|
+
return mesh;
|
|
285
|
+
}, [spikeCount, b, spikeLength, materials]);
|
|
286
|
+
useEffect(() => () => { crystals.geometry.dispose(); crystals.dispose(); }, [crystals]);
|
|
287
|
+
const dustGeometry = useMemo(() => {
|
|
288
|
+
const g = new THREE.BufferGeometry();
|
|
289
|
+
g.setAttribute('position', new THREE.BufferAttribute(new Float32Array(DUST_COUNT * 3), 3));
|
|
290
|
+
g.setAttribute('aSeed', new THREE.BufferAttribute(seedTriples(DUST_COUNT, 9), 3));
|
|
291
|
+
return g;
|
|
292
|
+
}, []);
|
|
293
|
+
useEffect(() => () => dustGeometry.dispose(), [dustGeometry]);
|
|
294
|
+
useLayoutEffect(() => {
|
|
295
|
+
uniforms.uSpeed.value = speed;
|
|
296
|
+
uniforms.uBound.value = b;
|
|
297
|
+
uniforms.uIntensity.value = intensity;
|
|
298
|
+
uniforms.uOpacity.value = opacity;
|
|
299
|
+
uniforms.uIceColor.value.set(iceColor);
|
|
300
|
+
uniforms.uPulseSpeed.value = pulseSpeed;
|
|
301
|
+
uniforms.uHabit.value = habit;
|
|
302
|
+
uniforms.uBranches.value = branches;
|
|
303
|
+
uniforms.uSparkle.value = sparkle;
|
|
304
|
+
}, [uniforms, speed, b, intensity, opacity, iceColor, pulseSpeed, habit, branches, sparkle]);
|
|
305
|
+
useFrame((_, delta) => {
|
|
306
|
+
uniforms.uTime.value += delta * uniforms.uSpeed.value * 2;
|
|
307
|
+
if (animations?.length)
|
|
308
|
+
applyGenerativeAnimations(materials, animations, animCache.current, Date.now(), false);
|
|
189
309
|
});
|
|
190
|
-
return (_jsxs("group", { ref:
|
|
310
|
+
return (_jsxs("group", { ref: rootRef, children: [_jsx("primitive", { object: crystals }), _jsx("points", { geometry: dustGeometry, material: materials[1], visible: frostEnabled, frustumCulled: false })] }));
|
|
191
311
|
};
|
|
192
312
|
export default IceCrystalsEffect;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { AnimationOptions, IceHaloConfig } from '@3driseai/3drise-core';
|
|
3
|
+
import type { ObjectReadyProps } from '../../types/objectReady';
|
|
4
|
+
export interface IceHaloEffectProps extends ObjectReadyProps {
|
|
5
|
+
boundingRadius: number;
|
|
6
|
+
config: IceHaloConfig;
|
|
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 IceHaloEffect: React.FC<IceHaloEffectProps>;
|
|
12
|
+
export default IceHaloEffect;
|
|
13
|
+
//# sourceMappingURL=IceHaloEffect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IceHaloEffect.d.ts","sourceRoot":"","sources":["../../../src/components/effects/IceHaloEffect.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsD,MAAM,OAAO,CAAC;AAG3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAG7E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,kBAAmB,SAAQ,gBAAgB;IACxD,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAChC,kFAAkF;IAClF,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AA4RD,QAAA,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CA+B/C,CAAC;AAEF,eAAe,aAAa,CAAC"}
|