@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,458 @@
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 Saturn-like ring system: Keplerian shear shapes icy clumps, resonant moons open gaps and spiral density waves, and the planet casts a soft shadow. Use for planets, sci-fi, space scenes.
9
+ *
10
+ * Ω ∝ r^−3/2 · gaps at r = r_moon (q/p)^(2/3) · wave phase m(θ−θ_moon) − kΔr² · I/F = ϖ₀P(α)μ₀/4(μ+μ₀)·(1−e^(−τ(1/μ+1/μ₀)))
11
+ */
12
+ const NOISE3 = /* glsl */ `
13
+ float hash13(vec3 p3){
14
+ p3 = fract(p3 * 0.1031);
15
+ p3 += dot(p3, p3.zyx + 31.32);
16
+ return fract((p3.x + p3.y) * p3.z);
17
+ }
18
+ float vnoise(vec3 p){
19
+ vec3 i = floor(p);
20
+ vec3 f = fract(p);
21
+ f = f * f * f * (f * (f * 6.0 - 15.0) + 10.0);
22
+ float n000 = hash13(i);
23
+ float n100 = hash13(i + vec3(1.0, 0.0, 0.0));
24
+ float n010 = hash13(i + vec3(0.0, 1.0, 0.0));
25
+ float n110 = hash13(i + vec3(1.0, 1.0, 0.0));
26
+ float n001 = hash13(i + vec3(0.0, 0.0, 1.0));
27
+ float n101 = hash13(i + vec3(1.0, 0.0, 1.0));
28
+ float n011 = hash13(i + vec3(0.0, 1.0, 1.0));
29
+ float n111 = hash13(i + vec3(1.0, 1.0, 1.0));
30
+ return mix(mix(mix(n000, n100, f.x), mix(n010, n110, f.x), f.y),
31
+ mix(mix(n001, n101, f.x), mix(n011, n111, f.x), f.y), f.z);
32
+ }
33
+ // low-frequency fBm; the rotation between octaves hides the lattice
34
+ const mat3 FBM_ROT = mat3(0.00, 0.80, 0.60, -0.80, 0.36, -0.48, -0.60, -0.48, 0.64);
35
+ float fbm3(vec3 p, int octaves){
36
+ float a = 0.5, s = 0.0;
37
+ for (int i = 0; i < 5; i++){
38
+ if (i >= octaves) break;
39
+ s += a * vnoise(p);
40
+ p = FBM_ROT * p * 2.02;
41
+ a *= 0.5;
42
+ }
43
+ return s;
44
+ }
45
+ `;
46
+ // ---------------------------------------------------------------------------
47
+ // A ring system is a differentially rotating disc of icy particles.
48
+ // * Kepler: Omega(r) = Omega_in (r_in / r)^(3/2). Neighbouring orbits shear
49
+ // past each other, so any clump is drawn out into a trailing streak. The
50
+ // clump texture is advected by a two-phase flow map (two copies of the same
51
+ // noise, each restarted half a cycle apart and cross-faded) so the pattern
52
+ // shears the way the gas does but never winds up.
53
+ // * Resonances: a moon at r_m forces the ring where the orbital periods are
54
+ // commensurate, r = r_m (q/p)^(2/3). Strong resonances clear gaps (2:1 with
55
+ // a Mimas-like moon = the Cassini division); the rest launch spiral density
56
+ // waves whose wavenumber grows linearly with distance from resonance, so
57
+ // the phase goes as m(theta - theta_moon) - k dr^2: a tight wave train that
58
+ // damps out within a few tens of wavelengths.
59
+ // * Light: a ring is an optically thin-to-thick slab. Single scattering gives
60
+ // I/F = w0 P(alpha) mu0 / (4 (mu + mu0)) (1 - exp(-tau (1/mu + 1/mu0)))
61
+ // on the lit side, and the transmitted form on the unlit side, where the
62
+ // dense B ring goes dark and only the thin parts glow. P(alpha) mixes a
63
+ // backscattering Lambert-sphere phase for the big particles with a
64
+ // Henyey-Greenstein lobe (g = 0.72) for the dust, which is why dusty
65
+ // ringlets and spokes blaze when the rings are lit from behind.
66
+ // ---------------------------------------------------------------------------
67
+ const ringCommon = /* glsl */ `
68
+ uniform float uTime;
69
+ uniform float uBound;
70
+ uniform float uInnerRadius;
71
+ uniform float uOuterRadius;
72
+ uniform float uMoons;
73
+ const float PI = 3.14159265359;
74
+ const float OMEGA_IN = 0.13;
75
+
76
+ float ringR(float x){ return (uInnerRadius + x * (uOuterRadius - uInnerRadius)) * uBound; }
77
+ float kepler(float r){ return OMEGA_IN * pow(max(uInnerRadius * uBound, 1e-4) / max(r, 1e-4), 1.5); }
78
+
79
+ // moon 0: Mimas-like, its 2:1 resonance is the Cassini division at x = 0.56
80
+ // moon 1: Janus-like, its 7:6 resonance truncates the A ring at x = 0.945
81
+ // moon 2: Pan, inside the Encke gap at x = 0.825
82
+ float moonRadius(int idx){
83
+ if (idx == 0) return ringR(0.56) / pow(0.5, 0.66666667);
84
+ if (idx == 1) return ringR(0.945) / pow(6.0 / 7.0, 0.66666667);
85
+ return ringR(0.825);
86
+ }
87
+ float moonPhase(int idx){
88
+ float r = moonRadius(idx);
89
+ return kepler(r) * uTime + float(idx) * 2.1;
90
+ }
91
+ `;
92
+ const ringVert = /* glsl */ `
93
+ uniform float uOuterRadius;
94
+ uniform float uBound;
95
+ varying vec3 vLocal;
96
+ varying vec3 vWorld;
97
+ varying vec3 vNormal;
98
+ varying vec3 vCenter;
99
+ void main(){
100
+ vec3 p = vec3(position.x, 0.0, -position.y) * uOuterRadius * uBound * 1.06;
101
+ vLocal = p;
102
+ vec4 wp = modelMatrix * vec4(p, 1.0);
103
+ vWorld = wp.xyz;
104
+ vNormal = normalize(mat3(modelMatrix) * vec3(0.0, 1.0, 0.0));
105
+ vCenter = (modelMatrix * vec4(0.0, 0.0, 0.0, 1.0)).xyz;
106
+ gl_Position = projectionMatrix * viewMatrix * wp;
107
+ }
108
+ `;
109
+ const ringFrag = /* glsl */ `
110
+ uniform vec3 uColor;
111
+ uniform vec3 uGapColor;
112
+ uniform vec3 uDustColor;
113
+ uniform float uIntensity;
114
+ uniform float uOpacity;
115
+ uniform float uSpokes;
116
+ uniform float uForwardScatter;
117
+ uniform float uSunAngle;
118
+ uniform float uSunElevation;
119
+ uniform float uHasHost;
120
+ ${ringCommon}
121
+ ${NOISE3}
122
+ varying vec3 vLocal;
123
+ varying vec3 vWorld;
124
+ varying vec3 vNormal;
125
+ varying vec3 vCenter;
126
+
127
+ float edgeAt(float x, float e, float w){ return smoothstep(e - w, e + w, x); }
128
+ float bandAt(float x, float a, float b, float w){ return edgeAt(x, a, w) * (1.0 - edgeAt(x, b, w)); }
129
+
130
+ // clumpy ring texture, sampled on the ring embedded in 3D so it wraps in angle
131
+ float clumps(float th, float r, float cyc, float lod){
132
+ float ka = 13.0, kr = 22.0;
133
+ vec3 q = vec3(cos(th), sin(th), 0.0) * (r / uBound) * ka + vec3(0.0, 0.0, r / uBound * kr + cyc * 13.7);
134
+ float n = vnoise(q) * 0.62 + vnoise(q * 2.3 + 11.0) * 0.38;
135
+ return mix(0.5, n, 1.0 - smoothstep(0.35, 1.0, lod));
136
+ }
137
+
138
+ void main(){
139
+ float r = length(vLocal.xz);
140
+ float th = atan(vLocal.z, vLocal.x);
141
+ float rin = uInnerRadius * uBound;
142
+ float rout = uOuterRadius * uBound;
143
+ float span = max(rout - rin, 1e-4);
144
+ float x = (r - rin) / span;
145
+ if (x < -0.05 || x > 1.05) discard;
146
+ float aa = fwidth(r) / span;
147
+ float w = max(aa, 0.0012);
148
+
149
+ float m1 = step(0.5, uMoons);
150
+ float m2 = step(1.5, uMoons);
151
+ float m3 = step(2.5, uMoons);
152
+
153
+ // ---- radial optical depth profile ------------------------------------
154
+ float tau = 0.0;
155
+ float dusty = 0.0;
156
+ // C ring: thin, dusty, with a few ringlets
157
+ float cR = bandAt(x, 0.005, 0.2, w * 2.0);
158
+ tau += cR * (0.16 + 0.07 * sin(x * 120.0));
159
+ dusty += cR * 0.75;
160
+ // B ring: the dense one, brightest in its middle
161
+ float bOut = mix(0.62, 0.56, m1);
162
+ float bR = bandAt(x, 0.2, bOut, w);
163
+ tau += bR * (1.5 + 1.1 * smoothstep(0.2, 0.36, x) - 0.75 * smoothstep(0.42, bOut, x));
164
+ // A ring
165
+ float aIn = mix(0.62, 0.645, m1);
166
+ float aOut = mix(1.0, 0.945, m2);
167
+ float aR = bandAt(x, aIn, aOut, w);
168
+ tau += aR * (0.85 - 0.3 * smoothstep(aIn, aOut, x));
169
+ // Cassini division: not empty, a few faint ringlets of darker material
170
+ float cass = bandAt(x, 0.56, 0.645, w) * m1;
171
+ tau += cass * (0.06 + 0.07 * smoothstep(0.55, 0.75, sin(x * 210.0)));
172
+ dusty += cass * 0.4;
173
+ // Encke gap with Pan inside, and the narrow dusty F ring
174
+ float encke = exp(-pow((x - 0.825) / 0.0085, 2.0)) * m3;
175
+ tau *= 1.0 - 0.94 * encke;
176
+ dusty += encke * 0.6;
177
+ float fRing = exp(-pow((x - 0.995) / 0.005, 2.0)) * m2;
178
+ tau += fRing * 0.3;
179
+ dusty += fRing * 0.9;
180
+ if (m2 < 0.5) tau *= 1.0 - smoothstep(0.95, 1.0, x);
181
+
182
+ // ---- spiral density waves from the resonances ------------------------
183
+ float wave = 0.0;
184
+ for (int k = 0; k < 2; k++){
185
+ if (float(k) + 0.5 > uMoons) break;
186
+ float rm = moonRadius(k);
187
+ float thm = moonPhase(k);
188
+ for (int m = 2; m <= 7; m++){
189
+ float fm = float(m);
190
+ float rres = rm * pow((fm - 1.0) / fm, 0.66666667);
191
+ float dr = r - rres;
192
+ if (dr < 0.0 || dr > 0.3 * uBound) continue;
193
+ float lam = 0.08 * uBound;
194
+ float dpsi = 2.0 * 6.2831853 * dr / (lam * lam);
195
+ float psi = fm * (th - thm) - 6.2831853 * dr * dr / (lam * lam);
196
+ float amp = smoothstep(0.0, 0.25 * lam, dr) * exp(-dr / (0.22 * uBound)) / fm;
197
+ amp *= 1.0 - smoothstep(0.2, 0.7, fwidth(r) * dpsi / 6.2831853);
198
+ wave += amp * sin(psi);
199
+ }
200
+ }
201
+ tau *= 1.0 + 0.85 * wave;
202
+
203
+ // ringlets: axisymmetric fine structure, faded out when a pixel spans several
204
+ float rl = 0.0;
205
+ float rlLod = fwidth(x);
206
+ for (int i = 0; i < 4; i++){
207
+ float f = 42.0 * pow(1.9, float(i));
208
+ float amp = 0.3 / pow(1.7, float(i));
209
+ rl += amp * sin(x * f + float(i) * 2.3) * (1.0 - smoothstep(0.12, 0.45, rlLod * f / 6.2831853));
210
+ }
211
+ tau *= 1.0 + 0.55 * rl;
212
+
213
+ // ---- clumps advected by the Keplerian shear (two-phase flow map) -----
214
+ float om = kepler(r);
215
+ float T = 9.0;
216
+ float u = uTime / T;
217
+ float kA = floor(u), kB = floor(u + 0.5);
218
+ float eA = (u - kA) * T, eB = (u + 0.5 - kB) * T;
219
+ float wA = 1.0 - abs(2.0 * (u - kA) - 1.0);
220
+ float lod = fwidth(r) / uBound * 22.0;
221
+ float nA = clumps(th - om * eA, r, kA, lod);
222
+ float nB = clumps(th - om * eB, r, kB, lod);
223
+ float n = mix(nB, nA, wA);
224
+ tau *= 0.72 + 0.6 * n;
225
+
226
+ // ---- spokes: dust lifted above the ring, turning with the planet ------
227
+ float spoke = 0.0;
228
+ if (uSpokes > 0.002){
229
+ float sa = th - kepler(ringR(0.4)) * uTime;
230
+ float sn = vnoise(vec3(cos(sa) * 5.5, sin(sa) * 5.5, uTime * 0.02 + 4.0));
231
+ spoke = smoothstep(0.55, 0.78, sn) * bandAt(x, 0.21, 0.55, 0.06);
232
+ spoke *= 0.45 + 0.55 * vnoise(vec3(cos(sa) * 16.0, sin(sa) * 16.0, x * 2.0));
233
+ spoke *= uSpokes;
234
+ tau += spoke * 0.1;
235
+ dusty += spoke * 0.8;
236
+ }
237
+ tau = max(tau, 0.0);
238
+ if (tau < 0.002) discard;
239
+ dusty = clamp(dusty, 0.0, 1.0);
240
+
241
+ // ---- illumination ----------------------------------------------------
242
+ float el = radians(uSunElevation), az = radians(uSunAngle);
243
+ vec3 S = vec3(cos(el) * cos(az), sin(el), cos(el) * sin(az));
244
+ vec3 N = normalize(vNormal);
245
+ vec3 P = vWorld - vCenter;
246
+ vec3 V = normalize(cameraPosition - vWorld);
247
+ float sN = dot(S, N), vN = dot(V, N);
248
+ float mu0 = max(abs(sN), 0.03);
249
+ float mu = max(abs(vN), 0.03);
250
+ float cosA = clamp(dot(S, V), -1.0, 1.0);
251
+ float alpha = acos(cosA);
252
+ // Lambert-sphere phase for the big particles + an opposition surge
253
+ float pMac = (8.0 / (3.0 * PI)) * (sin(alpha) + (PI - alpha) * cosA) + 0.55 * exp(-alpha / 0.1);
254
+ // Henyey-Greenstein lobe for the dust (forward scattering)
255
+ float g = 0.72;
256
+ float cs = -cosA;
257
+ float hg = (1.0 - g * g) / pow(max(1.0 + g * g - 2.0 * g * cs, 1e-3), 1.5);
258
+ float fd = clamp(uForwardScatter * (0.25 + 0.75 * dusty), 0.0, 1.0);
259
+ float phase = mix(pMac, hg, fd);
260
+
261
+ // planet shadow: does the ray from here toward the sun hit the planet?
262
+ float shadow = 1.0;
263
+ if (uHasHost > 0.5){
264
+ float b = dot(P, S);
265
+ float d = sqrt(max(dot(P, P) - b * b, 0.0));
266
+ shadow = mix(1.0, smoothstep(uBound * 0.94, uBound * 1.14, d), step(b, 0.0));
267
+ }
268
+
269
+ float w0 = 0.62;
270
+ float I;
271
+ if (sN * vN > 0.0){
272
+ I = w0 * phase * mu0 / (4.0 * (mu + mu0)) * (1.0 - exp(-tau * (1.0 / mu + 1.0 / mu0)));
273
+ } else {
274
+ float dm = mu0 - mu;
275
+ float sgn = dm >= 0.0 ? 1.0 : -1.0;
276
+ float dd = max(abs(dm), 0.03) * sgn;
277
+ I = w0 * phase * mu0 / (4.0 * dd) * (exp(-tau / mu0) - exp(-tau / mu));
278
+ }
279
+ // self-gravity wakes: the clumps are elongated and shade each other, so the
280
+ // same optical depth still shows texture where the ring is saturated
281
+ I = max(I, 0.0) * shadow * (0.86 + 0.28 * n);
282
+ // spokes are dark against the bright lit side and bright against a dark one
283
+ I *= 1.0 - 0.6 * spoke * (1.0 - fd);
284
+
285
+ float a = (1.0 - exp(-tau / mu)) * uOpacity;
286
+ vec3 ice = mix(uGapColor, uColor, smoothstep(0.1, 0.7, tau));
287
+ vec3 col = I * mix(ice, uDustColor, fd * 0.45) * 10.0 * uIntensity;
288
+ // the outermost edge of the drawn disc fades so the quad never shows
289
+ a *= 1.0 - smoothstep(1.0, 1.045, x);
290
+ col *= 1.0 - smoothstep(1.0, 1.045, x);
291
+ if (a < 0.003) discard;
292
+ gl_FragColor = vec4(col * a, a);
293
+ }
294
+ `;
295
+ // Tiny moonlets: shepherds riding in the gaps they keep open.
296
+ const moonVert = /* glsl */ `
297
+ attribute vec3 aMoon; // index, phase offset, size
298
+ uniform float uSunAngle;
299
+ uniform float uSunElevation;
300
+ uniform float uHasHost;
301
+ ${ringCommon}
302
+ varying float vLit;
303
+ void main(){
304
+ int idx = int(aMoon.x + 0.5);
305
+ float rm = moonRadius(idx);
306
+ float ph = moonPhase(idx) + aMoon.y;
307
+ vec3 p = vec3(cos(ph) * rm, 0.0, sin(ph) * rm);
308
+ vec4 wp = modelMatrix * vec4(p, 1.0);
309
+ vec3 P = wp.xyz - (modelMatrix * vec4(0.0, 0.0, 0.0, 1.0)).xyz;
310
+ float el = radians(uSunElevation), az = radians(uSunAngle);
311
+ vec3 S = vec3(cos(el) * cos(az), sin(el), cos(el) * sin(az));
312
+ vec3 V = normalize(cameraPosition - wp.xyz);
313
+ float b = dot(P, S);
314
+ float d = sqrt(max(dot(P, P) - b * b, 0.0));
315
+ float shadow = uHasHost > 0.5 ? mix(1.0, smoothstep(uBound * 0.94, uBound * 1.14, d), step(b, 0.0)) : 1.0;
316
+ vLit = (0.12 + 0.88 * clamp(0.5 + 0.5 * dot(S, V), 0.0, 1.0)) * shadow * step(float(idx) + 0.5, uMoons);
317
+ vec4 mv = viewMatrix * wp;
318
+ gl_PointSize = aMoon.z * uBound * (26.0 / max(-mv.z, 0.05));
319
+ gl_Position = projectionMatrix * mv;
320
+ }
321
+ `;
322
+ const moonFrag = /* glsl */ `
323
+ uniform vec3 uColor;
324
+ uniform float uIntensity;
325
+ uniform float uOpacity;
326
+ varying float vLit;
327
+ void main(){
328
+ float d = length(gl_PointCoord - 0.5) * 2.0;
329
+ if (d > 1.0 || vLit < 0.01) discard;
330
+ float core = smoothstep(1.0, 0.45, d);
331
+ float a = core * vLit * uOpacity;
332
+ if (a < 0.004) discard;
333
+ gl_FragColor = vec4(uColor * a * (0.7 + 0.5 * uIntensity), a);
334
+ }
335
+ `;
336
+ function createUniforms() {
337
+ return {
338
+ uTime: { value: 0 },
339
+ uSpeed: { value: 1 },
340
+ uIntensity: { value: 1 },
341
+ uOpacity: { value: 1 },
342
+ uColor: { value: new THREE.Color('#e9dcc2') },
343
+ uGapColor: { value: new THREE.Color('#8fa2b6') },
344
+ uDustColor: { value: new THREE.Color('#ffd3a1') },
345
+ uInnerRadius: { value: 1.35 },
346
+ uOuterRadius: { value: 2.6 },
347
+ uMoons: { value: 3 },
348
+ uSpokes: { value: 0.45 },
349
+ uForwardScatter: { value: 0.35 },
350
+ uSunAngle: { value: 45 },
351
+ uSunElevation: { value: 24 },
352
+ uBound: { value: 1 },
353
+ uHasHost: { value: 1 },
354
+ };
355
+ }
356
+ function applyConfig(u, c, bound, env) {
357
+ u.uSpeed.value = c.speed;
358
+ u.uIntensity.value = c.intensity;
359
+ u.uOpacity.value = c.opacity;
360
+ u.uColor.value.set(c.color);
361
+ u.uGapColor.value.set(c.gapColor);
362
+ u.uDustColor.value.set(c.dustColor);
363
+ u.uInnerRadius.value = c.innerRadius;
364
+ u.uOuterRadius.value = Math.max(c.outerRadius, c.innerRadius + 0.05);
365
+ u.uMoons.value = c.moons;
366
+ u.uSpokes.value = c.spokes;
367
+ u.uForwardScatter.value = c.forwardScatter;
368
+ u.uSunAngle.value = c.sunAngle;
369
+ u.uSunElevation.value = c.sunElevation;
370
+ u.uBound.value = bound;
371
+ u.uHasHost.value = env && env.hasHost ? 1 : 0;
372
+ }
373
+ function build(u, c, bound, env) {
374
+ const ringMat = new THREE.ShaderMaterial({
375
+ vertexShader: ringVert, fragmentShader: ringFrag, uniforms: u, side: THREE.DoubleSide,
376
+ transparent: true, depthWrite: false, blending: THREE.NormalBlending, premultipliedAlpha: true,
377
+ });
378
+ const moonMat = new THREE.ShaderMaterial({
379
+ vertexShader: moonVert, fragmentShader: moonFrag, uniforms: u,
380
+ transparent: true, depthWrite: false, blending: THREE.NormalBlending, premultipliedAlpha: true,
381
+ });
382
+ const quad = new THREE.PlaneGeometry(2, 2);
383
+ const moonGeo = new THREE.BufferGeometry();
384
+ moonGeo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(9), 3));
385
+ moonGeo.setAttribute('aMoon', new THREE.BufferAttribute(new Float32Array([
386
+ 0, 0, 2.2,
387
+ 1, 1.7, 1.7,
388
+ 2, 0.4, 1.2,
389
+ ]), 3));
390
+ const ring = new THREE.Mesh(quad, ringMat);
391
+ const moons = new THREE.Points(moonGeo, moonMat);
392
+ ring.frustumCulled = false;
393
+ moons.frustumCulled = false;
394
+ ring.renderOrder = 0;
395
+ moons.renderOrder = 1;
396
+ const disc = new THREE.Group();
397
+ disc.add(ring, moons);
398
+ const group = new THREE.Group();
399
+ group.add(disc);
400
+ return {
401
+ object: group,
402
+ materials: [ringMat, moonMat],
403
+ apply(c2) {
404
+ disc.rotation.set((c2.tilt * Math.PI) / 180, 0, (c2.tilt * Math.PI) / 180 * 0.35);
405
+ },
406
+ update(dt, camera) {
407
+ u.uTime.value += dt * u.uSpeed.value;
408
+ },
409
+ dispose() {
410
+ quad.dispose();
411
+ moonGeo.dispose();
412
+ ringMat.dispose();
413
+ moonMat.dispose();
414
+ },
415
+ };
416
+ }
417
+ /** Defaults for every knob, so an older saved config still renders. */
418
+ const DEFAULTS = {
419
+ intensity: 1,
420
+ speed: 1,
421
+ color: '#e9dcc2',
422
+ opacity: 1,
423
+ innerRadius: 1.35,
424
+ outerRadius: 2.6,
425
+ tilt: 17,
426
+ moons: 3,
427
+ spokes: 0.45,
428
+ forwardScatter: 0.35,
429
+ sunAngle: 45,
430
+ sunElevation: 24,
431
+ gapColor: '#8fa2b6',
432
+ dustColor: '#ffd3a1',
433
+ };
434
+ const PlanetaryRingsEffect = ({ boundingRadius, config, animations, hasHost = false, onObjectReady, onObjectDispose }) => {
435
+ const rootRef = useRef(null);
436
+ const animCache = useRef({});
437
+ useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
438
+ // a standalone effect gets boundingRadius 1
439
+ const b = boundingRadius > 0 ? boundingRadius : 1;
440
+ const cfg = useMemo(() => ({ ...DEFAULTS, ...config }), [config]);
441
+ const env = useMemo(() => ({ hasHost: hasHost }), [hasHost]);
442
+ // uniforms outlive the objects, so keyframe animations survive a rebuild
443
+ const uniforms = useMemo(() => createUniforms(), []);
444
+ // rebuilt only when the host size or a structural knob changes
445
+ const instance = useMemo(() => build(uniforms, { ...DEFAULTS, ...config }, b, env), [uniforms, b, env]);
446
+ useEffect(() => () => instance.dispose(), [instance]);
447
+ useLayoutEffect(() => {
448
+ applyConfig(uniforms, cfg, b, env);
449
+ instance.apply(cfg);
450
+ }, [instance, uniforms, cfg, b, env]);
451
+ useFrame((state, delta) => {
452
+ instance.update(Math.min(delta, 0.1), state.camera);
453
+ if (animations?.length)
454
+ applyGenerativeAnimations(instance.materials, animations, animCache.current, Date.now(), false);
455
+ });
456
+ return (_jsx("group", { ref: rootRef, children: _jsx("primitive", { object: instance.object }) }));
457
+ };
458
+ export default PlanetaryRingsEffect;
@@ -1,7 +1,7 @@
1
- import React from "react";
2
- import type { PortalConfig, AnimationOptions } from "@3driseai/3drise-core";
1
+ import React from 'react';
2
+ import type { AnimationOptions, PortalConfig } from '@3driseai/3drise-core';
3
3
  import type { ObjectReadyProps } from '../../types/objectReady';
4
- interface PortalEffectProps extends ObjectReadyProps {
4
+ export interface PortalEffectProps extends ObjectReadyProps {
5
5
  boundingRadius: number;
6
6
  config: PortalConfig;
7
7
  animations?: AnimationOptions[];
@@ -1 +1 @@
1
- {"version":3,"file":"PortalEffect.d.ts","sourceRoot":"","sources":["../../../src/components/effects/PortalEffect.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE5E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,UAAU,iBAAkB,SAAQ,gBAAgB;IAChD,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACnC;AAgHD,QAAA,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA6E7C,CAAC;AACF,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"PortalEffect.d.ts","sourceRoot":"","sources":["../../../src/components/effects/PortalEffect.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;CACnC;AAsND,QAAA,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAyE7C,CAAC;AAEF,eAAe,YAAY,CAAC"}