@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
@@ -1,7 +1,7 @@
1
- import React from "react";
2
- import type { SmokePlumeConfig, AnimationOptions } from "@3driseai/3drise-core";
1
+ import React from 'react';
2
+ import type { AnimationOptions, SmokePlumeConfig } from '@3driseai/3drise-core';
3
3
  import type { ObjectReadyProps } from '../../types/objectReady';
4
- interface SmokePlumeEffectProps extends ObjectReadyProps {
4
+ export interface SmokePlumeEffectProps extends ObjectReadyProps {
5
5
  boundingRadius: number;
6
6
  config: SmokePlumeConfig;
7
7
  animations?: AnimationOptions[];
@@ -1 +1 @@
1
- {"version":3,"file":"SmokePlumeEffect.d.ts","sourceRoot":"","sources":["../../../src/components/effects/SmokePlumeEffect.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEhF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,UAAU,qBAAsB,SAAQ,gBAAgB;IACpD,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACnC;AA6ED,QAAA,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CA8DrD,CAAC;AACF,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"SmokePlumeEffect.d.ts","sourceRoot":"","sources":["../../../src/components/effects/SmokePlumeEffect.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsD,MAAM,OAAO,CAAC;AAG3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAGhF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,qBAAsB,SAAQ,gBAAgB;IAC3D,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACnC;AA4JD,QAAA,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CA+DrD,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -1,144 +1,208 @@
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
- // ── Fully GPU-animated smoke puff vertex shader ───────────────────────────────
8
- // Each particle has a lifetime t ∈ [0,1). It rises, expands, and billows.
9
- // All motion driven by u_time — zero CPU writes per frame.
10
- const smokeVert = /* glsl */ `
11
- attribute float aT; // birth offset 0..1 → staggered spawn
12
- attribute vec2 aBase; // (x,z) spawn footprint
13
- attribute float aPhase; // per-particle noise phase
14
- attribute float aRotOff; // per-particle billboard rotation offset
15
- attribute float aSize; // per-particle max size
16
-
17
- uniform float u_time;
18
- uniform float u_speed;
7
+ /**
8
+ * A buoyant smoke plume rising from the top of the host.
9
+ *
10
+ * Puffs follow Morton-Taylor-Turner plume theory: height grows as t^(3/4) and
11
+ * the width grows linearly with height. A divergence-free curl field makes the
12
+ * billows, a crosswind bends the column as z^2, and an optional hot base glows
13
+ * like firelight. Each puff is lit from above and premultiplied, so dense
14
+ * smoke darkens what is behind it.
15
+ *
16
+ * Everything moves in the vertex shader; the CPU only uploads seeds once.
17
+ */
18
+ const PRE_NORMAL = { transparent: true, depthWrite: false, blending: THREE.NormalBlending, premultipliedAlpha: true };
19
+ function mulberry32(seed) {
20
+ return () => {
21
+ seed |= 0;
22
+ seed = (seed + 0x6d2b79f5) | 0;
23
+ let r = Math.imul(seed ^ (seed >>> 15), 1 | seed);
24
+ r = (r + Math.imul(r ^ (r >>> 7), 61 | r)) ^ r;
25
+ return ((r ^ (r >>> 14)) >>> 0) / 4294967296;
26
+ };
27
+ }
28
+ function buildPlumeParticles(count) {
29
+ const n = Math.round(Math.min(Math.max(count, 40), 600));
30
+ const rand = mulberry32(17);
31
+ const aT = new Float32Array(n);
32
+ const aSeed = new Float32Array(n * 3);
33
+ for (let i = 0; i < n; i++) {
34
+ aT[i] = i / n;
35
+ // gaussian-ish disc: the plume is densest on its axis
36
+ const r = Math.sqrt(-2 * Math.log(1 - rand() * 0.95)) * 0.45;
37
+ const a = rand() * Math.PI * 2;
38
+ aSeed[i * 3] = Math.cos(a) * r;
39
+ aSeed[i * 3 + 1] = Math.sin(a) * r;
40
+ aSeed[i * 3 + 2] = rand();
41
+ }
42
+ return { count: n, aT, aSeed };
43
+ }
44
+ const plumeVert = /* glsl */ `
45
+ attribute float aT;
46
+ attribute vec3 aSeed; // x,y: base disc position (-1..1), z: random
47
+ uniform float uTime;
19
48
  uniform float uHeight;
20
49
  uniform float uSpread;
21
- uniform float uTurb;
22
- uniform float uSizeScale;
23
-
24
- varying float vLife; // 0=just born, 1=about to die
50
+ uniform float uTurbulence;
51
+ uniform float uWind;
52
+ uniform float uBound;
53
+ uniform float uIntensity;
54
+ varying float vLife;
25
55
  varying float vAlpha;
56
+ varying float vRot;
57
+ varying float vSeed;
58
+ varying vec3 vLight;
26
59
 
27
- void main() {
28
- // Age loops continuously
29
- float life = fract(aT + u_time * u_speed * 0.22);
30
- vLife = life;
31
-
32
- // Footprint expands as smoke rises and dissipates
33
- float expand = 1.0 + life * 2.5;
34
- float x = aBase.x * expand * uSpread;
35
- float z = aBase.y * expand * uSpread;
36
-
37
- // Turbulent horizontal drift — two layers of sine turbulence
38
- float t1 = sin(life * 4.2 + aPhase) * uTurb * uSpread * life * 0.9;
39
- float t2 = sin(life * 7.1 + aPhase * 1.7 + 1.3) * uTurb * uSpread * life * 0.4;
40
- float t3 = cos(life * 3.5 - aPhase * 0.8) * uTurb * uSpread * life * 0.3;
41
- x += t1 + t2;
42
- z += t3 + t2 * 0.5;
43
-
44
- float y = life * uHeight - uHeight * 0.45;
45
-
46
- // Particle grows from tiny to large, then fades to nothing
47
- float growFade = smoothstep(0.0, 0.15, life) * smoothstep(1.0, 0.6, life);
48
- float sz = aSize * uSizeScale * growFade;
60
+ // Divergence-free swirl: the curl of A = (sin(k1 y), sin(k2 z), sin(k3 x))
61
+ // is (-k2 cos(k2 z), -k3 cos(k3 x), -k1 cos(k1 y)), summed over two octaves.
62
+ vec3 curlField(vec3 p, float t){
63
+ vec3 c = -vec3(1.7 * cos(1.7 * p.z + t * 0.6), 1.3 * cos(1.3 * p.x - t * 0.4), 1.1 * cos(1.1 * p.y + t * 0.5));
64
+ vec3 q = vec3(p.y, p.z, p.x) * 2.3 + 4.0;
65
+ c += -0.5 * vec3(3.1 * cos(3.1 * q.z - t), 2.7 * cos(2.7 * q.x + t * 0.8), 2.9 * cos(2.9 * q.y - t * 0.7));
66
+ return c * 0.35;
67
+ }
49
68
 
50
- // Alpha: ramp in quickly, stay opaque, fade out
51
- vAlpha = growFade * growFade;
69
+ void main(){
70
+ float life = fract(aT + uTime * 0.11);
71
+ vLife = life;
72
+ // Morton-Taylor-Turner point-source plume: rise z ~ t^(3/4),
73
+ // width grows linearly with height (constant entrainment).
74
+ float Hs = uHeight * uBound;
75
+ float Ss = uSpread * uBound;
76
+ float z = pow(life, 0.75) * Hs;
77
+ float w = Ss * 0.35 + 0.24 * z;
78
+ vec3 base = vec3(aSeed.x * w, 0.0, aSeed.y * w);
79
+ // the plume leaves from the top of the object
80
+ vec3 pos = base + vec3(0.0, z + uBound * 0.6, 0.0);
81
+ pos += curlField(vec3(aSeed.xy * 2.0, z / max(Hs, 1e-3) * 3.0), uTime * 0.5 + aSeed.z * 6.0) * uTurbulence * w * 1.6;
82
+ // a crosswind bends the plume parabolically
83
+ pos.x += uWind * z * z / max(Hs, 1e-3);
52
84
 
53
- vec4 mv = modelViewMatrix * vec4(x, y, z, 1.0);
54
- gl_PointSize = sz * (200.0 / -mv.z);
55
- gl_Position = projectionMatrix * mv;
85
+ vec4 mv = modelViewMatrix * vec4(pos, 1.0);
86
+ float grow = smoothstep(0.0, 0.08, life);
87
+ float size = (0.45 * Ss + 0.5 * w) * grow;
88
+ // mass spreads over a growing cross-section, so density thins out
89
+ float thin = pow(clamp(Ss * 0.35 / max(w, 1e-3), 0.0, 1.0), 0.9);
90
+ vAlpha = grow * (1.0 - smoothstep(0.7, 1.0, life)) * (0.35 + 0.65 * thin);
91
+ vRot = aSeed.z * 6.2832 + life * (aSeed.z - 0.5) * 2.0;
92
+ vSeed = aSeed.z;
93
+ vLight = normalize((viewMatrix * vec4(0.35, 1.0, 0.25, 0.0)).xyz);
94
+ gl_PointSize = size * uIntensity * 900.0 / max(-mv.z, 1e-3);
95
+ gl_Position = projectionMatrix * mv;
56
96
  }
57
97
  `;
58
- const smokeFrag = /* glsl */ `
98
+ const plumeFrag = /* glsl */ `
59
99
  uniform vec3 uSmokeColor;
60
100
  uniform float uOpacity;
101
+ uniform float uHeat;
61
102
  varying float vLife;
62
103
  varying float vAlpha;
104
+ varying float vRot;
105
+ varying float vSeed;
106
+ varying vec3 vLight;
63
107
 
64
- void main() {
65
- if (vAlpha < 0.01) discard;
66
-
67
- vec2 c = gl_PointCoord - 0.5;
68
- float d = length(c);
69
- if (d > 0.5) discard;
70
-
71
- // Soft gaussian puff — not a crisp disc
72
- float g = exp(-d * d * 3.8);
108
+
109
+ float hash12(vec2 p){
110
+ vec3 p3 = fract(vec3(p.xyx) * 0.1031);
111
+ p3 += dot(p3, p3.yzx + 33.33);
112
+ return fract((p3.x + p3.y) * p3.z);
113
+ }
114
+ float vnoise2(vec2 p){
115
+ vec2 i = floor(p);
116
+ vec2 f = fract(p);
117
+ f = f * f * (3.0 - 2.0 * f);
118
+ return mix(mix(hash12(i), hash12(i + vec2(1.0, 0.0)), f.x),
119
+ mix(hash12(i + vec2(0.0, 1.0)), hash12(i + vec2(1.0, 1.0)), f.x), f.y);
120
+ }
121
+ float fbm2(vec2 p){
122
+ float a = 0.5, s = 0.0;
123
+ for (int i = 0; i < 4; i++){
124
+ s += a * vnoise2(p);
125
+ p = mat2(1.6, 1.2, -1.2, 1.6) * p;
126
+ a *= 0.5;
127
+ }
128
+ return s;
129
+ }
73
130
 
74
- // Slightly darken as smoke ages (hot white → grey → dark)
75
- float darkening = 1.0 - vLife * 0.45;
76
- vec3 col = uSmokeColor * darkening;
77
131
 
78
- gl_FragColor = vec4(col, g * vAlpha * uOpacity);
132
+ void main(){
133
+ vec2 c = gl_PointCoord * 2.0 - 1.0;
134
+ c.y = -c.y;
135
+ float cr = cos(vRot), sr = sin(vRot);
136
+ vec2 rc = mat2(cr, -sr, sr, cr) * c;
137
+ float r = length(c);
138
+ if (r > 1.0) discard;
139
+ // billows: the silhouette is eroded by fBm, so no two puffs match
140
+ float b = fbm2(rc * 1.7 + vSeed * 31.0 + vLife * 0.8);
141
+ float shape = 1.0 - smoothstep(0.35, 1.0, r + (b - 0.5) * 0.6);
142
+ if (shape < 0.01) discard;
143
+ // treat the puff as a soft sphere: wrap lighting from above plus a
144
+ // bright rim on the side facing the light
145
+ vec3 n = normalize(vec3(c * 0.9, sqrt(max(1.0 - r * r, 0.0))));
146
+ float diff = 0.5 + 0.5 * dot(n, vLight);
147
+ float rim = pow(1.0 - n.z, 2.0) * max(dot(normalize(vec3(c, 0.0) + 1e-5), vLight.xyz), 0.0) * 0.5;
148
+ vec3 col = uSmokeColor * (0.25 + 0.85 * diff) * (0.75 + 0.5 * b) + vec3(rim) * uSmokeColor;
149
+ // hot smoke glows near the source: a blackbody-like ramp that cools fast
150
+ float hot = uHeat * exp(-vLife * 7.0);
151
+ col += mix(vec3(1.0, 0.25, 0.04), vec3(1.0, 0.75, 0.35), hot) * hot * 1.6;
152
+ float a = shape * vAlpha * uOpacity;
153
+ gl_FragColor = vec4(col * a, a);
79
154
  }
80
155
  `;
81
156
  const SmokePlumeEffect = ({ boundingRadius, config, animations, onObjectReady, onObjectDispose }) => {
82
- const { particleCount, height, spread, smokeColor, turbulence, opacity, speed, intensity } = config;
83
- const startValuesCacheRef = useRef({});
157
+ const { intensity = 1, speed = 1, opacity = 0.6, particleCount = 180, height = 2, spread = 0.5, smokeColor = '#b4b4b8', turbulence = 0.5, heat = 0, wind = 0, } = config;
84
158
  const rootRef = useRef(null);
159
+ const animCache = useRef({});
85
160
  useObjectReady(rootRef, config, onObjectReady, onObjectDispose);
86
- const h = boundingRadius * height;
87
- const s = boundingRadius * spread;
88
- const { geo, mat } = useMemo(() => {
89
- const N = Math.max(40, particleCount);
90
- const aT = new Float32Array(N);
91
- const aBase = new Float32Array(N * 2);
92
- const aPhase = new Float32Array(N);
93
- const aRotOff = new Float32Array(N);
94
- const aSize = new Float32Array(N);
95
- for (let i = 0; i < N; i++) {
96
- aT[i] = i / N;
97
- // Concentrate spawns near center, sparse toward edges
98
- const r = Math.pow(Math.random(), 0.6) * s;
99
- const a = Math.random() * Math.PI * 2;
100
- aBase[i * 2] = Math.cos(a) * r;
101
- aBase[i * 2 + 1] = Math.sin(a) * r;
102
- aPhase[i] = Math.random() * Math.PI * 2;
103
- aRotOff[i] = Math.random() * Math.PI * 2;
104
- aSize[i] = 35 + Math.random() * 45; // puff screen size
105
- }
161
+ // a standalone effect gets boundingRadius 1; guard against an empty host
162
+ const b = boundingRadius > 0 ? boundingRadius : 1;
163
+ const uniforms = useMemo(() => ({
164
+ uTime: { value: 0 },
165
+ uSpeed: { value: 1 },
166
+ uBound: { value: 1 },
167
+ uIntensity: { value: 1 },
168
+ uOpacity: { value: 1 },
169
+ uHeight: { value: 2 },
170
+ uSpread: { value: 0.5 },
171
+ uTurbulence: { value: 0.5 },
172
+ uWind: { value: 0 },
173
+ uHeat: { value: 0 },
174
+ uSmokeColor: { value: new THREE.Color() },
175
+ }), []);
176
+ const materials = useMemo(() => [
177
+ new THREE.ShaderMaterial({ vertexShader: plumeVert, fragmentShader: plumeFrag, uniforms, ...PRE_NORMAL }),
178
+ ], [uniforms]);
179
+ useEffect(() => () => materials.forEach((m) => m.dispose()), [materials]);
180
+ const geometry = useMemo(() => {
181
+ const p = buildPlumeParticles(particleCount);
106
182
  const g = new THREE.BufferGeometry();
107
- g.setAttribute('position', new THREE.BufferAttribute(new Float32Array(N * 3), 3));
108
- g.setAttribute('aT', new THREE.BufferAttribute(aT, 1));
109
- g.setAttribute('aBase', new THREE.BufferAttribute(aBase, 2));
110
- g.setAttribute('aPhase', new THREE.BufferAttribute(aPhase, 1));
111
- g.setAttribute('aRotOff', new THREE.BufferAttribute(aRotOff, 1));
112
- g.setAttribute('aSize', new THREE.BufferAttribute(aSize, 1));
113
- g.frustumCulled = false;
114
- const sc = new THREE.Color(smokeColor);
115
- const m = new THREE.ShaderMaterial({
116
- vertexShader: smokeVert,
117
- fragmentShader: smokeFrag,
118
- uniforms: {
119
- u_time: { value: 0 },
120
- u_speed: { value: speed },
121
- uHeight: { value: h },
122
- uSpread: { value: s },
123
- uTurb: { value: turbulence },
124
- uSizeScale: { value: intensity },
125
- uSmokeColor: { value: new THREE.Vector3(sc.r, sc.g, sc.b) },
126
- uOpacity: { value: opacity },
127
- },
128
- transparent: true,
129
- depthWrite: false,
130
- // Normal blending (not additive) — smoke should look opaque/grey, not glowy
131
- blending: THREE.NormalBlending,
132
- });
133
- return { geo: g, mat: m };
134
- // eslint-disable-next-line react-hooks/exhaustive-deps
135
- }, [particleCount, h, s, smokeColor, turbulence, opacity, speed, intensity]);
136
- useFrame((state) => {
137
- mat.uniforms.u_time.value = state.clock.getElapsedTime();
138
- if (animations?.length) {
139
- applyGenerativeAnimations([mat], animations, startValuesCacheRef.current, Date.now(), false);
140
- }
183
+ g.setAttribute('position', new THREE.BufferAttribute(new Float32Array(p.count * 3), 3));
184
+ g.setAttribute('aT', new THREE.BufferAttribute(p.aT, 1));
185
+ g.setAttribute('aSeed', new THREE.BufferAttribute(p.aSeed, 3));
186
+ return g;
187
+ }, [particleCount]);
188
+ useEffect(() => () => geometry.dispose(), [geometry]);
189
+ useLayoutEffect(() => {
190
+ uniforms.uSpeed.value = speed;
191
+ uniforms.uBound.value = b;
192
+ uniforms.uIntensity.value = intensity;
193
+ uniforms.uOpacity.value = opacity;
194
+ uniforms.uHeight.value = height;
195
+ uniforms.uSpread.value = spread;
196
+ uniforms.uTurbulence.value = turbulence;
197
+ uniforms.uWind.value = wind;
198
+ uniforms.uHeat.value = heat;
199
+ uniforms.uSmokeColor.value.set(smokeColor);
200
+ }, [uniforms, speed, b, intensity, opacity, height, spread, turbulence, wind, heat, smokeColor]);
201
+ useFrame((_, delta) => {
202
+ uniforms.uTime.value += delta * uniforms.uSpeed.value;
203
+ if (animations?.length)
204
+ applyGenerativeAnimations(materials, animations, animCache.current, Date.now(), false);
141
205
  });
142
- return _jsx("points", { ref: rootRef, geometry: geo, material: mat });
206
+ return (_jsx("group", { ref: rootRef, children: _jsx("points", { geometry: geometry, material: materials[0], frustumCulled: false }) }));
143
207
  };
144
208
  export default SmokePlumeEffect;
@@ -1,7 +1,7 @@
1
- import React from "react";
2
- import type { SmokeRingConfig, AnimationOptions } from "@3driseai/3drise-core";
1
+ import React from 'react';
2
+ import type { AnimationOptions, SmokeRingConfig } from '@3driseai/3drise-core';
3
3
  import type { ObjectReadyProps } from '../../types/objectReady';
4
- interface SmokeRingEffectProps extends ObjectReadyProps {
4
+ export interface SmokeRingEffectProps extends ObjectReadyProps {
5
5
  boundingRadius: number;
6
6
  config: SmokeRingConfig;
7
7
  animations?: AnimationOptions[];
@@ -1 +1 @@
1
- {"version":3,"file":"SmokeRingEffect.d.ts","sourceRoot":"","sources":["../../../src/components/effects/SmokeRingEffect.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,UAAU,oBAAqB,SAAQ,gBAAgB;IACnD,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,eAAe,CAAC;IACxB,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACnC;AAiED,QAAA,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CA2FnD,CAAC;AACF,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"SmokeRingEffect.d.ts","sourceRoot":"","sources":["../../../src/components/effects/SmokeRingEffect.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsD,MAAM,OAAO,CAAC;AAG3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAG/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,WAAW,oBAAqB,SAAQ,gBAAgB;IAC1D,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,eAAe,CAAC;IACxB,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACnC;AAoND,QAAA,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CA+EnD,CAAC;AAEF,eAAe,eAAe,CAAC"}