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