@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,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { AnimationOptions, GalaxyConfig } from '@3driseai/3drise-core';
|
|
3
|
+
import type { ObjectReadyProps } from '../../types/objectReady';
|
|
4
|
+
export interface GalaxyEffectProps extends ObjectReadyProps {
|
|
5
|
+
boundingRadius: number;
|
|
6
|
+
config: GalaxyConfig;
|
|
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 GalaxyEffect: React.FC<GalaxyEffectProps>;
|
|
12
|
+
export default GalaxyEffect;
|
|
13
|
+
//# sourceMappingURL=GalaxyEffect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GalaxyEffect.d.ts","sourceRoot":"","sources":["../../../src/components/effects/GalaxyEffect.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsD,MAAM,OAAO,CAAC;AAG3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAG5E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,iBAAkB,SAAQ,gBAAgB;IACvD,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAChC,kFAAkF;IAClF,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AA8VD,QAAA,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA+B7C,CAAC;AAEF,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useLayoutEffect, useMemo, useRef } from 'react';
|
|
3
|
+
import * as THREE from 'three';
|
|
4
|
+
import { useFrame } from '@react-three/fiber';
|
|
5
|
+
import { applyGenerativeAnimations } from '../../utils/handleGenerativeEffectsAnimations.js';
|
|
6
|
+
import { useObjectReady } from '../../hooks/useObjectReady.js';
|
|
7
|
+
/**
|
|
8
|
+
* A spiral galaxy from density-wave theory: nested elliptical orbits twist into arms, lit by young blue stars, dust lanes and a warm bulge. Use for space, cosmic, sci-fi scenes.
|
|
9
|
+
*
|
|
10
|
+
* Kalnajs ellipses: orbit a rotated by θ(a) = k·a · flat rotation curve Ω ∝ 1/r · arms = where the ellipses crowd
|
|
11
|
+
*/
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Density-wave spiral (Lin-Shu, drawn the way Kalnajs showed it).
|
|
14
|
+
// Every star moves on a centred ellipse whose size is a (0..1). The ellipse
|
|
15
|
+
// for size a is turned by theta(a) = armTwist * a, so neighbouring orbits are
|
|
16
|
+
// slightly rotated against each other. Where the rotated ellipses crowd
|
|
17
|
+
// together the surface density is higher, and those crowded strips form two
|
|
18
|
+
// trailing spiral arms. The stars themselves keep moving (flat rotation curve,
|
|
19
|
+
// Omega ~ 1/r) and flow through the arms; only the pattern stays, turning
|
|
20
|
+
// slowly at the pattern speed. Young, hot stars are born where the gas is
|
|
21
|
+
// compressed (in the arms) and die before they leave them, so blue light
|
|
22
|
+
// traces the arms while old stars stay smooth and warm.
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
const orbitCommon = /* glsl */ `
|
|
25
|
+
uniform float uTime;
|
|
26
|
+
uniform float uRadius;
|
|
27
|
+
uniform float uArmTwist;
|
|
28
|
+
uniform float uEllipticity;
|
|
29
|
+
uniform float uBound;
|
|
30
|
+
|
|
31
|
+
const float PATTERN_SPEED = 0.035;
|
|
32
|
+
|
|
33
|
+
// position on the disk for orbit size a and orbital phase M
|
|
34
|
+
vec2 orbitPos(float a, float M, float twistOffset){
|
|
35
|
+
float R = a * uRadius * uBound;
|
|
36
|
+
float th = a * uArmTwist + uTime * PATTERN_SPEED + twistOffset;
|
|
37
|
+
// centred ellipse, long axis along x before the twist
|
|
38
|
+
vec2 p = vec2(R * cos(M), R * (1.0 - uEllipticity) * sin(M));
|
|
39
|
+
float c = cos(th), s = sin(th);
|
|
40
|
+
return vec2(c * p.x - s * p.y, s * p.x + c * p.y);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Phase advance: flat rotation curve (v = const, Omega = v / r), softened at the centre.
|
|
44
|
+
float orbitPhase(float a, float M0){
|
|
45
|
+
return M0 + uTime * 0.22 / (a + 0.12);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Density contrast of the arm at orbit a, orbital phase M: the ellipses crowd at
|
|
49
|
+
// the ends of their minor axis (M = +-pi/2), where neighbouring orbits run closest.
|
|
50
|
+
float armness(float M){
|
|
51
|
+
return pow(0.5 + 0.5 * cos(2.0 * M - 3.14159265), 3.0);
|
|
52
|
+
}
|
|
53
|
+
`;
|
|
54
|
+
const starVert = /* glsl */ `
|
|
55
|
+
attribute vec4 aOrbit; // a (0..1), M0, kind (0 disk old, 1 disk young, 2 bulge, 3 HII knot), seed
|
|
56
|
+
attribute vec3 aExtra; // bulge position (unit gaussian) or (z offset, size, temperature)
|
|
57
|
+
uniform float uIntensity;
|
|
58
|
+
uniform float uBulgeSize;
|
|
59
|
+
${orbitCommon}
|
|
60
|
+
varying vec3 vCol;
|
|
61
|
+
varying float vAlpha;
|
|
62
|
+
varying float vSoft;
|
|
63
|
+
uniform vec3 uColor;
|
|
64
|
+
uniform vec3 uCoreColor;
|
|
65
|
+
|
|
66
|
+
void main(){
|
|
67
|
+
float a = aOrbit.x;
|
|
68
|
+
float kind = aOrbit.z;
|
|
69
|
+
float seed = aOrbit.w;
|
|
70
|
+
vec3 pos;
|
|
71
|
+
float bright;
|
|
72
|
+
float size;
|
|
73
|
+
vSoft = 0.0;
|
|
74
|
+
if (kind > 1.5 && kind < 2.5){
|
|
75
|
+
// bulge: a slowly turning oblate gaussian of old stars
|
|
76
|
+
float spin = uTime * 0.12;
|
|
77
|
+
vec3 g = aExtra * uBulgeSize * uRadius * uBound * 0.55;
|
|
78
|
+
g.y *= 0.62;
|
|
79
|
+
float c = cos(spin), s = sin(spin);
|
|
80
|
+
pos = vec3(c * g.x - s * g.z, g.y, s * g.x + c * g.z);
|
|
81
|
+
float r = length(aExtra);
|
|
82
|
+
bright = 0.55 * exp(-r * r * 0.35);
|
|
83
|
+
size = 1.1 + seed;
|
|
84
|
+
vCol = mix(uCoreColor, vec3(1.0, 0.93, 0.82), seed * 0.4);
|
|
85
|
+
} else {
|
|
86
|
+
float M = orbitPhase(a, aOrbit.y);
|
|
87
|
+
vec2 p = orbitPos(a, M, 0.0);
|
|
88
|
+
// the disk thins toward the edge and flares slightly
|
|
89
|
+
float h = aExtra.x * uRadius * uBound * (0.018 + 0.03 * exp(-a * 6.0));
|
|
90
|
+
pos = vec3(p.x, h, p.y);
|
|
91
|
+
float arm = armness(M);
|
|
92
|
+
// radial light profile of an exponential disk
|
|
93
|
+
float disk = exp(-a * 1.7);
|
|
94
|
+
if (kind < 0.5){
|
|
95
|
+
bright = 0.5 * disk * (0.4 + 0.6 * arm);
|
|
96
|
+
size = aExtra.y * 1.3;
|
|
97
|
+
// old population: warm white, a touch redder outward
|
|
98
|
+
vCol = mix(vec3(1.0, 0.9, 0.78), vec3(1.0, 0.82, 0.7), a);
|
|
99
|
+
} else if (kind < 1.5){
|
|
100
|
+
// young population: only lives in the arm it was born in
|
|
101
|
+
bright = 1.7 * disk * smoothstep(0.12, 0.9, arm) * (0.4 + 0.6 * smoothstep(0.05, 0.25, a));
|
|
102
|
+
size = aExtra.y * 1.25;
|
|
103
|
+
vCol = mix(uColor, vec3(0.85, 0.92, 1.0), aExtra.z);
|
|
104
|
+
} else {
|
|
105
|
+
// HII regions: soft pink knots of ionised hydrogen, strung along the arms
|
|
106
|
+
bright = 0.5 * disk * smoothstep(0.55, 0.95, arm);
|
|
107
|
+
size = aExtra.y * 5.0;
|
|
108
|
+
vSoft = 1.0;
|
|
109
|
+
vCol = vec3(1.0, 0.45, 0.62);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
vAlpha = bright;
|
|
113
|
+
vec4 mv = modelViewMatrix * vec4(pos, 1.0);
|
|
114
|
+
gl_PointSize = size * (0.9 + 0.4 * uIntensity) * (22.0 * uBound / max(-mv.z, 0.05));
|
|
115
|
+
gl_Position = projectionMatrix * mv;
|
|
116
|
+
}
|
|
117
|
+
`;
|
|
118
|
+
const starFrag = /* glsl */ `
|
|
119
|
+
uniform float uIntensity;
|
|
120
|
+
uniform float uOpacity;
|
|
121
|
+
varying vec3 vCol;
|
|
122
|
+
varying float vAlpha;
|
|
123
|
+
varying float vSoft;
|
|
124
|
+
void main(){
|
|
125
|
+
float d = length(gl_PointCoord - 0.5) * 2.0;
|
|
126
|
+
if (d > 1.0 || vAlpha < 0.001) discard;
|
|
127
|
+
// point-spread: a tight core and a faint halo; HII knots are all halo
|
|
128
|
+
float core = exp(-d * d * 14.0);
|
|
129
|
+
float halo = exp(-d * 3.0) * 0.3;
|
|
130
|
+
float g = mix(core + halo, exp(-d * d * 3.0) * 0.35, vSoft) * (1.0 - smoothstep(0.8, 1.0, d));
|
|
131
|
+
vec3 c = vCol * g * vAlpha * uIntensity * uOpacity;
|
|
132
|
+
gl_FragColor = vec4(c, 1.0);
|
|
133
|
+
}
|
|
134
|
+
`;
|
|
135
|
+
// Dust sits on the inner (concave) edge of each arm, where the gas is shocked
|
|
136
|
+
// before the new stars light up: the same orbits, a little behind in angle.
|
|
137
|
+
const dustVert = /* glsl */ `
|
|
138
|
+
attribute vec4 aOrbit; // a, M0, size, seed
|
|
139
|
+
${orbitCommon}
|
|
140
|
+
uniform float uDust;
|
|
141
|
+
varying float vAlpha;
|
|
142
|
+
void main(){
|
|
143
|
+
float a = aOrbit.x;
|
|
144
|
+
float M = orbitPhase(a, aOrbit.y);
|
|
145
|
+
vec2 p = orbitPos(a, M, -0.22);
|
|
146
|
+
float arm = armness(M);
|
|
147
|
+
vAlpha = uDust * smoothstep(0.45, 0.95, arm) * smoothstep(0.14, 0.32, a) * (1.0 - smoothstep(0.75, 1.0, a));
|
|
148
|
+
vec4 mv = modelViewMatrix * vec4(p.x, (aOrbit.w - 0.5) * 0.02 * uRadius * uBound, p.y, 1.0);
|
|
149
|
+
gl_PointSize = aOrbit.z * (70.0 * uBound / max(-mv.z, 0.05));
|
|
150
|
+
gl_Position = projectionMatrix * mv;
|
|
151
|
+
}
|
|
152
|
+
`;
|
|
153
|
+
const dustFrag = /* glsl */ `
|
|
154
|
+
uniform float uOpacity;
|
|
155
|
+
varying float vAlpha;
|
|
156
|
+
void main(){
|
|
157
|
+
float d = length(gl_PointCoord - 0.5) * 2.0;
|
|
158
|
+
if (d > 1.0 || vAlpha < 0.002) discard;
|
|
159
|
+
float a = exp(-d * d * 3.0) * (1.0 - d) * vAlpha * uOpacity * 0.16;
|
|
160
|
+
// premultiplied: dark reddish-brown dust that absorbs the light behind it
|
|
161
|
+
gl_FragColor = vec4(vec3(0.06, 0.035, 0.025) * a, a);
|
|
162
|
+
}
|
|
163
|
+
`;
|
|
164
|
+
const coreVert = /* glsl */ `
|
|
165
|
+
uniform float uRadius;
|
|
166
|
+
uniform float uBulgeSize;
|
|
167
|
+
uniform float uBound;
|
|
168
|
+
varying vec2 vUv;
|
|
169
|
+
void main(){
|
|
170
|
+
vUv = uv;
|
|
171
|
+
vec4 mv = modelViewMatrix * vec4(0.0, 0.0, 0.0, 1.0);
|
|
172
|
+
mv.xy += position.xy * uRadius * uBound * (uBulgeSize * 1.6 + 0.12);
|
|
173
|
+
gl_Position = projectionMatrix * mv;
|
|
174
|
+
}
|
|
175
|
+
`;
|
|
176
|
+
const coreFrag = /* glsl */ `
|
|
177
|
+
uniform vec3 uCoreColor;
|
|
178
|
+
uniform float uIntensity;
|
|
179
|
+
uniform float uOpacity;
|
|
180
|
+
varying vec2 vUv;
|
|
181
|
+
void main(){
|
|
182
|
+
float d = length(vUv - 0.5) * 2.0;
|
|
183
|
+
// de Vaucouleurs-like surface brightness: a sharp nucleus and a broad glow
|
|
184
|
+
float g = exp(-7.67 * (pow(d + 0.02, 0.25) - 0.376)) * 0.9;
|
|
185
|
+
g *= 1.0 - smoothstep(0.7, 1.0, d);
|
|
186
|
+
vec3 c = mix(uCoreColor, vec3(1.0, 0.97, 0.92), exp(-d * 12.0)) * g * uIntensity * uOpacity;
|
|
187
|
+
float lum = max(c.r, max(c.g, c.b));
|
|
188
|
+
if (lum < 0.002) discard;
|
|
189
|
+
// premultiplied: the alpha follows the light, so the quad never shows as a rectangle
|
|
190
|
+
gl_FragColor = vec4(c, min(lum, 1.0));
|
|
191
|
+
}
|
|
192
|
+
`;
|
|
193
|
+
function mulberry32(seed) {
|
|
194
|
+
return () => {
|
|
195
|
+
seed |= 0;
|
|
196
|
+
seed = (seed + 0x6d2b79f5) | 0;
|
|
197
|
+
let r = Math.imul(seed ^ (seed >>> 15), 1 | seed);
|
|
198
|
+
r = (r + Math.imul(r ^ (r >>> 7), 61 | r)) ^ r;
|
|
199
|
+
return ((r ^ (r >>> 14)) >>> 0) / 4294967296;
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function gaussian(rand) {
|
|
203
|
+
return Math.sqrt(-2 * Math.log(1 - rand() * 0.9999)) * Math.cos(Math.PI * 2 * rand());
|
|
204
|
+
}
|
|
205
|
+
/** Star attributes: an exponential disk (old + young + HII) and a gaussian bulge. */
|
|
206
|
+
function buildStars(count) {
|
|
207
|
+
const n = Math.round(Math.min(Math.max(count, 2000), 40000));
|
|
208
|
+
const rand = mulberry32(7);
|
|
209
|
+
const orbit = new Float32Array(n * 4);
|
|
210
|
+
const extra = new Float32Array(n * 3);
|
|
211
|
+
for (let i = 0; i < n; i++) {
|
|
212
|
+
const k = rand();
|
|
213
|
+
let kind = 0;
|
|
214
|
+
if (k < 0.16)
|
|
215
|
+
kind = 2;
|
|
216
|
+
else if (k < 0.5)
|
|
217
|
+
kind = 1;
|
|
218
|
+
else if (k < 0.515)
|
|
219
|
+
kind = 3;
|
|
220
|
+
// exponential disk: surface density ~ e^(-r/h); sample the radius from r e^(-r/h)
|
|
221
|
+
let a = 2;
|
|
222
|
+
for (let tries = 0; tries < 8 && a > 1; tries++)
|
|
223
|
+
a = -Math.log(rand() * rand() + 1e-6) / 3.3;
|
|
224
|
+
if (a > 1)
|
|
225
|
+
a = rand();
|
|
226
|
+
a = Math.max(a, 0.02 + rand() * 0.03);
|
|
227
|
+
orbit[i * 4] = a;
|
|
228
|
+
orbit[i * 4 + 1] = rand() * Math.PI * 2;
|
|
229
|
+
orbit[i * 4 + 2] = kind;
|
|
230
|
+
orbit[i * 4 + 3] = rand();
|
|
231
|
+
if (kind === 2) {
|
|
232
|
+
extra[i * 3] = gaussian(rand);
|
|
233
|
+
extra[i * 3 + 1] = gaussian(rand);
|
|
234
|
+
extra[i * 3 + 2] = gaussian(rand);
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
extra[i * 3] = gaussian(rand);
|
|
238
|
+
extra[i * 3 + 1] = 0.6 + Math.pow(rand(), 3) * 1.8;
|
|
239
|
+
extra[i * 3 + 2] = rand();
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
const geo = new THREE.BufferGeometry();
|
|
243
|
+
geo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(n * 3), 3));
|
|
244
|
+
geo.setAttribute('aOrbit', new THREE.BufferAttribute(orbit, 4));
|
|
245
|
+
geo.setAttribute('aExtra', new THREE.BufferAttribute(extra, 3));
|
|
246
|
+
return geo;
|
|
247
|
+
}
|
|
248
|
+
function buildDust(count) {
|
|
249
|
+
const n = Math.round(Math.min(Math.max(count, 2000), 40000) * 0.12);
|
|
250
|
+
const rand = mulberry32(19);
|
|
251
|
+
const orbit = new Float32Array(n * 4);
|
|
252
|
+
for (let i = 0; i < n; i++) {
|
|
253
|
+
orbit[i * 4] = 0.05 + Math.pow(rand(), 0.8) * 0.9;
|
|
254
|
+
orbit[i * 4 + 1] = rand() * Math.PI * 2;
|
|
255
|
+
orbit[i * 4 + 2] = 0.6 + rand() * 0.9;
|
|
256
|
+
orbit[i * 4 + 3] = rand();
|
|
257
|
+
}
|
|
258
|
+
const geo = new THREE.BufferGeometry();
|
|
259
|
+
geo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(n * 3), 3));
|
|
260
|
+
geo.setAttribute('aOrbit', new THREE.BufferAttribute(orbit, 4));
|
|
261
|
+
return geo;
|
|
262
|
+
}
|
|
263
|
+
function createUniforms() {
|
|
264
|
+
return {
|
|
265
|
+
uTime: { value: 0 },
|
|
266
|
+
uSpeed: { value: 1 },
|
|
267
|
+
uIntensity: { value: 1 },
|
|
268
|
+
uOpacity: { value: 1 },
|
|
269
|
+
uColor: { value: new THREE.Color() },
|
|
270
|
+
uCoreColor: { value: new THREE.Color() },
|
|
271
|
+
uRadius: { value: 3 },
|
|
272
|
+
uArmTwist: { value: 5.5 },
|
|
273
|
+
uEllipticity: { value: 0.32 },
|
|
274
|
+
uBulgeSize: { value: 0.22 },
|
|
275
|
+
uDust: { value: 0.6 },
|
|
276
|
+
uBound: { value: 1 },
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
function applyConfig(u, c, bound, env) {
|
|
280
|
+
u.uSpeed.value = c.speed;
|
|
281
|
+
u.uIntensity.value = c.intensity;
|
|
282
|
+
u.uOpacity.value = c.opacity;
|
|
283
|
+
u.uColor.value.set(c.color);
|
|
284
|
+
u.uCoreColor.value.set(c.coreColor);
|
|
285
|
+
u.uRadius.value = c.radius;
|
|
286
|
+
u.uArmTwist.value = c.armTwist;
|
|
287
|
+
u.uEllipticity.value = c.ellipticity;
|
|
288
|
+
u.uBulgeSize.value = c.bulgeSize;
|
|
289
|
+
u.uDust.value = c.dust;
|
|
290
|
+
u.uBound.value = bound;
|
|
291
|
+
}
|
|
292
|
+
function build(u, c, bound, env) {
|
|
293
|
+
const add = { transparent: true, depthWrite: false, blending: THREE.AdditiveBlending };
|
|
294
|
+
const starMat = new THREE.ShaderMaterial({ vertexShader: starVert, fragmentShader: starFrag, uniforms: u, ...add });
|
|
295
|
+
const dustMat = new THREE.ShaderMaterial({
|
|
296
|
+
vertexShader: dustVert, fragmentShader: dustFrag, uniforms: u,
|
|
297
|
+
transparent: true, depthWrite: false, blending: THREE.NormalBlending, premultipliedAlpha: true,
|
|
298
|
+
});
|
|
299
|
+
const coreMat = new THREE.ShaderMaterial({ vertexShader: coreVert, fragmentShader: coreFrag, uniforms: u, ...add, premultipliedAlpha: true });
|
|
300
|
+
const starGeo = buildStars(c.starCount);
|
|
301
|
+
const dustGeo = buildDust(c.starCount);
|
|
302
|
+
const quad = new THREE.PlaneGeometry(2, 2);
|
|
303
|
+
const stars = new THREE.Points(starGeo, starMat);
|
|
304
|
+
const dust = new THREE.Points(dustGeo, dustMat);
|
|
305
|
+
const core = new THREE.Mesh(quad, coreMat);
|
|
306
|
+
stars.frustumCulled = false;
|
|
307
|
+
dust.frustumCulled = false;
|
|
308
|
+
core.frustumCulled = false;
|
|
309
|
+
core.renderOrder = 0;
|
|
310
|
+
stars.renderOrder = 1;
|
|
311
|
+
dust.renderOrder = 2;
|
|
312
|
+
const disk = new THREE.Group();
|
|
313
|
+
disk.add(core, stars, dust);
|
|
314
|
+
const group = new THREE.Group();
|
|
315
|
+
group.add(disk);
|
|
316
|
+
return {
|
|
317
|
+
object: group,
|
|
318
|
+
materials: [starMat, dustMat, coreMat],
|
|
319
|
+
apply(c2) {
|
|
320
|
+
disk.rotation.set((c2.tilt * Math.PI) / 180, 0, 0);
|
|
321
|
+
},
|
|
322
|
+
update(dt, camera) {
|
|
323
|
+
u.uTime.value += dt * u.uSpeed.value;
|
|
324
|
+
},
|
|
325
|
+
dispose() {
|
|
326
|
+
starGeo.dispose();
|
|
327
|
+
dustGeo.dispose();
|
|
328
|
+
quad.dispose();
|
|
329
|
+
starMat.dispose();
|
|
330
|
+
dustMat.dispose();
|
|
331
|
+
coreMat.dispose();
|
|
332
|
+
},
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
/** Defaults for every knob, so an older saved config still renders. */
|
|
336
|
+
const DEFAULTS = {
|
|
337
|
+
intensity: 1,
|
|
338
|
+
speed: 1,
|
|
339
|
+
color: '#9fc4ff',
|
|
340
|
+
opacity: 0.9,
|
|
341
|
+
starCount: 18000,
|
|
342
|
+
radius: 3,
|
|
343
|
+
armTwist: 5.5,
|
|
344
|
+
ellipticity: 0.32,
|
|
345
|
+
bulgeSize: 0.22,
|
|
346
|
+
dust: 0.6,
|
|
347
|
+
tilt: 22,
|
|
348
|
+
coreColor: '#ffd9a8',
|
|
349
|
+
};
|
|
350
|
+
const GalaxyEffect = ({ boundingRadius, config, animations, hasHost = false, onObjectReady, onObjectDispose }) => {
|
|
351
|
+
const rootRef = useRef(null);
|
|
352
|
+
const animCache = useRef({});
|
|
353
|
+
useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
|
|
354
|
+
// a standalone effect gets boundingRadius 1
|
|
355
|
+
const b = boundingRadius > 0 ? boundingRadius : 1;
|
|
356
|
+
const cfg = useMemo(() => ({ ...DEFAULTS, ...config }), [config]);
|
|
357
|
+
const env = useMemo(() => ({ hasHost: hasHost }), [hasHost]);
|
|
358
|
+
// uniforms outlive the objects, so keyframe animations survive a rebuild
|
|
359
|
+
const uniforms = useMemo(() => createUniforms(), []);
|
|
360
|
+
// rebuilt only when the host size or a structural knob changes
|
|
361
|
+
const instance = useMemo(() => build(uniforms, { ...DEFAULTS, ...config }, b, env), [uniforms, b, env, cfg.starCount]);
|
|
362
|
+
useEffect(() => () => instance.dispose(), [instance]);
|
|
363
|
+
useLayoutEffect(() => {
|
|
364
|
+
applyConfig(uniforms, cfg, b, env);
|
|
365
|
+
instance.apply(cfg);
|
|
366
|
+
}, [instance, uniforms, cfg, b, env]);
|
|
367
|
+
useFrame((state, delta) => {
|
|
368
|
+
instance.update(Math.min(delta, 0.1), state.camera);
|
|
369
|
+
if (animations?.length)
|
|
370
|
+
applyGenerativeAnimations(instance.materials, animations, animCache.current, Date.now(), false);
|
|
371
|
+
});
|
|
372
|
+
return (_jsx("group", { ref: rootRef, children: _jsx("primitive", { object: instance.object }) }));
|
|
373
|
+
};
|
|
374
|
+
export default GalaxyEffect;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { AnimationOptions, HarmonicShellConfig } from '@3driseai/3drise-core';
|
|
3
|
+
import type { ObjectReadyProps } from '../../types/objectReady';
|
|
4
|
+
export interface HarmonicShellEffectProps extends ObjectReadyProps {
|
|
5
|
+
boundingRadius: number;
|
|
6
|
+
config: HarmonicShellConfig;
|
|
7
|
+
animations?: AnimationOptions[];
|
|
8
|
+
}
|
|
9
|
+
declare const HarmonicShellEffect: React.FC<HarmonicShellEffectProps>;
|
|
10
|
+
export default HarmonicShellEffect;
|
|
11
|
+
//# sourceMappingURL=HarmonicShellEffect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HarmonicShellEffect.d.ts","sourceRoot":"","sources":["../../../src/components/effects/HarmonicShellEffect.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,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;CACnC;AA0KD,QAAA,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CAAC,wBAAwB,CA2D3D,CAAC;AACF,eAAe,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useLayoutEffect, useMemo, useRef } from 'react';
|
|
3
|
+
import * as THREE from 'three';
|
|
4
|
+
import { useFrame } from '@react-three/fiber';
|
|
5
|
+
import { applyGenerativeAnimations } from '../../utils/handleGenerativeEffectsAnimations.js';
|
|
6
|
+
import { useObjectReady } from '../../hooks/useObjectReady.js';
|
|
7
|
+
/**
|
|
8
|
+
* An iridescent shell around the host, displaced by real spherical harmonics
|
|
9
|
+
* up to l = 4 whose weights drift slowly. The colour is thin-film
|
|
10
|
+
* interference, as on a soap bubble, and the lines where the harmonic field
|
|
11
|
+
* crosses zero glow.
|
|
12
|
+
*/
|
|
13
|
+
/** Band (l) of each of the 24 real-SH terms in the shader. */
|
|
14
|
+
const SH_BANDS = [1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4];
|
|
15
|
+
function mulberry32(seed) {
|
|
16
|
+
return () => {
|
|
17
|
+
seed |= 0;
|
|
18
|
+
seed = (seed + 0x6d2b79f5) | 0;
|
|
19
|
+
let r = Math.imul(seed ^ (seed >>> 15), 1 | seed);
|
|
20
|
+
r = (r + Math.imul(r ^ (r >>> 7), 61 | r)) ^ r;
|
|
21
|
+
return ((r ^ (r >>> 14)) >>> 0) / 4294967296;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** Fixed per-term frequencies and phases, so the same config always moves the same way. */
|
|
25
|
+
function createHarmonicState() {
|
|
26
|
+
const rand = mulberry32(7);
|
|
27
|
+
const f1 = new Float32Array(24), f2 = new Float32Array(24), p1 = new Float32Array(24), p2 = new Float32Array(24);
|
|
28
|
+
for (let i = 0; i < 24; i++) {
|
|
29
|
+
f1[i] = 0.13 + 0.3 * rand();
|
|
30
|
+
f2[i] = 0.07 + 0.2 * rand();
|
|
31
|
+
p1[i] = rand() * Math.PI * 2;
|
|
32
|
+
p2[i] = rand() * Math.PI * 2;
|
|
33
|
+
}
|
|
34
|
+
// per-band gate, eased so changing `degree` blends bands in and out
|
|
35
|
+
return { f1, f2, p1, p2, gate: new Float32Array([0, 0, 0, 0, 0]) };
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Writes the 24 weights for time T into `out`. Bands above `degree` fade
|
|
39
|
+
* out; the top band leads and lower bands sit at about half strength.
|
|
40
|
+
*/
|
|
41
|
+
function updateHarmonicWeights(state, out, T, degree, dt) {
|
|
42
|
+
const deg = Math.round(Math.min(Math.max(degree, 1), 4));
|
|
43
|
+
const ease = Math.min(1, dt * 2.5);
|
|
44
|
+
let energy = 0;
|
|
45
|
+
for (let l = 1; l <= 4; l++) {
|
|
46
|
+
// l = 1 only shifts the shell sideways, so it stays quiet unless it is the only band
|
|
47
|
+
const target = l > deg ? 0 : l === deg ? 1 : l === 1 ? 0.2 : 0.55;
|
|
48
|
+
state.gate[l] += (target - state.gate[l]) * ease;
|
|
49
|
+
energy += state.gate[l] * state.gate[l] * (2 * l + 1);
|
|
50
|
+
}
|
|
51
|
+
const norm = 1 / Math.sqrt(Math.max(energy / 5, 1e-3));
|
|
52
|
+
for (let i = 0; i < 24; i++) {
|
|
53
|
+
const g = state.gate[SH_BANDS[i]];
|
|
54
|
+
out[i] = g * norm * (0.7 * Math.sin(T * state.f1[i] + state.p1[i]) + 0.3 * Math.sin(T * state.f2[i] + state.p2[i]));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
const vert = /* glsl */ `
|
|
58
|
+
uniform float uW[24];
|
|
59
|
+
uniform float uAmplitude;
|
|
60
|
+
uniform float uBound;
|
|
61
|
+
uniform float uSizeMultiplier;
|
|
62
|
+
varying vec3 vNormal;
|
|
63
|
+
varying vec3 vViewDir;
|
|
64
|
+
varying float vField;
|
|
65
|
+
|
|
66
|
+
// Real spherical harmonics for l = 1..4, as polynomials in the unit vector,
|
|
67
|
+
// each scaled so its peak is about 1. The CPU animates the 24 weights.
|
|
68
|
+
float sh(vec3 n){
|
|
69
|
+
float x = n.x, y = n.y, z = n.z;
|
|
70
|
+
float x2 = x * x, y2 = y * y, z2 = z * z;
|
|
71
|
+
float s = 0.0;
|
|
72
|
+
s += uW[0] * y + uW[1] * z + uW[2] * x;
|
|
73
|
+
s += uW[3] * 2.0 * x * y + uW[4] * 2.0 * y * z + uW[5] * (1.5 * z2 - 0.5)
|
|
74
|
+
+ uW[6] * 2.0 * x * z + uW[7] * (x2 - y2);
|
|
75
|
+
s += uW[8] * y * (3.0 * x2 - y2)
|
|
76
|
+
+ uW[9] * 5.2 * x * y * z
|
|
77
|
+
+ uW[10] * 0.8 * y * (5.0 * z2 - 1.0)
|
|
78
|
+
+ uW[11] * 0.5 * z * (5.0 * z2 - 3.0)
|
|
79
|
+
+ uW[12] * 0.8 * x * (5.0 * z2 - 1.0)
|
|
80
|
+
+ uW[13] * 2.6 * z * (x2 - y2)
|
|
81
|
+
+ uW[14] * x * (x2 - 3.0 * y2);
|
|
82
|
+
s += uW[15] * 4.0 * x * y * (x2 - y2)
|
|
83
|
+
+ uW[16] * 3.0 * y * z * (3.0 * x2 - y2)
|
|
84
|
+
+ uW[17] * 1.5 * x * y * (7.0 * z2 - 1.0)
|
|
85
|
+
+ uW[18] * 0.95 * y * z * (7.0 * z2 - 3.0)
|
|
86
|
+
+ uW[19] * 0.125 * (35.0 * z2 * z2 - 30.0 * z2 + 3.0)
|
|
87
|
+
+ uW[20] * 0.95 * x * z * (7.0 * z2 - 3.0)
|
|
88
|
+
+ uW[21] * 0.78 * (x2 - y2) * (7.0 * z2 - 1.0)
|
|
89
|
+
+ uW[22] * 3.0 * x * z * (x2 - 3.0 * y2)
|
|
90
|
+
+ uW[23] * (x2 * (x2 - 3.0 * y2) - y2 * (3.0 * x2 - y2));
|
|
91
|
+
return s;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
vec3 shellPoint(vec3 n, out float f){
|
|
95
|
+
f = sh(n);
|
|
96
|
+
return n * uBound * uSizeMultiplier * (1.0 + uAmplitude * f);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
void main(){
|
|
100
|
+
vec3 n = normalize(position);
|
|
101
|
+
float f, f1, f2;
|
|
102
|
+
vec3 p0 = shellPoint(n, f);
|
|
103
|
+
|
|
104
|
+
// normal from two nearby points on the displaced surface
|
|
105
|
+
vec3 up = abs(n.y) > 0.99 ? vec3(1.0, 0.0, 0.0) : vec3(0.0, 1.0, 0.0);
|
|
106
|
+
vec3 t1 = normalize(cross(n, up));
|
|
107
|
+
vec3 t2 = cross(n, t1);
|
|
108
|
+
vec3 p1 = shellPoint(normalize(n + t1 * 0.01), f1);
|
|
109
|
+
vec3 p2 = shellPoint(normalize(n + t2 * 0.01), f2);
|
|
110
|
+
vec3 nrm = normalize(cross(p1 - p0, p2 - p0));
|
|
111
|
+
if (dot(nrm, n) < 0.0) nrm = -nrm;
|
|
112
|
+
|
|
113
|
+
vField = f;
|
|
114
|
+
vec4 mv = modelViewMatrix * vec4(p0, 1.0);
|
|
115
|
+
vNormal = normalize(normalMatrix * nrm);
|
|
116
|
+
vViewDir = normalize(-mv.xyz);
|
|
117
|
+
gl_Position = projectionMatrix * mv;
|
|
118
|
+
}
|
|
119
|
+
`;
|
|
120
|
+
const frag = /* glsl */ `
|
|
121
|
+
uniform vec3 uColor;
|
|
122
|
+
uniform vec3 uRimColor;
|
|
123
|
+
uniform float uOpacity;
|
|
124
|
+
uniform float uIntensity;
|
|
125
|
+
uniform float uFilmThickness;
|
|
126
|
+
uniform float uNodalLines;
|
|
127
|
+
varying vec3 vNormal;
|
|
128
|
+
varying vec3 vViewDir;
|
|
129
|
+
varying float vField;
|
|
130
|
+
|
|
131
|
+
const float TAU = 6.28318530718;
|
|
132
|
+
|
|
133
|
+
void main(){
|
|
134
|
+
vec3 N = normalize(vNormal);
|
|
135
|
+
vec3 V = normalize(vViewDir);
|
|
136
|
+
float ndv = abs(dot(N, V));
|
|
137
|
+
|
|
138
|
+
// Thin-film interference, as on a soap bubble: a film of index 1.33 and
|
|
139
|
+
// thickness d delays the inner reflection by 2*n*d*cos(theta_t). Each
|
|
140
|
+
// wavelength interferes on its own, which splits white light into bands.
|
|
141
|
+
// The film is thicker where the harmonic pushes the shell out, so the
|
|
142
|
+
// colour bands follow the shape.
|
|
143
|
+
float cosT = sqrt(max(1.0 - (1.0 - ndv * ndv) / (1.33 * 1.33), 0.0));
|
|
144
|
+
float opd = 2.0 * 1.33 * uFilmThickness * (1.0 + 0.35 * vField) * cosT;
|
|
145
|
+
vec3 film = 0.5 + 0.5 * cos(TAU * opd / vec3(650.0, 532.0, 450.0));
|
|
146
|
+
|
|
147
|
+
float fres = pow(1.0 - ndv, 3.0);
|
|
148
|
+
vec3 col = uColor * 0.25 + film * (0.2 + 0.8 * fres);
|
|
149
|
+
col = mix(col, uRimColor, fres * 0.45);
|
|
150
|
+
|
|
151
|
+
// nodal lines: where the harmonic field crosses zero
|
|
152
|
+
float nd = abs(vField) / max(fwidth(vField), 1e-4);
|
|
153
|
+
float nodal = (1.0 - smoothstep(0.5, 1.5, nd)) * uNodalLines;
|
|
154
|
+
col += uRimColor * nodal * 1.5;
|
|
155
|
+
|
|
156
|
+
float a = (0.06 + 0.9 * fres + nodal * 0.8) * uOpacity;
|
|
157
|
+
if (a < 0.003) discard;
|
|
158
|
+
gl_FragColor = vec4(col * uIntensity, a);
|
|
159
|
+
}
|
|
160
|
+
`;
|
|
161
|
+
const HarmonicShellEffect = ({ boundingRadius, config, animations, onObjectReady, onObjectDispose }) => {
|
|
162
|
+
const { intensity, speed, color, opacity, degree = 3, amplitude = 0.22, sizeMultiplier = 1.35, filmThickness = 380, nodalLines = 0.6, rimColor = '#bfe8ff', } = config;
|
|
163
|
+
const rootRef = useRef(null);
|
|
164
|
+
const animCache = useRef({});
|
|
165
|
+
useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
|
|
166
|
+
const state = useMemo(createHarmonicState, []);
|
|
167
|
+
const uniforms = useMemo(() => ({
|
|
168
|
+
uTime: { value: 0 },
|
|
169
|
+
uSpeed: { value: 1 },
|
|
170
|
+
uIntensity: { value: 1 },
|
|
171
|
+
uOpacity: { value: 1 },
|
|
172
|
+
uColor: { value: new THREE.Color() },
|
|
173
|
+
uRimColor: { value: new THREE.Color() },
|
|
174
|
+
uAmplitude: { value: 0.22 },
|
|
175
|
+
uBound: { value: 1 },
|
|
176
|
+
uSizeMultiplier: { value: 1.35 },
|
|
177
|
+
uFilmThickness: { value: 380 },
|
|
178
|
+
uNodalLines: { value: 0.6 },
|
|
179
|
+
uDegree: { value: 3 },
|
|
180
|
+
uW: { value: new Float32Array(24) },
|
|
181
|
+
}), []);
|
|
182
|
+
const material = useMemo(() => new THREE.ShaderMaterial({ vertexShader: vert, fragmentShader: frag, uniforms, transparent: true, depthWrite: false, blending: THREE.AdditiveBlending }), [uniforms]);
|
|
183
|
+
useEffect(() => () => material.dispose(), [material]);
|
|
184
|
+
const materials = useMemo(() => [material], [material]);
|
|
185
|
+
// the vertex shader displaces a unit sphere, so the mesh never needs rebuilding
|
|
186
|
+
const geometry = useMemo(() => new THREE.SphereGeometry(1, 192, 128), []);
|
|
187
|
+
useEffect(() => () => geometry.dispose(), [geometry]);
|
|
188
|
+
useLayoutEffect(() => {
|
|
189
|
+
uniforms.uSpeed.value = speed;
|
|
190
|
+
uniforms.uIntensity.value = intensity;
|
|
191
|
+
uniforms.uOpacity.value = opacity;
|
|
192
|
+
uniforms.uColor.value.set(color);
|
|
193
|
+
uniforms.uRimColor.value.set(rimColor);
|
|
194
|
+
uniforms.uAmplitude.value = amplitude;
|
|
195
|
+
uniforms.uBound.value = boundingRadius;
|
|
196
|
+
uniforms.uSizeMultiplier.value = sizeMultiplier;
|
|
197
|
+
uniforms.uFilmThickness.value = filmThickness;
|
|
198
|
+
uniforms.uNodalLines.value = nodalLines;
|
|
199
|
+
uniforms.uDegree.value = degree;
|
|
200
|
+
}, [uniforms, speed, intensity, opacity, color, rimColor, amplitude, boundingRadius, sizeMultiplier, filmThickness, nodalLines, degree]);
|
|
201
|
+
useFrame((_, delta) => {
|
|
202
|
+
uniforms.uTime.value += delta * uniforms.uSpeed.value;
|
|
203
|
+
updateHarmonicWeights(state, uniforms.uW.value, uniforms.uTime.value, uniforms.uDegree.value, delta);
|
|
204
|
+
if (animations?.length)
|
|
205
|
+
applyGenerativeAnimations(materials, animations, animCache.current, Date.now(), false);
|
|
206
|
+
});
|
|
207
|
+
return (_jsx("group", { ref: rootRef, children: _jsx("mesh", { geometry: geometry, material: material }) }));
|
|
208
|
+
};
|
|
209
|
+
export default HarmonicShellEffect;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import React from
|
|
2
|
-
import type {
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { AnimationOptions, IceCrystalsConfig } from '@3driseai/3drise-core';
|
|
3
3
|
import type { ObjectReadyProps } from '../../types/objectReady';
|
|
4
|
-
interface IceCrystalsEffectProps extends ObjectReadyProps {
|
|
4
|
+
export interface IceCrystalsEffectProps extends ObjectReadyProps {
|
|
5
5
|
boundingRadius: number;
|
|
6
6
|
config: IceCrystalsConfig;
|
|
7
7
|
animations?: AnimationOptions[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IceCrystalsEffect.d.ts","sourceRoot":"","sources":["../../../src/components/effects/IceCrystalsEffect.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"IceCrystalsEffect.d.ts","sourceRoot":"","sources":["../../../src/components/effects/IceCrystalsEffect.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsD,MAAM,OAAO,CAAC;AAG3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAGjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,sBAAuB,SAAQ,gBAAgB;IAC5D,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACnC;AA+PD,QAAA,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,sBAAsB,CAyEvD,CAAC;AAEF,eAAe,iBAAiB,CAAC"}
|