@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.
Files changed (116) hide show
  1. package/README.md +4 -3
  2. package/dist/components/ChladniGrid.d.ts +15 -0
  3. package/dist/components/ChladniGrid.d.ts.map +1 -0
  4. package/dist/components/ChladniGrid.js +62 -0
  5. package/dist/components/CustomPrimitive.d.ts +2 -0
  6. package/dist/components/CustomPrimitive.d.ts.map +1 -1
  7. package/dist/components/CustomPrimitive.js +2 -2
  8. package/dist/components/EffectsGenerator.d.ts.map +1 -1
  9. package/dist/components/EffectsGenerator.js +26 -2
  10. package/dist/components/FlowNetGrid.d.ts +15 -0
  11. package/dist/components/FlowNetGrid.d.ts.map +1 -0
  12. package/dist/components/FlowNetGrid.js +61 -0
  13. package/dist/components/GirihGrid.d.ts +15 -0
  14. package/dist/components/GirihGrid.d.ts.map +1 -0
  15. package/dist/components/GirihGrid.js +62 -0
  16. package/dist/components/GravWaveGrid.d.ts +15 -0
  17. package/dist/components/GravWaveGrid.d.ts.map +1 -0
  18. package/dist/components/GravWaveGrid.js +62 -0
  19. package/dist/components/GridsGenerator.d.ts +2 -2
  20. package/dist/components/GridsGenerator.d.ts.map +1 -1
  21. package/dist/components/GridsGenerator.js +21 -0
  22. package/dist/components/HyperbolicGrid.d.ts +15 -0
  23. package/dist/components/HyperbolicGrid.d.ts.map +1 -0
  24. package/dist/components/HyperbolicGrid.js +61 -0
  25. package/dist/components/LayoutInstances.d.ts +13 -0
  26. package/dist/components/LayoutInstances.d.ts.map +1 -0
  27. package/dist/components/LayoutInstances.js +39 -0
  28. package/dist/components/ModelPrimitive.d.ts +2 -0
  29. package/dist/components/ModelPrimitive.d.ts.map +1 -1
  30. package/dist/components/ModelPrimitive.js +3 -3
  31. package/dist/components/ObjectNode.d.ts.map +1 -1
  32. package/dist/components/ObjectNode.js +36 -8
  33. package/dist/components/ReactionDiffusionGrid.d.ts +15 -0
  34. package/dist/components/ReactionDiffusionGrid.d.ts.map +1 -0
  35. package/dist/components/ReactionDiffusionGrid.js +376 -0
  36. package/dist/components/WallpaperGrid.d.ts +15 -0
  37. package/dist/components/WallpaperGrid.d.ts.map +1 -0
  38. package/dist/components/WallpaperGrid.js +555 -0
  39. package/dist/components/cursor/CursorController.d.ts.map +1 -1
  40. package/dist/components/effects/BlackHoleEffect.d.ts +3 -3
  41. package/dist/components/effects/BlackHoleEffect.d.ts.map +1 -1
  42. package/dist/components/effects/BlackHoleEffect.js +293 -163
  43. package/dist/components/effects/DandelionSeedsEffect.d.ts +13 -0
  44. package/dist/components/effects/DandelionSeedsEffect.d.ts.map +1 -0
  45. package/dist/components/effects/DandelionSeedsEffect.js +510 -0
  46. package/dist/components/effects/FallingLeavesEffect.d.ts +13 -0
  47. package/dist/components/effects/FallingLeavesEffect.d.ts.map +1 -0
  48. package/dist/components/effects/FallingLeavesEffect.js +446 -0
  49. package/dist/components/effects/FieldLinesEffect.d.ts +11 -0
  50. package/dist/components/effects/FieldLinesEffect.d.ts.map +1 -0
  51. package/dist/components/effects/FieldLinesEffect.js +260 -0
  52. package/dist/components/effects/FireflySwarmEffect.d.ts +13 -0
  53. package/dist/components/effects/FireflySwarmEffect.d.ts.map +1 -0
  54. package/dist/components/effects/FireflySwarmEffect.js +408 -0
  55. package/dist/components/effects/GalaxyEffect.d.ts +13 -0
  56. package/dist/components/effects/GalaxyEffect.d.ts.map +1 -0
  57. package/dist/components/effects/GalaxyEffect.js +374 -0
  58. package/dist/components/effects/HarmonicShellEffect.d.ts +11 -0
  59. package/dist/components/effects/HarmonicShellEffect.d.ts.map +1 -0
  60. package/dist/components/effects/HarmonicShellEffect.js +209 -0
  61. package/dist/components/effects/IceCrystalsEffect.d.ts +3 -3
  62. package/dist/components/effects/IceCrystalsEffect.d.ts.map +1 -1
  63. package/dist/components/effects/IceCrystalsEffect.js +286 -166
  64. package/dist/components/effects/IceHaloEffect.d.ts +13 -0
  65. package/dist/components/effects/IceHaloEffect.d.ts.map +1 -0
  66. package/dist/components/effects/IceHaloEffect.js +306 -0
  67. package/dist/components/effects/LichtenbergEffect.d.ts +13 -0
  68. package/dist/components/effects/LichtenbergEffect.d.ts.map +1 -0
  69. package/dist/components/effects/LichtenbergEffect.js +517 -0
  70. package/dist/components/effects/MoleculesEffect.d.ts +3 -3
  71. package/dist/components/effects/MoleculesEffect.d.ts.map +1 -1
  72. package/dist/components/effects/MoleculesEffect.js +322 -164
  73. package/dist/components/effects/MurmurationEffect.d.ts +13 -0
  74. package/dist/components/effects/MurmurationEffect.d.ts.map +1 -0
  75. package/dist/components/effects/MurmurationEffect.js +539 -0
  76. package/dist/components/effects/PlanetaryRingsEffect.d.ts +13 -0
  77. package/dist/components/effects/PlanetaryRingsEffect.d.ts.map +1 -0
  78. package/dist/components/effects/PlanetaryRingsEffect.js +458 -0
  79. package/dist/components/effects/PortalEffect.d.ts +3 -3
  80. package/dist/components/effects/PortalEffect.d.ts.map +1 -1
  81. package/dist/components/effects/PortalEffect.js +240 -163
  82. package/dist/components/effects/ShockwaveEffect.d.ts +5 -3
  83. package/dist/components/effects/ShockwaveEffect.d.ts.map +1 -1
  84. package/dist/components/effects/ShockwaveEffect.js +538 -142
  85. package/dist/components/effects/SmokePlumeEffect.d.ts +3 -3
  86. package/dist/components/effects/SmokePlumeEffect.d.ts.map +1 -1
  87. package/dist/components/effects/SmokePlumeEffect.js +180 -116
  88. package/dist/components/effects/SmokeRingEffect.d.ts +3 -3
  89. package/dist/components/effects/SmokeRingEffect.d.ts.map +1 -1
  90. package/dist/components/effects/SmokeRingEffect.js +260 -135
  91. package/dist/components/effects/StrangeAttractorEffect.d.ts +11 -0
  92. package/dist/components/effects/StrangeAttractorEffect.d.ts.map +1 -0
  93. package/dist/components/effects/StrangeAttractorEffect.js +229 -0
  94. package/dist/components/effects/VolumetricFogEffect.d.ts +5 -3
  95. package/dist/components/effects/VolumetricFogEffect.d.ts.map +1 -1
  96. package/dist/components/effects/VolumetricFogEffect.js +211 -93
  97. package/dist/components/effects/WaterCausticsEffect.d.ts +13 -0
  98. package/dist/components/effects/WaterCausticsEffect.d.ts.map +1 -0
  99. package/dist/components/effects/WaterCausticsEffect.js +308 -0
  100. package/dist/components/effects/index.d.ts +9 -0
  101. package/dist/components/effects/index.d.ts.map +1 -1
  102. package/dist/components/effects/index.js +9 -0
  103. package/dist/components/index.d.ts +16 -0
  104. package/dist/components/index.d.ts.map +1 -1
  105. package/dist/components/index.js +8 -0
  106. package/dist/constants.d.ts +1 -1
  107. package/dist/constants.d.ts.map +1 -1
  108. package/dist/constants.js +1 -1
  109. package/dist/hooks/useModelObject.d.ts +6 -1
  110. package/dist/hooks/useModelObject.d.ts.map +1 -1
  111. package/dist/hooks/useModelObject.js +19 -6
  112. package/dist/types/gridConfigs.d.ts +98 -0
  113. package/dist/types/gridConfigs.d.ts.map +1 -1
  114. package/dist/types/index.d.ts +1 -1
  115. package/dist/types/index.d.ts.map +1 -1
  116. 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 "react";
2
- import type { IceCrystalsConfig, AnimationOptions } from "@3driseai/3drise-core";
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":"AAIA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,UAAU,sBAAuB,SAAQ,gBAAgB;IACrD,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACnC;AAmED,QAAA,MAAM,iBAAiB,EAAE,KAAK,CAAC,EAAE,CAAC,sBAAsB,CAwHvD,CAAC;AACF,eAAe,iBAAiB,CAAC"}
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"}