@3driseai/3drise-viewer 0.1.16 → 0.1.18

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 (145) 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/Galaxy.d.ts.map +1 -1
  14. package/dist/components/Galaxy.js +2 -0
  15. package/dist/components/GirihGrid.d.ts +15 -0
  16. package/dist/components/GirihGrid.d.ts.map +1 -0
  17. package/dist/components/GirihGrid.js +62 -0
  18. package/dist/components/GravWaveGrid.d.ts +15 -0
  19. package/dist/components/GravWaveGrid.d.ts.map +1 -0
  20. package/dist/components/GravWaveGrid.js +62 -0
  21. package/dist/components/GridsGenerator.d.ts +2 -2
  22. package/dist/components/GridsGenerator.d.ts.map +1 -1
  23. package/dist/components/GridsGenerator.js +21 -0
  24. package/dist/components/HyperbolicGrid.d.ts +15 -0
  25. package/dist/components/HyperbolicGrid.d.ts.map +1 -0
  26. package/dist/components/HyperbolicGrid.js +61 -0
  27. package/dist/components/LayoutInstances.d.ts +13 -0
  28. package/dist/components/LayoutInstances.d.ts.map +1 -0
  29. package/dist/components/LayoutInstances.js +39 -0
  30. package/dist/components/ModelPrimitive.d.ts +2 -0
  31. package/dist/components/ModelPrimitive.d.ts.map +1 -1
  32. package/dist/components/ModelPrimitive.js +3 -3
  33. package/dist/components/ObjectNode.d.ts.map +1 -1
  34. package/dist/components/ObjectNode.js +44 -9
  35. package/dist/components/ParticlesGenerator.d.ts +10 -0
  36. package/dist/components/ParticlesGenerator.d.ts.map +1 -0
  37. package/dist/components/ParticlesGenerator.js +23 -0
  38. package/dist/components/ReactionDiffusionGrid.d.ts +15 -0
  39. package/dist/components/ReactionDiffusionGrid.d.ts.map +1 -0
  40. package/dist/components/ReactionDiffusionGrid.js +376 -0
  41. package/dist/components/WallpaperGrid.d.ts +15 -0
  42. package/dist/components/WallpaperGrid.d.ts.map +1 -0
  43. package/dist/components/WallpaperGrid.js +555 -0
  44. package/dist/components/cursor/CursorController.d.ts.map +1 -1
  45. package/dist/components/effects/BlackHoleEffect.d.ts +3 -3
  46. package/dist/components/effects/BlackHoleEffect.d.ts.map +1 -1
  47. package/dist/components/effects/BlackHoleEffect.js +293 -163
  48. package/dist/components/effects/DandelionSeedsEffect.d.ts +13 -0
  49. package/dist/components/effects/DandelionSeedsEffect.d.ts.map +1 -0
  50. package/dist/components/effects/DandelionSeedsEffect.js +510 -0
  51. package/dist/components/effects/FallingLeavesEffect.d.ts +13 -0
  52. package/dist/components/effects/FallingLeavesEffect.d.ts.map +1 -0
  53. package/dist/components/effects/FallingLeavesEffect.js +446 -0
  54. package/dist/components/effects/FieldLinesEffect.d.ts +11 -0
  55. package/dist/components/effects/FieldLinesEffect.d.ts.map +1 -0
  56. package/dist/components/effects/FieldLinesEffect.js +260 -0
  57. package/dist/components/effects/FireflySwarmEffect.d.ts +13 -0
  58. package/dist/components/effects/FireflySwarmEffect.d.ts.map +1 -0
  59. package/dist/components/effects/FireflySwarmEffect.js +408 -0
  60. package/dist/components/effects/GalaxyEffect.d.ts +13 -0
  61. package/dist/components/effects/GalaxyEffect.d.ts.map +1 -0
  62. package/dist/components/effects/GalaxyEffect.js +374 -0
  63. package/dist/components/effects/HarmonicShellEffect.d.ts +11 -0
  64. package/dist/components/effects/HarmonicShellEffect.d.ts.map +1 -0
  65. package/dist/components/effects/HarmonicShellEffect.js +209 -0
  66. package/dist/components/effects/IceCrystalsEffect.d.ts +3 -3
  67. package/dist/components/effects/IceCrystalsEffect.d.ts.map +1 -1
  68. package/dist/components/effects/IceCrystalsEffect.js +286 -166
  69. package/dist/components/effects/IceHaloEffect.d.ts +13 -0
  70. package/dist/components/effects/IceHaloEffect.d.ts.map +1 -0
  71. package/dist/components/effects/IceHaloEffect.js +306 -0
  72. package/dist/components/effects/LichtenbergEffect.d.ts +13 -0
  73. package/dist/components/effects/LichtenbergEffect.d.ts.map +1 -0
  74. package/dist/components/effects/LichtenbergEffect.js +517 -0
  75. package/dist/components/effects/MoleculesEffect.d.ts +3 -3
  76. package/dist/components/effects/MoleculesEffect.d.ts.map +1 -1
  77. package/dist/components/effects/MoleculesEffect.js +322 -164
  78. package/dist/components/effects/MurmurationEffect.d.ts +13 -0
  79. package/dist/components/effects/MurmurationEffect.d.ts.map +1 -0
  80. package/dist/components/effects/MurmurationEffect.js +539 -0
  81. package/dist/components/effects/PlanetaryRingsEffect.d.ts +13 -0
  82. package/dist/components/effects/PlanetaryRingsEffect.d.ts.map +1 -0
  83. package/dist/components/effects/PlanetaryRingsEffect.js +458 -0
  84. package/dist/components/effects/PortalEffect.d.ts +3 -3
  85. package/dist/components/effects/PortalEffect.d.ts.map +1 -1
  86. package/dist/components/effects/PortalEffect.js +240 -163
  87. package/dist/components/effects/ShockwaveEffect.d.ts +5 -3
  88. package/dist/components/effects/ShockwaveEffect.d.ts.map +1 -1
  89. package/dist/components/effects/ShockwaveEffect.js +538 -142
  90. package/dist/components/effects/SmokePlumeEffect.d.ts +3 -3
  91. package/dist/components/effects/SmokePlumeEffect.d.ts.map +1 -1
  92. package/dist/components/effects/SmokePlumeEffect.js +180 -116
  93. package/dist/components/effects/SmokeRingEffect.d.ts +3 -3
  94. package/dist/components/effects/SmokeRingEffect.d.ts.map +1 -1
  95. package/dist/components/effects/SmokeRingEffect.js +260 -135
  96. package/dist/components/effects/StrangeAttractorEffect.d.ts +11 -0
  97. package/dist/components/effects/StrangeAttractorEffect.d.ts.map +1 -0
  98. package/dist/components/effects/StrangeAttractorEffect.js +229 -0
  99. package/dist/components/effects/VolumetricFogEffect.d.ts +5 -3
  100. package/dist/components/effects/VolumetricFogEffect.d.ts.map +1 -1
  101. package/dist/components/effects/VolumetricFogEffect.js +211 -93
  102. package/dist/components/effects/WaterCausticsEffect.d.ts +13 -0
  103. package/dist/components/effects/WaterCausticsEffect.d.ts.map +1 -0
  104. package/dist/components/effects/WaterCausticsEffect.js +308 -0
  105. package/dist/components/effects/index.d.ts +9 -0
  106. package/dist/components/effects/index.d.ts.map +1 -1
  107. package/dist/components/effects/index.js +9 -0
  108. package/dist/components/index.d.ts +19 -0
  109. package/dist/components/index.d.ts.map +1 -1
  110. package/dist/components/index.js +10 -0
  111. package/dist/components/particles/BioluminescentWakeParticles.d.ts +12 -0
  112. package/dist/components/particles/BioluminescentWakeParticles.d.ts.map +1 -0
  113. package/dist/components/particles/BioluminescentWakeParticles.js +1123 -0
  114. package/dist/components/particles/BrownianDustParticles.d.ts +12 -0
  115. package/dist/components/particles/BrownianDustParticles.d.ts.map +1 -0
  116. package/dist/components/particles/BrownianDustParticles.js +757 -0
  117. package/dist/components/particles/ElectronOrbitalsParticles.d.ts +12 -0
  118. package/dist/components/particles/ElectronOrbitalsParticles.d.ts.map +1 -0
  119. package/dist/components/particles/ElectronOrbitalsParticles.js +944 -0
  120. package/dist/components/particles/GrinderSparksParticles.d.ts +12 -0
  121. package/dist/components/particles/GrinderSparksParticles.d.ts.map +1 -0
  122. package/dist/components/particles/GrinderSparksParticles.js +744 -0
  123. package/dist/components/particles/TidalStreamParticles.d.ts +12 -0
  124. package/dist/components/particles/TidalStreamParticles.d.ts.map +1 -0
  125. package/dist/components/particles/TidalStreamParticles.js +788 -0
  126. package/dist/components/particles/index.d.ts +11 -0
  127. package/dist/components/particles/index.d.ts.map +1 -0
  128. package/dist/components/particles/index.js +6 -0
  129. package/dist/constants.d.ts +1 -1
  130. package/dist/constants.d.ts.map +1 -1
  131. package/dist/constants.js +1 -1
  132. package/dist/hooks/useModelObject.d.ts +6 -1
  133. package/dist/hooks/useModelObject.d.ts.map +1 -1
  134. package/dist/hooks/useModelObject.js +19 -6
  135. package/dist/types/gridConfigs.d.ts +98 -0
  136. package/dist/types/gridConfigs.d.ts.map +1 -1
  137. package/dist/types/index.d.ts +1 -1
  138. package/dist/types/index.d.ts.map +1 -1
  139. package/dist/utils/particleTrails.d.ts +70 -0
  140. package/dist/utils/particleTrails.d.ts.map +1 -0
  141. package/dist/utils/particleTrails.js +369 -0
  142. package/dist/utils/pointerField.d.ts +28 -0
  143. package/dist/utils/pointerField.d.ts.map +1 -0
  144. package/dist/utils/pointerField.js +103 -0
  145. package/package.json +2 -2
@@ -0,0 +1,369 @@
1
+ // Particle trails — the shared trail contract for CPU-simulated particle objects.
2
+ // Three trail types, each an instanced chain of soft sprites (no 1-px GL lines):
3
+ // streak — motion blur: a straight tail along the current velocity (exposure = trailLength);
4
+ // only moving particles streak.
5
+ // comet — history tails: 10 past positions per particle, kept in 10 instanced attribute slots
6
+ // (one slot uploaded per sample), joined by Catmull–Rom splines that slide smoothly.
7
+ // echo — afterglow: particles shed ghost sprites that keep some of their velocity, spread,
8
+ // swell and fade (a ring-buffer pool with partial uploads).
9
+ // Every sample's alpha is proportional to its step length along the trail, so slow or still
10
+ // particles never stack sprites into hot spots and brightness per unit length stays even.
11
+ // The component supplies GLSL that defines
12
+ // vec3 trailShade(vec3 pSim, float f); // light at a trail sample (f: 0 head .. 1 tail)
13
+ // vec3 trailView(vec3 mv, out float mag); // optional view-space warp (e.g. a lens), else identity
14
+ // and shares its uniform object, which must include createTrailUniforms().
15
+ // Every trail has reset(i): call it when particle i teleports (respawn, wrap) so comet history
16
+ // does not draw a line from its old place.
17
+ // Optional opts.k (Float32Array n): a per-particle scalar (heat, emission, alive flag) the GLSL reads
18
+ // as TRAIL_K (1.0 when not given). Echo ghosts keep the value they had when they were shed.
19
+ import * as THREE from 'three';
20
+ /** mulberry32 — seeded PRNG, returns a function giving floats in [0,1). */
21
+ function mulberry32(seed) {
22
+ let a = seed >>> 0;
23
+ return function () {
24
+ a = (a + 0x6d2b79f5) >>> 0;
25
+ let t = a;
26
+ t = Math.imul(t ^ (t >>> 15), t | 1);
27
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
28
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
29
+ };
30
+ }
31
+ export const TRAIL_MODES = ['none', 'streak', 'comet', 'echo'];
32
+ const SLOTS = 10;
33
+ const SUB = 4; // default spline samples per comet segment (opts.sub overrides)
34
+ const CAPS = { streak: 40000, comet: 5000, echo: 8000 };
35
+ export function createTrailUniforms() {
36
+ return {
37
+ uTrailLength: { value: 1.5 },
38
+ uTrailOpacity: { value: 0.6 },
39
+ uTrailColor: { value: new THREE.Color('#ffffff') },
40
+ uTrailShare: { value: 0.3 },
41
+ uTrailHead: { value: 0 },
42
+ uTrailAcc: { value: 0 },
43
+ uTrailTime: { value: 0 },
44
+ uVelScale: { value: 1 },
45
+ };
46
+ }
47
+ export function applyTrailConfig(u, c) {
48
+ u.uTrailLength.value = c.trailLength;
49
+ u.uTrailOpacity.value = c.trailOpacity;
50
+ u.uTrailColor.value.set(c.trailColor);
51
+ u.uTrailShare.value = c.trailShare;
52
+ }
53
+ /** GUI spec for the trail knobs; `labels` names the three types for this object. */
54
+ export function trailSpec(labels) {
55
+ return {
56
+ trailMode: { options: { Off: 'none', [labels.streak]: 'streak', [labels.comet]: 'comet', [labels.echo]: 'echo' } },
57
+ trailLength: { min: 0.2, max: 6, step: 0.05 },
58
+ trailOpacity: { min: 0, max: 1, step: 0.01 },
59
+ trailColor: 'color',
60
+ trailShare: { min: 0.02, max: 1, step: 0.01 },
61
+ };
62
+ }
63
+ const HEAD = /* glsl */ `
64
+ uniform float uSize, uViewH, uIntensity, uOpacity;
65
+ uniform float uTrailLength, uTrailOpacity, uTrailAcc, uTrailTime, uVelScale;
66
+ uniform int uTrailHead;
67
+ uniform vec3 uTrailColor;
68
+ varying vec3 vCol;
69
+ varying float vA;
70
+ `;
71
+ const FRAG = /* glsl */ `
72
+ varying vec3 vCol;
73
+ varying float vA;
74
+ void main() {
75
+ vec2 d = gl_PointCoord * 2.0 - 1.0;
76
+ float r2 = dot(d, d);
77
+ if (r2 > 1.0) discard;
78
+ float a = (exp(-r2 * 4.0) + 0.25 * exp(-r2 * 1.5)) * (1.0 - r2) * vA;
79
+ if (a < 0.002) discard;
80
+ gl_FragColor = vec4(vCol, a);
81
+ }
82
+ `;
83
+ // shared tail of every trail vertex shader: shade, tint toward trailColor, size, project
84
+ function finish(sizePx) {
85
+ return /* glsl */ `
86
+ vec3 light = trailShade(p, f);
87
+ vCol = light * mix(vec3(1.0), uTrailColor * 1.25, smoothstep(0.0, 0.8, f)) * uIntensity;
88
+ vec4 mvp = modelViewMatrix * vec4(p * uSize, 1.0);
89
+ float mag;
90
+ vec3 mv = trailView(mvp.xyz, mag);
91
+ vCol *= mag;
92
+ vA = clamp(vA, 0.0, 1.0) * uOpacity * uTrailOpacity;
93
+ gl_PointSize = clamp(uSize * uViewH * ${sizePx.toFixed(4)} * (1.0 - 0.5 * f) * sqrt(mag) / -mv.z, 1.0, 40.0);
94
+ gl_Position = projectionMatrix * vec4(mv, 1.0);
95
+ `;
96
+ }
97
+ const K_ON = '#define TRAIL_K aK\nattribute float aK;\n';
98
+ const K_OFF = '#define TRAIL_K 1.0\n';
99
+ function streakShader(glsl, sizePx, spacing, kdef) {
100
+ const S = 24;
101
+ return {
102
+ S,
103
+ vertex: /* glsl */ `
104
+ ${HEAD}
105
+ attribute vec3 aP;
106
+ attribute vec3 aV;
107
+ ${kdef}
108
+ ${glsl}
109
+ void main() {
110
+ float f = position.x; // 0 head .. 1 tail
111
+ vec3 dv = aV * uVelScale * uTrailLength;
112
+ float len = length(dv);
113
+ vec3 p = aP - dv * f;
114
+ float stepLen = len / ${(S - 1).toFixed(1)};
115
+ vA = 1.2 * pow(1.0 - f, 1.4) * clamp(stepLen / ${spacing.toFixed(4)}, 0.0, 1.0) * step(0.001, f);
116
+ ${finish(sizePx)}
117
+ }
118
+ `,
119
+ };
120
+ }
121
+ function cometShader(glsl, sizePx, spacing, kdef, SUB) {
122
+ const slots = Array.from({ length: SLOTS }, (_, i) => `attribute vec3 h${i};`).join('\n');
123
+ const pick = Array.from({ length: SLOTS }, (_, i) => ` ${i === 0 ? 'if' : 'else if'} (i == ${i}) return h${i};`).join('\n');
124
+ return /* glsl */ `
125
+ ${HEAD}
126
+ attribute vec3 aP;
127
+ ${slots}
128
+ ${kdef}
129
+ ${glsl}
130
+ vec3 slot(int i) {
131
+ ${pick}
132
+ return h${SLOTS - 1};
133
+ }
134
+ // L(0) = live position, L(k) = k-th most recent stored sample
135
+ vec3 L(int k) {
136
+ if (k <= 0) return aP;
137
+ int kk = min(k, ${SLOTS});
138
+ return slot(int(mod(float(uTrailHead - (kk - 1) + ${SLOTS * 2}), ${SLOTS.toFixed(1)})));
139
+ }
140
+ vec3 catmull(vec3 a, vec3 b, vec3 c, vec3 d, float t) {
141
+ float t2 = t * t, t3 = t2 * t;
142
+ return 0.5 * ((2.0 * b) + (-a + c) * t + (2.0 * a - 5.0 * b + 4.0 * c - d) * t2 + (-a + 3.0 * b - 3.0 * c + d) * t3);
143
+ }
144
+ void main() {
145
+ float s = position.x; // 0 .. ${SLOTS} along the chain
146
+ int k = int(floor(s));
147
+ float t = s - float(k);
148
+ vec3 p0 = L(k - 1), p1 = L(k), p2 = L(k + 1), p3 = L(k + 2);
149
+ vec3 p = catmull(p0, p1, p2, p3, t);
150
+ float dt = uTrailLength / ${(SLOTS - 1).toFixed(1)};
151
+ float age = s < 1.0 ? s * uTrailAcc : uTrailAcc + (s - 1.0) * dt;
152
+ float f = clamp(age / uTrailLength, 0.0, 1.0);
153
+ float stepLen = length(p2 - p1) / ${SUB.toFixed(1)};
154
+ vA = pow(1.0 - f, 1.3) * clamp(stepLen / ${spacing.toFixed(4)}, 0.0, 1.0) * step(0.0001, s);
155
+ ${finish(sizePx)}
156
+ }
157
+ `;
158
+ }
159
+ function echoShader(glsl, sizePx, kdef) {
160
+ return /* glsl */ `
161
+ ${HEAD}
162
+ attribute vec4 aE0; // emitted position xyz, birth time
163
+ attribute vec4 aE1; // emitter velocity xyz, seed
164
+ ${kdef}
165
+ ${glsl}
166
+ void main() {
167
+ float age = uTrailTime - aE0.w;
168
+ float f = age / max(uTrailLength, 1e-3);
169
+ vec3 dir = normalize(vec3(fract(aE1.w * 13.7), fract(aE1.w * 71.3), fract(aE1.w * 37.9)) - 0.5 + 1e-3);
170
+ // ghosts keep a decaying share of the emitter's velocity and diffuse (spread ~ sqrt(age))
171
+ float carry = (1.0 - exp(-age * 1.5)) / 1.5;
172
+ vec3 p = aE0.xyz + aE1.xyz * uVelScale * carry * 0.6 + dir * 0.03 * sqrt(max(age, 0.0)) * uTrailLength;
173
+ vA = (f < 0.0 || f > 1.0) ? 0.0 : smoothstep(0.0, 0.08, f) * pow(1.0 - f, 2.0) * 0.55;
174
+ f = clamp(f, 0.0, 1.0);
175
+ ${finish(sizePx * 1.4).replace('(1.0 - 0.5 * f)', '(1.0 + 1.2 * f)')}
176
+ }
177
+ `;
178
+ }
179
+ /**
180
+ * Build a trail for a CPU particle set.
181
+ * opts: { u, n, pos (Float32Array n*3, sim units), vel (Float32Array n*3), glsl, sizePx, spacing }
182
+ * `vel` is in sim units per sim-time unit; the component keeps u.uVelScale = sim-time per real second.
183
+ */
184
+ export function createCpuTrail(mode, opts) {
185
+ if (!mode || mode === 'none')
186
+ return null;
187
+ const { u, n, pos, vel, glsl } = opts;
188
+ const kArr = opts.k || null;
189
+ const kdef = kArr ? K_ON : K_OFF;
190
+ const sizePx = opts.sizePx ?? 0.006;
191
+ const spacing = opts.spacing ?? 0.004;
192
+ const nSub = Math.max(1, Math.min(CAPS[mode], Math.round(n * Math.min(Math.max(u.uTrailShare.value, 0), 1))));
193
+ const object = new THREE.Group();
194
+ const materials = [];
195
+ const geos = [];
196
+ const mk = (vertexShader) => {
197
+ const m = new THREE.ShaderMaterial({
198
+ uniforms: u, vertexShader, fragmentShader: FRAG,
199
+ transparent: true, depthWrite: false, blending: THREE.AdditiveBlending,
200
+ });
201
+ materials.push(m);
202
+ return m;
203
+ };
204
+ if (mode === 'streak') {
205
+ const sh = streakShader(glsl, sizePx, spacing, kdef);
206
+ const g = new THREE.InstancedBufferGeometry();
207
+ const tpl = new Float32Array(sh.S * 3);
208
+ for (let i = 0; i < sh.S; i++)
209
+ tpl[i * 3] = i / (sh.S - 1);
210
+ g.setAttribute('position', new THREE.BufferAttribute(tpl, 3));
211
+ const aP = new THREE.InstancedBufferAttribute(pos, 3).setUsage(THREE.DynamicDrawUsage);
212
+ const aV = new THREE.InstancedBufferAttribute(vel, 3).setUsage(THREE.DynamicDrawUsage);
213
+ g.setAttribute('aP', aP);
214
+ g.setAttribute('aV', aV);
215
+ const aK = kArr ? new THREE.InstancedBufferAttribute(kArr, 1).setUsage(THREE.DynamicDrawUsage) : null;
216
+ if (aK)
217
+ g.setAttribute('aK', aK);
218
+ g.instanceCount = nSub;
219
+ geos.push(g);
220
+ const pts = new THREE.Points(g, mk(sh.vertex));
221
+ pts.frustumCulled = false;
222
+ object.add(pts);
223
+ return {
224
+ object, materials, nSub,
225
+ reset() { },
226
+ update() { aP.needsUpdate = true; aV.needsUpdate = true; if (aK)
227
+ aK.needsUpdate = true; },
228
+ dispose() { geos.forEach((x) => x.dispose()); materials.forEach((x) => x.dispose()); },
229
+ };
230
+ }
231
+ if (mode === 'comet') {
232
+ const g = new THREE.InstancedBufferGeometry();
233
+ const SUBn = opts.sub || SUB; // spline samples per history segment (fast particles need more)
234
+ const M = SLOTS * SUBn + 1;
235
+ const tpl = new Float32Array(M * 3);
236
+ for (let i = 0; i < M; i++)
237
+ tpl[i * 3] = i / SUBn;
238
+ g.setAttribute('position', new THREE.BufferAttribute(tpl, 3));
239
+ const aP = new THREE.InstancedBufferAttribute(pos, 3).setUsage(THREE.DynamicDrawUsage);
240
+ g.setAttribute('aP', aP);
241
+ const aK = kArr ? new THREE.InstancedBufferAttribute(kArr, 1).setUsage(THREE.DynamicDrawUsage) : null;
242
+ if (aK)
243
+ g.setAttribute('aK', aK);
244
+ const slots = [];
245
+ for (let s = 0; s < SLOTS; s++) {
246
+ const arr = new Float32Array(nSub * 3);
247
+ arr.set(pos.subarray(0, nSub * 3));
248
+ const at = new THREE.InstancedBufferAttribute(arr, 3).setUsage(THREE.DynamicDrawUsage);
249
+ g.setAttribute('h' + s, at);
250
+ slots.push(at);
251
+ }
252
+ g.instanceCount = nSub;
253
+ geos.push(g);
254
+ const pts = new THREE.Points(g, mk(cometShader(glsl, sizePx, spacing, kdef, SUBn)));
255
+ pts.frustumCulled = false;
256
+ object.add(pts);
257
+ let head = 0, acc = 0, dirtyReset = false;
258
+ return {
259
+ object, materials, nSub,
260
+ // a particle that was recycled (respawned elsewhere) starts a fresh history
261
+ reset(i) {
262
+ if (i >= nSub)
263
+ return;
264
+ const x = pos[i * 3], y = pos[i * 3 + 1], z = pos[i * 3 + 2];
265
+ for (let s = 0; s < SLOTS; s++) {
266
+ const a = slots[s].array;
267
+ a[i * 3] = x;
268
+ a[i * 3 + 1] = y;
269
+ a[i * 3 + 2] = z;
270
+ }
271
+ dirtyReset = true;
272
+ },
273
+ update(dt) {
274
+ aP.needsUpdate = true;
275
+ if (aK)
276
+ aK.needsUpdate = true;
277
+ if (dirtyReset) {
278
+ for (let s = 0; s < SLOTS; s++)
279
+ slots[s].needsUpdate = true;
280
+ dirtyReset = false;
281
+ }
282
+ const interval = Math.max(u.uTrailLength.value, 0.05) / (SLOTS - 1);
283
+ acc += dt;
284
+ if (acc >= interval) {
285
+ acc = Math.min(acc - interval, interval);
286
+ head = (head + 1) % SLOTS;
287
+ slots[head].array.set(pos.subarray(0, nSub * 3));
288
+ slots[head].needsUpdate = true;
289
+ }
290
+ u.uTrailHead.value = head;
291
+ u.uTrailAcc.value = acc;
292
+ },
293
+ dispose() { geos.forEach((x) => x.dispose()); materials.forEach((x) => x.dispose()); },
294
+ };
295
+ }
296
+ // echo
297
+ const P = nSub * 6;
298
+ const e0 = new Float32Array(P * 4);
299
+ const e1 = new Float32Array(P * 4);
300
+ const rng = mulberry32(0xec40);
301
+ for (let i = 0; i < P; i++) {
302
+ e0[i * 4 + 3] = -1e9;
303
+ e1[i * 4 + 3] = rng();
304
+ }
305
+ const g = new THREE.BufferGeometry();
306
+ g.setAttribute('position', new THREE.BufferAttribute(new Float32Array(P * 3), 3));
307
+ const a0 = new THREE.BufferAttribute(e0, 4).setUsage(THREE.DynamicDrawUsage);
308
+ const a1 = new THREE.BufferAttribute(e1, 4).setUsage(THREE.DynamicDrawUsage);
309
+ g.setAttribute('aE0', a0);
310
+ g.setAttribute('aE1', a1);
311
+ const eK = kArr ? new Float32Array(P) : null;
312
+ const a2 = eK ? new THREE.BufferAttribute(eK, 1).setUsage(THREE.DynamicDrawUsage) : null;
313
+ if (a2)
314
+ g.setAttribute('aK', a2);
315
+ geos.push(g);
316
+ const pts = new THREE.Points(g, mk(echoShader(glsl, sizePx, kdef)));
317
+ pts.frustumCulled = false;
318
+ object.add(pts);
319
+ let write = 0, src = 0, emitAcc = 0;
320
+ return {
321
+ object, materials, nSub,
322
+ reset() { },
323
+ update(dt) {
324
+ u.uTrailTime.value += dt;
325
+ const t = u.uTrailTime.value;
326
+ // each tracked particle sheds a ghost every trailLength / 6
327
+ emitAcc += (dt * nSub * 6) / Math.max(u.uTrailLength.value, 0.05);
328
+ let k = Math.min(Math.floor(emitAcc), P);
329
+ emitAcc -= k;
330
+ if (k <= 0)
331
+ return;
332
+ const start = write;
333
+ for (let q = 0; q < k; q++) {
334
+ const w = write * 4, s = src * 3;
335
+ e0[w] = pos[s];
336
+ e0[w + 1] = pos[s + 1];
337
+ e0[w + 2] = pos[s + 2];
338
+ e0[w + 3] = t;
339
+ e1[w] = vel[s];
340
+ e1[w + 1] = vel[s + 1];
341
+ e1[w + 2] = vel[s + 2];
342
+ if (eK)
343
+ eK[write] = kArr[src];
344
+ write = (write + 1) % P;
345
+ src = (src + 1) % nSub;
346
+ }
347
+ // partial uploads (two ranges when the ring wraps)
348
+ const first = Math.min(k, P - start);
349
+ a0.clearUpdateRanges();
350
+ a1.clearUpdateRanges();
351
+ a0.addUpdateRange(start * 4, first * 4);
352
+ a1.addUpdateRange(start * 4, first * 4);
353
+ if (k > first) {
354
+ a0.addUpdateRange(0, (k - first) * 4);
355
+ a1.addUpdateRange(0, (k - first) * 4);
356
+ }
357
+ a0.needsUpdate = true;
358
+ a1.needsUpdate = true;
359
+ if (a2) {
360
+ a2.clearUpdateRanges();
361
+ a2.addUpdateRange(start, first);
362
+ if (k > first)
363
+ a2.addUpdateRange(0, k - first);
364
+ a2.needsUpdate = true;
365
+ }
366
+ },
367
+ dispose() { geos.forEach((x) => x.dispose()); materials.forEach((x) => x.dispose()); },
368
+ };
369
+ }
@@ -0,0 +1,28 @@
1
+ import * as THREE from 'three';
2
+ export declare const POINTER_IDLE = 1.4;
3
+ export interface PointerUniforms {
4
+ uPointer: THREE.IUniform<THREE.Vector3>;
5
+ uPointerVel: THREE.IUniform<THREE.Vector3>;
6
+ uPointerActive: THREE.IUniform<number>;
7
+ /** Optional: the component's mode index; -1 means off. */
8
+ uPointerMode?: THREE.IUniform<number>;
9
+ }
10
+ export declare function createPointerUniforms(): PointerUniforms;
11
+ export interface PointerState {
12
+ ndc: THREE.Vector2;
13
+ inside: boolean;
14
+ lastMove: number;
15
+ clock: number;
16
+ fresh: boolean;
17
+ raw: THREE.Vector3;
18
+ prevRaw: THREE.Vector3;
19
+ }
20
+ export declare function createPointerState(): PointerState;
21
+ /** Wire DOM pointer events into a pointer state. Read-only: never prevents or stops anything. */
22
+ export declare function bindPointer(el: HTMLElement, s: PointerState): () => void;
23
+ /** One pointer state per component, bound to the R3F event source (falls back to the canvas). */
24
+ export declare function usePointerField(): PointerState;
25
+ /** Advance the pointer uniforms by dt. `anchor` (sim units) is where the interaction plane passes;
26
+ * `normal` (object-local) pins the plane's orientation (e.g. a water surface) instead of facing the camera. */
27
+ export declare function stepPointerField(u: PointerUniforms, s: PointerState, camera: THREE.Camera, object: THREE.Object3D, size: number, dt: number, anchor?: THREE.Vector3, normal?: THREE.Vector3): void;
28
+ //# sourceMappingURL=pointerField.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pointerField.d.ts","sourceRoot":"","sources":["../../src/utils/pointerField.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,eAAO,MAAM,YAAY,MAAM,CAAC;AAEhC,MAAM,WAAW,eAAe;IAC5B,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACxC,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3C,cAAc,EAAE,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACvC,0DAA0D;IAC1D,YAAY,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;CACzC;AAED,wBAAgB,qBAAqB,IAAI,eAAe,CAMvD;AAED,MAAM,WAAW,YAAY;IACzB,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,KAAK,CAAC,OAAO,CAAC;IACnB,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC;CAC1B;AAED,wBAAgB,kBAAkB,IAAI,YAAY,CAEjD;AAED,iGAAiG;AACjG,wBAAgB,WAAW,CAAC,EAAE,EAAE,WAAW,EAAE,CAAC,EAAE,YAAY,GAAG,MAAM,IAAI,CAmBxE;AAED,iGAAiG;AACjG,wBAAgB,eAAe,IAAI,YAAY,CAM9C;AASD;gHACgH;AAChH,wBAAgB,gBAAgB,CAC5B,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,QAAQ,EACjF,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,OAAO,GACzE,IAAI,CA8BN"}
@@ -0,0 +1,103 @@
1
+ // Pointer field — the shared mouse contract every generative particle object uses.
2
+ // The pointer is projected onto the plane through an anchor of the object that faces the camera
3
+ // and expressed in the component's sim units (object-local / size). Components read
4
+ // uPointer (vec3, sim units), uPointerVel (vec3, sim units per second, smoothed),
5
+ // uPointerActive (0..1, eases in on movement, fades after POINTER_IDLE s of stillness or on leave)
6
+ // and decide themselves what the cursor is physically. pointerMode 'none' maps to uPointerMode = -1:
7
+ // the cursor then does nothing and costs nothing. See the 3drise-particles skill.
8
+ import { useEffect, useMemo } from 'react';
9
+ import { useThree } from '@react-three/fiber';
10
+ import * as THREE from 'three';
11
+ export const POINTER_IDLE = 1.4;
12
+ export function createPointerUniforms() {
13
+ return {
14
+ uPointer: { value: new THREE.Vector3() },
15
+ uPointerVel: { value: new THREE.Vector3() },
16
+ uPointerActive: { value: 0 },
17
+ };
18
+ }
19
+ export function createPointerState() {
20
+ return { ndc: new THREE.Vector2(), inside: false, lastMove: -1e9, clock: 0, fresh: true, raw: new THREE.Vector3(), prevRaw: new THREE.Vector3() };
21
+ }
22
+ /** Wire DOM pointer events into a pointer state. Read-only: never prevents or stops anything. */
23
+ export function bindPointer(el, s) {
24
+ const move = (e) => {
25
+ const r = el.getBoundingClientRect();
26
+ s.ndc.set(((e.clientX - r.left) / r.width) * 2 - 1, -((e.clientY - r.top) / r.height) * 2 + 1);
27
+ if (!s.inside)
28
+ s.fresh = true;
29
+ s.inside = true;
30
+ s.lastMove = s.clock;
31
+ };
32
+ const leave = () => { s.inside = false; };
33
+ el.addEventListener('pointermove', move, { passive: true });
34
+ el.addEventListener('pointerdown', move, { passive: true });
35
+ el.addEventListener('pointerleave', leave);
36
+ el.addEventListener('pointercancel', leave);
37
+ return () => {
38
+ el.removeEventListener('pointermove', move);
39
+ el.removeEventListener('pointerdown', move);
40
+ el.removeEventListener('pointerleave', leave);
41
+ el.removeEventListener('pointercancel', leave);
42
+ };
43
+ }
44
+ /** One pointer state per component, bound to the R3F event source (falls back to the canvas). */
45
+ export function usePointerField() {
46
+ const gl = useThree((st) => st.gl);
47
+ const connected = useThree((st) => st.events.connected);
48
+ const state = useMemo(createPointerState, []);
49
+ useEffect(() => bindPointer(connected ?? gl.domElement, state), [gl, connected, state]);
50
+ return state;
51
+ }
52
+ const _ray = new THREE.Raycaster();
53
+ const _plane = new THREE.Plane();
54
+ const _n = new THREE.Vector3();
55
+ const _c = new THREE.Vector3();
56
+ const _hit = new THREE.Vector3();
57
+ const _v = new THREE.Vector3();
58
+ /** Advance the pointer uniforms by dt. `anchor` (sim units) is where the interaction plane passes;
59
+ * `normal` (object-local) pins the plane's orientation (e.g. a water surface) instead of facing the camera. */
60
+ export function stepPointerField(u, s, camera, object, size, dt, anchor, normal) {
61
+ s.clock += dt;
62
+ if (u.uPointerMode && u.uPointerMode.value < 0) {
63
+ u.uPointerActive.value = 0;
64
+ u.uPointerVel.value.set(0, 0, 0);
65
+ return;
66
+ }
67
+ const active = s.inside && s.clock - s.lastMove < POINTER_IDLE ? 1 : 0;
68
+ const rate = active > u.uPointerActive.value ? 6 : 1.5;
69
+ u.uPointerActive.value += (active - u.uPointerActive.value) * Math.min(1, dt * rate);
70
+ if (!s.inside) {
71
+ u.uPointerVel.value.multiplyScalar(Math.max(0, 1 - dt * 4));
72
+ return;
73
+ }
74
+ object.updateWorldMatrix(true, false);
75
+ if (anchor)
76
+ object.localToWorld(_c.copy(anchor).multiplyScalar(size));
77
+ else
78
+ object.getWorldPosition(_c);
79
+ if (normal)
80
+ _n.copy(normal).transformDirection(object.matrixWorld);
81
+ else
82
+ camera.getWorldDirection(_n);
83
+ _plane.setFromNormalAndCoplanarPoint(_n, _c);
84
+ _ray.setFromCamera(s.ndc, camera);
85
+ if (!_ray.ray.intersectPlane(_plane, _hit))
86
+ return;
87
+ object.worldToLocal(_hit);
88
+ _hit.divideScalar(Math.max(size, 1e-4));
89
+ if (s.fresh) {
90
+ s.prevRaw.copy(_hit);
91
+ u.uPointer.value.copy(_hit);
92
+ s.fresh = false;
93
+ }
94
+ s.raw.copy(_hit);
95
+ if (dt > 0) {
96
+ _v.copy(s.raw).sub(s.prevRaw).divideScalar(dt);
97
+ if (_v.length() > 8)
98
+ _v.setLength(8);
99
+ u.uPointerVel.value.lerp(_v, Math.min(1, dt / 0.08));
100
+ }
101
+ s.prevRaw.copy(s.raw);
102
+ u.uPointer.value.lerp(s.raw, Math.min(1, dt / 0.05));
103
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@3driseai/3drise-viewer",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -39,6 +39,6 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "@3driseai/3drise-common": "^1.0.1",
42
- "@3driseai/3drise-core": "^1.0.10"
42
+ "@3driseai/3drise-core": "^1.0.12"
43
43
  }
44
44
  }