@ntalmagor/3drize-viewer 0.1.25 → 0.1.26

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 (119) hide show
  1. package/dist/components/Bubbles.d.ts +8 -0
  2. package/dist/components/Bubbles.d.ts.map +1 -0
  3. package/dist/components/Bubbles.js +106 -0
  4. package/dist/components/Clouds.js +18 -10
  5. package/dist/components/CreatedObject.js +1 -1
  6. package/dist/components/Earth.d.ts +18 -0
  7. package/dist/components/Earth.d.ts.map +1 -0
  8. package/dist/components/Earth.js +43 -0
  9. package/dist/components/Glyphs.d.ts +8 -0
  10. package/dist/components/Glyphs.d.ts.map +1 -0
  11. package/dist/components/Glyphs.js +58 -0
  12. package/dist/components/GridHelper.js +1 -1
  13. package/dist/components/Moon.d.ts.map +1 -1
  14. package/dist/components/Moon.js +20 -6
  15. package/dist/components/ObjectNode.d.ts.map +1 -1
  16. package/dist/components/ObjectNode.js +23 -5
  17. package/dist/components/PathRenderer.d.ts +0 -2
  18. package/dist/components/PathRenderer.d.ts.map +1 -1
  19. package/dist/components/PathRenderer.js +4 -1
  20. package/dist/components/ProjectLoader.d.ts.map +1 -1
  21. package/dist/components/ProjectLoader.js +1 -7
  22. package/dist/components/SceneBuilder.d.ts.map +1 -1
  23. package/dist/components/SceneBuilder.js +2 -1
  24. package/dist/components/SkySystem.d.ts.map +1 -1
  25. package/dist/components/SkySystem.js +2 -2
  26. package/dist/components/Skybox.js +1 -1
  27. package/dist/components/SparklesBurst.d.ts +8 -0
  28. package/dist/components/SparklesBurst.d.ts.map +1 -0
  29. package/dist/components/SparklesBurst.js +62 -0
  30. package/dist/components/cursor/AuraCursor.d.ts +6 -0
  31. package/dist/components/cursor/AuraCursor.d.ts.map +1 -0
  32. package/dist/components/cursor/AuraCursor.js +30 -0
  33. package/dist/components/cursor/AuroraCursor.d.ts +6 -0
  34. package/dist/components/cursor/AuroraCursor.d.ts.map +1 -0
  35. package/dist/components/cursor/AuroraCursor.js +207 -0
  36. package/dist/components/cursor/BubblesCursor.d.ts +6 -0
  37. package/dist/components/cursor/BubblesCursor.d.ts.map +1 -0
  38. package/dist/components/cursor/BubblesCursor.js +184 -0
  39. package/dist/components/cursor/CometCursor.d.ts +6 -0
  40. package/dist/components/cursor/CometCursor.d.ts.map +1 -0
  41. package/dist/components/cursor/CometCursor.js +191 -0
  42. package/dist/components/cursor/CrosshairCursor.d.ts +6 -0
  43. package/dist/components/cursor/CrosshairCursor.d.ts.map +1 -0
  44. package/dist/components/cursor/CrosshairCursor.js +142 -0
  45. package/dist/components/cursor/CursorActions.d.ts +8 -0
  46. package/dist/components/cursor/CursorActions.d.ts.map +1 -0
  47. package/dist/components/cursor/CursorActions.js +124 -0
  48. package/dist/components/cursor/CursorController.d.ts +10 -0
  49. package/dist/components/cursor/CursorController.d.ts.map +1 -0
  50. package/dist/components/cursor/CursorController.js +5 -0
  51. package/dist/components/cursor/CursorEffects.d.ts +16 -0
  52. package/dist/components/cursor/CursorEffects.d.ts.map +1 -0
  53. package/dist/components/cursor/CursorEffects.js +110 -0
  54. package/dist/components/cursor/FerrofluidCursor.d.ts +6 -0
  55. package/dist/components/cursor/FerrofluidCursor.d.ts.map +1 -0
  56. package/dist/components/cursor/FerrofluidCursor.js +102 -0
  57. package/dist/components/cursor/GlyphRingCursor.d.ts +6 -0
  58. package/dist/components/cursor/GlyphRingCursor.d.ts.map +1 -0
  59. package/dist/components/cursor/GlyphRingCursor.js +123 -0
  60. package/dist/components/cursor/LightningCursor.d.ts +6 -0
  61. package/dist/components/cursor/LightningCursor.d.ts.map +1 -0
  62. package/dist/components/cursor/LightningCursor.js +28 -0
  63. package/dist/components/cursor/LiquidMetalCursor.d.ts +6 -0
  64. package/dist/components/cursor/LiquidMetalCursor.d.ts.map +1 -0
  65. package/dist/components/cursor/LiquidMetalCursor.js +61 -0
  66. package/dist/components/cursor/OrbCursor.d.ts +6 -0
  67. package/dist/components/cursor/OrbCursor.d.ts.map +1 -0
  68. package/dist/components/cursor/OrbCursor.js +32 -0
  69. package/dist/components/cursor/PlasmaWhipCursor.d.ts +6 -0
  70. package/dist/components/cursor/PlasmaWhipCursor.d.ts.map +1 -0
  71. package/dist/components/cursor/PlasmaWhipCursor.js +134 -0
  72. package/dist/components/cursor/SparklesCursor.d.ts +6 -0
  73. package/dist/components/cursor/SparklesCursor.d.ts.map +1 -0
  74. package/dist/components/cursor/SparklesCursor.js +151 -0
  75. package/dist/components/cursor/TeslaCursor.d.ts +6 -0
  76. package/dist/components/cursor/TeslaCursor.d.ts.map +1 -0
  77. package/dist/components/cursor/TeslaCursor.js +223 -0
  78. package/dist/components/cursor/clickActions/BoltFlashAction.d.ts +11 -0
  79. package/dist/components/cursor/clickActions/BoltFlashAction.d.ts.map +1 -0
  80. package/dist/components/cursor/clickActions/BoltFlashAction.js +62 -0
  81. package/dist/components/cursor/clickActions/GlyphInscriptionAction.d.ts +11 -0
  82. package/dist/components/cursor/clickActions/GlyphInscriptionAction.d.ts.map +1 -0
  83. package/dist/components/cursor/clickActions/GlyphInscriptionAction.js +114 -0
  84. package/dist/components/cursor/clickActions/ImplosionAction.d.ts +11 -0
  85. package/dist/components/cursor/clickActions/ImplosionAction.d.ts.map +1 -0
  86. package/dist/components/cursor/clickActions/ImplosionAction.js +93 -0
  87. package/dist/components/cursor/clickActions/PhaseShiftAction.d.ts +11 -0
  88. package/dist/components/cursor/clickActions/PhaseShiftAction.d.ts.map +1 -0
  89. package/dist/components/cursor/clickActions/PhaseShiftAction.js +71 -0
  90. package/dist/components/cursor/clickActions/PlasmaBombAction.d.ts +11 -0
  91. package/dist/components/cursor/clickActions/PlasmaBombAction.d.ts.map +1 -0
  92. package/dist/components/cursor/clickActions/PlasmaBombAction.js +98 -0
  93. package/dist/components/cursor/clickActions/RealityTearAction.d.ts +11 -0
  94. package/dist/components/cursor/clickActions/RealityTearAction.d.ts.map +1 -0
  95. package/dist/components/cursor/clickActions/RealityTearAction.js +92 -0
  96. package/dist/components/cursor/clickActions/ShockwaveAction.d.ts +11 -0
  97. package/dist/components/cursor/clickActions/ShockwaveAction.d.ts.map +1 -0
  98. package/dist/components/cursor/clickActions/ShockwaveAction.js +55 -0
  99. package/dist/components/cursor/clickActions/SparklesBurstAction.d.ts +11 -0
  100. package/dist/components/cursor/clickActions/SparklesBurstAction.d.ts.map +1 -0
  101. package/dist/components/cursor/clickActions/SparklesBurstAction.js +99 -0
  102. package/dist/components/cursor/clickActions/VoronoiFractureAction.d.ts +11 -0
  103. package/dist/components/cursor/clickActions/VoronoiFractureAction.d.ts.map +1 -0
  104. package/dist/components/cursor/clickActions/VoronoiFractureAction.js +149 -0
  105. package/dist/hooks/useContinuousEffects.js +2 -7
  106. package/dist/hooks/useMeshController.d.ts.map +1 -1
  107. package/dist/hooks/useMeshController.js +78 -28
  108. package/dist/hooks/useSequenceAnimation.d.ts.map +1 -1
  109. package/dist/hooks/useSequenceAnimation.js +38 -17
  110. package/dist/index.d.ts +4 -0
  111. package/dist/index.d.ts.map +1 -1
  112. package/dist/index.js +3 -0
  113. package/dist/utils/CameraSingleton.d.ts +8 -1
  114. package/dist/utils/CameraSingleton.d.ts.map +1 -1
  115. package/dist/utils/CameraSingleton.js +22 -1
  116. package/dist/utils/cursorPicking.d.ts +8 -0
  117. package/dist/utils/cursorPicking.d.ts.map +1 -0
  118. package/dist/utils/cursorPicking.js +35 -0
  119. package/package.json +2 -4
@@ -0,0 +1,62 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useRef } from 'react';
3
+ import { useFrame } from '@react-three/fiber';
4
+ import * as THREE from 'three';
5
+ import { SparklesBurstMaterial } from '@ntalmagor/3drize-core';
6
+ const SparklesBurst = ({ settings }) => {
7
+ const config = settings.config;
8
+ const planeSize = useMemo(() => Math.max(0.05, config.scale ?? 2.5), [config.scale]);
9
+ const geometry = useMemo(() => new THREE.PlaneGeometry(planeSize, planeSize, 1, 1), [planeSize]);
10
+ const material = useMemo(() => {
11
+ const m = new SparklesBurstMaterial();
12
+ m.transparent = true;
13
+ m.depthWrite = false;
14
+ m.side = THREE.DoubleSide;
15
+ m.blending = THREE.AdditiveBlending;
16
+ return m;
17
+ }, []);
18
+ useEffect(() => {
19
+ const u = material.uniforms;
20
+ u.uCoreColor.value.set(config.coreColor ?? '#ffffff');
21
+ u.uBurstColor.value.set(config.burstColor ?? '#ffaa00');
22
+ u.uRayCount.value = Math.max(3, config.rayCount ?? 8);
23
+ u.uIntensity.value = config.intensity ?? 1.0;
24
+ u.uOpacity.value = config.opacity ?? 1.0;
25
+ u.uHoverColor.value.set(config.hoverColor ?? '#ffffff');
26
+ u.uHoverRadius.value = config.hoverRadius ?? 0.3;
27
+ u.uHoverIntensity.value = config.hoverIntensity ?? 0.0;
28
+ }, [
29
+ config.coreColor,
30
+ config.burstColor,
31
+ config.rayCount,
32
+ config.intensity,
33
+ config.opacity,
34
+ config.hoverColor,
35
+ config.hoverRadius,
36
+ config.hoverIntensity,
37
+ material,
38
+ ]);
39
+ useEffect(() => () => { geometry.dispose(); material.dispose(); }, [geometry, material]);
40
+ // Track elapsed time JS-side so loop on/off toggles cleanly without
41
+ // depending on the global clock for the progress phase.
42
+ const startRef = useRef(null);
43
+ // Reset start when loop toggles so behaviour is predictable.
44
+ useEffect(() => { startRef.current = null; }, [config.loop, config.durationMs]);
45
+ useFrame((state) => {
46
+ const now = state.clock.getElapsedTime();
47
+ if (startRef.current === null)
48
+ startRef.current = now;
49
+ const elapsedMs = (now - startRef.current) * 1000;
50
+ const duration = Math.max(1, config.durationMs ?? 1500);
51
+ const loop = config.loop ?? true;
52
+ const progress = loop
53
+ ? (elapsedMs % duration) / duration
54
+ : Math.min(elapsedMs / duration, 1);
55
+ material.uniforms.uProgress.value = progress;
56
+ material.uniforms.uTime.value = now;
57
+ });
58
+ if (!settings.meshSettings.visible)
59
+ return null;
60
+ return _jsx("mesh", { geometry: geometry, material: material, frustumCulled: false });
61
+ };
62
+ export default SparklesBurst;
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ import type { AuraCursorConfig } from '@ntalmagor/3drize-core';
3
+ import type { CursorTypeProps } from './CursorController';
4
+ declare const AuraCursor: React.FC<CursorTypeProps<AuraCursorConfig>>;
5
+ export default AuraCursor;
6
+ //# sourceMappingURL=AuraCursor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuraCursor.d.ts","sourceRoot":"","sources":["../../../src/components/cursor/AuraCursor.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA0B,MAAM,OAAO,CAAC;AAG/C,OAAO,KAAK,EAAc,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG3E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE1D,QAAA,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAAC,gBAAgB,CAAC,CAkC3D,CAAC;AAEF,eAAe,UAAU,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useMemo, useRef } from 'react';
3
+ import { useFrame } from '@react-three/fiber';
4
+ import AuraEffect from '../effects/AuraEffect.js';
5
+ const AuraCursor = ({ config, worldPosRef, idleMsRef, }) => {
6
+ const groupRef = useRef(null);
7
+ const auraConfig = useMemo(() => ({
8
+ enabled: true,
9
+ intensity: config.intensity,
10
+ speed: 1,
11
+ color: config.color,
12
+ opacity: config.opacity,
13
+ layerCount: 2,
14
+ fresnelPower: config.falloff,
15
+ pulseSpeed: 1.5,
16
+ innerColor: config.color,
17
+ outerColor: config.color,
18
+ noiseAmount: 0.3,
19
+ sizeMultiplier: 2,
20
+ }), [config]);
21
+ useFrame(() => {
22
+ if (!groupRef.current)
23
+ return;
24
+ groupRef.current.position.copy(worldPosRef.current);
25
+ const idleFactor = Math.max(0, 1 - idleMsRef.current / Math.max(config.idleFadeMs, 1));
26
+ groupRef.current.scale.setScalar(config.scale * (0.75 + 0.25 * idleFactor));
27
+ });
28
+ return (_jsx("group", { ref: groupRef, children: _jsx(AuraEffect, { boundingRadius: 0.2, config: auraConfig }) }));
29
+ };
30
+ export default AuraCursor;
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ import type { AuroraCursorConfig } from '@ntalmagor/3drize-core';
3
+ import type { CursorTypeProps } from './CursorController';
4
+ declare const AuroraCursor: React.FC<CursorTypeProps<AuroraCursorConfig>>;
5
+ export default AuroraCursor;
6
+ //# sourceMappingURL=AuroraCursor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuroraCursor.d.ts","sourceRoot":"","sources":["../../../src/components/cursor/AuroraCursor.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAqC,MAAM,OAAO,CAAC;AAG1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAyE1D,QAAA,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAAC,kBAAkB,CAAC,CAsJ/D,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -0,0 +1,207 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useRef } from 'react';
3
+ import { useFrame, useThree } from '@react-three/fiber';
4
+ import * as THREE from 'three';
5
+ const MAX_TRAIL = 200;
6
+ const vertexShader = /* glsl */ `
7
+ attribute float aT;
8
+ uniform float uTime;
9
+ varying float vT;
10
+ varying vec2 vUv;
11
+ void main() {
12
+ vT = aT;
13
+ vUv = uv;
14
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
15
+ }
16
+ `;
17
+ const fragmentShader = /* glsl */ `
18
+ uniform vec3 uColor;
19
+ uniform float uOpacity;
20
+ uniform float uTime;
21
+ uniform float uFlowSpeed;
22
+ varying float vT;
23
+ varying vec2 vUv;
24
+
25
+ float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }
26
+ float noise(vec2 p) {
27
+ vec2 i = floor(p);
28
+ vec2 f = fract(p);
29
+ f = f * f * (3.0 - 2.0 * f);
30
+ return mix(
31
+ mix(hash(i + vec2(0,0)), hash(i + vec2(1,0)), f.x),
32
+ mix(hash(i + vec2(0,1)), hash(i + vec2(1,1)), f.x),
33
+ f.y);
34
+ }
35
+ float fbm(vec2 p) {
36
+ float v = 0.0;
37
+ float a = 0.5;
38
+ for (int i = 0; i < 4; i++) {
39
+ v += a * noise(p);
40
+ p *= 2.03;
41
+ a *= 0.5;
42
+ }
43
+ return v;
44
+ }
45
+
46
+ void main() {
47
+ // vT is 0 at the trail tail, 1 at the cursor (head).
48
+ // vUv.y is 0..1 across ribbon width.
49
+ float t = uTime * uFlowSpeed;
50
+ float n = fbm(vec2(vT * 6.0 - t, vUv.y * 2.0));
51
+
52
+ // Vertical curtain falloff
53
+ float curtain = smoothstep(0.0, 0.2, vUv.y) * (1.0 - smoothstep(0.8, 1.0, vUv.y));
54
+
55
+ // Aurora multi-band color: green/cyan/magenta mix driven by noise + uColor
56
+ vec3 green = vec3(0.1, 0.95, 0.6);
57
+ vec3 cyan = vec3(0.2, 0.7, 1.0);
58
+ vec3 magenta = vec3(0.9, 0.3, 0.95);
59
+ vec3 aurora = mix(green, cyan, smoothstep(0.3, 0.7, n));
60
+ aurora = mix(aurora, magenta, smoothstep(0.7, 0.95, n));
61
+ aurora = mix(aurora, uColor, 0.35);
62
+
63
+ // Head glow
64
+ float head = smoothstep(0.7, 1.0, vT);
65
+ aurora += head * 0.4;
66
+
67
+ float tailFade = smoothstep(0.0, 0.15, vT);
68
+ float alpha = curtain * (0.35 + n * 0.6) * tailFade * uOpacity;
69
+ if (alpha < 0.005) discard;
70
+ gl_FragColor = vec4(aurora, alpha);
71
+ }
72
+ `;
73
+ const AuroraCursor = ({ config, worldPosRef, hoverRef, idleMsRef, }) => {
74
+ const N = Math.min(MAX_TRAIL, Math.max(4, config.trailLength));
75
+ const { camera } = useThree();
76
+ // History positions (newest at index 0, oldest at N-1)
77
+ const history = useMemo(() => {
78
+ const arr = new Float32Array(MAX_TRAIL * 3);
79
+ for (let i = 0; i < MAX_TRAIL; i++)
80
+ arr[i * 3 + 1] = -1e6;
81
+ return arr;
82
+ }, []);
83
+ // 2 verts per segment, 6 indices per quad
84
+ const positions = useMemo(() => new Float32Array(MAX_TRAIL * 2 * 3), []);
85
+ const uvs = useMemo(() => new Float32Array(MAX_TRAIL * 2 * 2), []);
86
+ const ts = useMemo(() => new Float32Array(MAX_TRAIL * 2), []);
87
+ const indices = useMemo(() => new Uint16Array((MAX_TRAIL - 1) * 6), []);
88
+ const geometry = useMemo(() => {
89
+ const g = new THREE.BufferGeometry();
90
+ g.setAttribute('position', new THREE.BufferAttribute(positions, 3));
91
+ g.setAttribute('uv', new THREE.BufferAttribute(uvs, 2));
92
+ g.setAttribute('aT', new THREE.BufferAttribute(ts, 1));
93
+ // Build index buffer
94
+ for (let i = 0; i < MAX_TRAIL - 1; i++) {
95
+ const a = i * 2, b = i * 2 + 1, c = (i + 1) * 2, d = (i + 1) * 2 + 1;
96
+ const base = i * 6;
97
+ indices[base + 0] = a;
98
+ indices[base + 1] = b;
99
+ indices[base + 2] = c;
100
+ indices[base + 3] = b;
101
+ indices[base + 4] = d;
102
+ indices[base + 5] = c;
103
+ }
104
+ g.setIndex(new THREE.BufferAttribute(indices, 1));
105
+ // UV per vert
106
+ for (let i = 0; i < MAX_TRAIL; i++) {
107
+ uvs[i * 2 * 2 + 0] = i / (MAX_TRAIL - 1);
108
+ uvs[i * 2 * 2 + 1] = 0;
109
+ uvs[i * 2 * 2 + 2] = i / (MAX_TRAIL - 1);
110
+ uvs[i * 2 * 2 + 3] = 1;
111
+ }
112
+ return g;
113
+ }, [positions, uvs, ts, indices]);
114
+ const material = useMemo(() => new THREE.ShaderMaterial({
115
+ uniforms: {
116
+ uColor: { value: new THREE.Color(config.color) },
117
+ uOpacity: { value: config.opacity },
118
+ uTime: { value: 0 },
119
+ uFlowSpeed: { value: config.flowSpeed },
120
+ },
121
+ vertexShader,
122
+ fragmentShader,
123
+ transparent: true,
124
+ depthWrite: false,
125
+ blending: THREE.AdditiveBlending,
126
+ side: THREE.DoubleSide,
127
+ }), []);
128
+ useEffect(() => {
129
+ material.uniforms.uColor.value.set(config.color);
130
+ material.uniforms.uOpacity.value = config.opacity;
131
+ material.uniforms.uFlowSpeed.value = config.flowSpeed;
132
+ }, [config.color, config.opacity, config.flowSpeed, material]);
133
+ useEffect(() => () => { geometry.dispose(); material.dispose(); }, [geometry, material]);
134
+ const firstFrame = useRef(true);
135
+ const tmpTangent = useMemo(() => new THREE.Vector3(), []);
136
+ const tmpToCam = useMemo(() => new THREE.Vector3(), []);
137
+ const tmpSide = useMemo(() => new THREE.Vector3(), []);
138
+ useFrame((state) => {
139
+ material.uniforms.uTime.value = state.clock.getElapsedTime();
140
+ const wp = worldPosRef.current;
141
+ if (firstFrame.current) {
142
+ for (let i = 0; i < N; i++) {
143
+ history[i * 3 + 0] = wp.x;
144
+ history[i * 3 + 1] = wp.y;
145
+ history[i * 3 + 2] = wp.z;
146
+ }
147
+ firstFrame.current = false;
148
+ }
149
+ // Shift history newest-first and write current
150
+ history.copyWithin(3, 0, (N - 1) * 3);
151
+ history[0] = wp.x;
152
+ history[1] = wp.y;
153
+ history[2] = wp.z;
154
+ // Width tapers and hover-driven boost
155
+ const idleFactor = Math.max(0, 1 - idleMsRef.current / Math.max(config.idleFadeMs, 1));
156
+ const hoverBoost = hoverRef.current ? 1.4 : 1.0;
157
+ const baseWidth = config.ribbonWidth * config.scale * (0.4 + idleFactor * 0.6) * hoverBoost;
158
+ // Camera position for facing the ribbon
159
+ const cam = camera.position;
160
+ for (let i = 0; i < N; i++) {
161
+ const idx = i * 3;
162
+ const px = history[idx + 0], py = history[idx + 1], pz = history[idx + 2];
163
+ // Tangent: difference between neighbors
164
+ const nextIdx = Math.max(0, i - 1) * 3;
165
+ const prevIdx = Math.min(N - 1, i + 1) * 3;
166
+ tmpTangent.set(history[nextIdx + 0] - history[prevIdx + 0], history[nextIdx + 1] - history[prevIdx + 1], history[nextIdx + 2] - history[prevIdx + 2]);
167
+ if (tmpTangent.lengthSq() < 1e-8)
168
+ tmpTangent.set(1, 0, 0);
169
+ tmpTangent.normalize();
170
+ // Side = tangent × (point-to-cam)
171
+ tmpToCam.set(cam.x - px, cam.y - py, cam.z - pz).normalize();
172
+ tmpSide.crossVectors(tmpTangent, tmpToCam);
173
+ if (tmpSide.lengthSq() < 1e-8)
174
+ tmpSide.set(0, 1, 0);
175
+ tmpSide.normalize();
176
+ // Width tapers: 1 at head, 0 at tail
177
+ const tNorm = 1 - i / Math.max(N - 1, 1);
178
+ const halfW = baseWidth * (0.2 + tNorm * 0.8);
179
+ const v0 = i * 2 * 3;
180
+ const v1 = v0 + 3;
181
+ positions[v0 + 0] = px + tmpSide.x * halfW;
182
+ positions[v0 + 1] = py + tmpSide.y * halfW;
183
+ positions[v0 + 2] = pz + tmpSide.z * halfW;
184
+ positions[v1 + 0] = px - tmpSide.x * halfW;
185
+ positions[v1 + 1] = py - tmpSide.y * halfW;
186
+ positions[v1 + 2] = pz - tmpSide.z * halfW;
187
+ ts[i * 2 + 0] = tNorm;
188
+ ts[i * 2 + 1] = tNorm;
189
+ }
190
+ // Zero out unused tail past N (in case trailLength shrinks)
191
+ for (let i = N; i < MAX_TRAIL; i++) {
192
+ const v0 = i * 2 * 3;
193
+ const v1 = v0 + 3;
194
+ positions[v0 + 0] = wp.x;
195
+ positions[v0 + 1] = -1e6;
196
+ positions[v0 + 2] = wp.z;
197
+ positions[v1 + 0] = wp.x;
198
+ positions[v1 + 1] = -1e6;
199
+ positions[v1 + 2] = wp.z;
200
+ }
201
+ geometry.attributes.position.needsUpdate = true;
202
+ geometry.attributes.aT.needsUpdate = true;
203
+ geometry.setDrawRange(0, (N - 1) * 6);
204
+ });
205
+ return _jsx("mesh", { geometry: geometry, material: material, frustumCulled: false });
206
+ };
207
+ export default AuroraCursor;
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ import type { BubblesCursorConfig } from '@ntalmagor/3drize-core';
3
+ import type { CursorTypeProps } from './CursorController';
4
+ declare const BubblesCursor: React.FC<CursorTypeProps<BubblesCursorConfig>>;
5
+ export default BubblesCursor;
6
+ //# sourceMappingURL=BubblesCursor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BubblesCursor.d.ts","sourceRoot":"","sources":["../../../src/components/cursor/BubblesCursor.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAqC,MAAM,OAAO,CAAC;AAG1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAElE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAgF1D,QAAA,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAAC,mBAAmB,CAAC,CAwHjE,CAAC;AAEF,eAAe,aAAa,CAAC"}
@@ -0,0 +1,184 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useRef } from 'react';
3
+ import { useFrame } from '@react-three/fiber';
4
+ import * as THREE from 'three';
5
+ const MAX_BUBBLES = 256;
6
+ const pointsVertexShader = /* glsl */ `
7
+ attribute float aAge;
8
+ attribute float aLife;
9
+ attribute float aBaseSize;
10
+ attribute float aPhase;
11
+ uniform float uTime;
12
+ uniform float uScale;
13
+ uniform float uPopWindow; // fraction at end of life used for pop
14
+ varying float vAgeN;
15
+ varying float vPopN;
16
+ varying float vPhase;
17
+ void main() {
18
+ vAgeN = clamp(aAge / max(aLife, 1.0), 0.0, 1.0);
19
+ float popStart = 1.0 - uPopWindow;
20
+ vPopN = (vAgeN > popStart) ? (vAgeN - popStart) / uPopWindow : 0.0;
21
+ vPhase = aPhase;
22
+ vec4 mv = modelViewMatrix * vec4(position, 1.0);
23
+ float wobble = 1.0 + sin(uTime * 2.0 + aPhase) * 0.06;
24
+ // Scale-burst during pop
25
+ float popBurst = 1.0 + vPopN * 1.5;
26
+ float size = aBaseSize * wobble * popBurst * uScale;
27
+ gl_PointSize = size * (300.0 / -mv.z);
28
+ gl_Position = projectionMatrix * mv;
29
+ }
30
+ `;
31
+ const pointsFragmentShader = /* glsl */ `
32
+ uniform vec3 uColor;
33
+ uniform float uOpacity;
34
+ uniform float uIridescence;
35
+ uniform float uTime;
36
+ varying float vAgeN;
37
+ varying float vPopN;
38
+ varying float vPhase;
39
+
40
+ // Thin-film interference approximation: hue shifts with fresnel angle and
41
+ // a per-bubble phase that animates slowly to feel alive.
42
+ vec3 thinFilm(float fresnel, float phase) {
43
+ float t = fresnel + phase * 0.15 + uTime * 0.05;
44
+ return vec3(
45
+ 0.5 + 0.5 * sin(6.2831 * (t + 0.0)),
46
+ 0.5 + 0.5 * sin(6.2831 * (t + 0.33)),
47
+ 0.5 + 0.5 * sin(6.2831 * (t + 0.66))
48
+ );
49
+ }
50
+
51
+ void main() {
52
+ vec2 c = gl_PointCoord * 2.0 - 1.0;
53
+ float r = length(c);
54
+ if (r > 1.0) discard;
55
+
56
+ // Fake sphere normal: approximate Z from disk distance
57
+ float z = sqrt(max(0.0, 1.0 - r * r));
58
+ vec3 n = vec3(c.x, c.y, z);
59
+ vec3 viewDir = vec3(0.0, 0.0, 1.0);
60
+ float fresnel = pow(1.0 - max(dot(n, viewDir), 0.0), 2.5);
61
+
62
+ // Iridescent rim + dimmer center (thin film, transparent middle)
63
+ vec3 film = thinFilm(fresnel, vPhase);
64
+ vec3 col = mix(uColor, film, uIridescence);
65
+
66
+ // Specular highlight at top-left
67
+ float spec = pow(max(dot(n, normalize(vec3(-0.4, 0.6, 0.7))), 0.0), 32.0);
68
+
69
+ // Pop flash brightens then fades hard
70
+ float popFlash = vPopN * (1.0 - vPopN) * 4.0;
71
+
72
+ float lifeFade = 1.0 - smoothstep(0.85, 1.0, vAgeN) * (vPopN > 0.0 ? 0.0 : 1.0);
73
+ float baseAlpha = (fresnel * 0.85 + spec * 0.5 + popFlash * 0.6);
74
+ float alpha = baseAlpha * uOpacity * lifeFade;
75
+ if (alpha < 0.004) discard;
76
+
77
+ gl_FragColor = vec4(col * (1.0 + spec + popFlash), alpha);
78
+ }
79
+ `;
80
+ const BubblesCursor = ({ config, worldPosRef, velocityRef, hoverRef, idleMsRef, }) => {
81
+ const positions = useMemo(() => new Float32Array(MAX_BUBBLES * 3), []);
82
+ const velocities = useMemo(() => new Float32Array(MAX_BUBBLES * 3), []);
83
+ const ages = useMemo(() => new Float32Array(MAX_BUBBLES), []);
84
+ const lives = useMemo(() => new Float32Array(MAX_BUBBLES), []);
85
+ const baseSizes = useMemo(() => new Float32Array(MAX_BUBBLES), []);
86
+ const phases = useMemo(() => new Float32Array(MAX_BUBBLES), []);
87
+ const alive = useMemo(() => new Uint8Array(MAX_BUBBLES), []);
88
+ const geometry = useMemo(() => {
89
+ const g = new THREE.BufferGeometry();
90
+ g.setAttribute('position', new THREE.BufferAttribute(positions, 3));
91
+ g.setAttribute('aAge', new THREE.BufferAttribute(ages, 1));
92
+ g.setAttribute('aLife', new THREE.BufferAttribute(lives, 1));
93
+ g.setAttribute('aBaseSize', new THREE.BufferAttribute(baseSizes, 1));
94
+ g.setAttribute('aPhase', new THREE.BufferAttribute(phases, 1));
95
+ for (let i = 0; i < MAX_BUBBLES; i++)
96
+ positions[i * 3 + 1] = -1e6;
97
+ return g;
98
+ }, [positions, ages, lives, baseSizes, phases]);
99
+ const material = useMemo(() => new THREE.ShaderMaterial({
100
+ uniforms: {
101
+ uTime: { value: 0 },
102
+ uScale: { value: config.scale / 10 },
103
+ uColor: { value: new THREE.Color(config.color) },
104
+ uOpacity: { value: config.opacity },
105
+ uIridescence: { value: config.iridescence },
106
+ uPopWindow: { value: 0.18 },
107
+ },
108
+ vertexShader: pointsVertexShader,
109
+ fragmentShader: pointsFragmentShader,
110
+ transparent: true,
111
+ depthWrite: false,
112
+ blending: THREE.NormalBlending,
113
+ }), []);
114
+ useEffect(() => {
115
+ material.uniforms.uColor.value.set(config.color);
116
+ material.uniforms.uScale.value = config.scale / 10;
117
+ material.uniforms.uOpacity.value = config.opacity;
118
+ material.uniforms.uIridescence.value = config.iridescence;
119
+ }, [config.color, config.scale, config.opacity, config.iridescence, material]);
120
+ useEffect(() => () => { geometry.dispose(); material.dispose(); }, [geometry, material]);
121
+ const spawnAccum = useRef(0);
122
+ const startTime = useRef(0);
123
+ useFrame((state, delta) => {
124
+ material.uniforms.uTime.value = state.clock.getElapsedTime();
125
+ if (startTime.current === 0)
126
+ startTime.current = state.clock.getElapsedTime();
127
+ const wp = worldPosRef.current;
128
+ const idleFactor = Math.max(0, 1 - idleMsRef.current / Math.max(config.idleFadeMs, 1));
129
+ const hover = hoverRef.current;
130
+ const hoverBoost = hover ? 1.6 : 1.0;
131
+ // Spawn rate (bubbles per second)
132
+ spawnAccum.current += config.spawnRate * delta * (0.5 + idleFactor * 0.5) * hoverBoost;
133
+ let toSpawn = Math.floor(spawnAccum.current);
134
+ spawnAccum.current -= toSpawn;
135
+ const dtMs = delta * 1000;
136
+ let scan = 0;
137
+ while (toSpawn > 0 && scan < MAX_BUBBLES) {
138
+ if (alive[scan] === 0) {
139
+ const base = scan * 3;
140
+ // Spawn near cursor with small lateral jitter
141
+ positions[base + 0] = wp.x + (Math.random() - 0.5) * 0.2 * config.scale;
142
+ positions[base + 1] = wp.y + (Math.random() - 0.5) * 0.1 * config.scale;
143
+ positions[base + 2] = wp.z + (Math.random() - 0.5) * 0.2 * config.scale;
144
+ // Drift + float up
145
+ velocities[base + 0] = (Math.random() - 0.5) * 0.4 + velocityRef.current.x * 0.05;
146
+ velocities[base + 1] = config.floatSpeed * (0.7 + Math.random() * 0.6);
147
+ velocities[base + 2] = (Math.random() - 0.5) * 0.4 + velocityRef.current.z * 0.05;
148
+ ages[scan] = 0;
149
+ lives[scan] = config.lifetimeMs * (0.7 + Math.random() * 0.6);
150
+ baseSizes[scan] = (16 + Math.random() * 28) * (config.scale / 10);
151
+ phases[scan] = Math.random() * 100;
152
+ alive[scan] = 1;
153
+ toSpawn--;
154
+ }
155
+ scan++;
156
+ }
157
+ for (let j = 0; j < MAX_BUBBLES; j++) {
158
+ if (alive[j] === 0)
159
+ continue;
160
+ const base = j * 3;
161
+ ages[j] += dtMs;
162
+ if (ages[j] >= lives[j]) {
163
+ alive[j] = 0;
164
+ positions[base + 1] = -1e6;
165
+ continue;
166
+ }
167
+ // Gentle horizontal drift damping; upward drift continues
168
+ velocities[base + 0] *= 0.99;
169
+ velocities[base + 2] *= 0.99;
170
+ // Wobble: tiny sinusoidal horizontal nudge based on phase
171
+ const wobble = Math.sin(state.clock.getElapsedTime() * 1.3 + phases[j]) * 0.05;
172
+ positions[base + 0] += velocities[base + 0] * delta + wobble * delta;
173
+ positions[base + 1] += velocities[base + 1] * delta;
174
+ positions[base + 2] += velocities[base + 2] * delta;
175
+ }
176
+ geometry.attributes.position.needsUpdate = true;
177
+ geometry.attributes.aAge.needsUpdate = true;
178
+ geometry.attributes.aLife.needsUpdate = true;
179
+ geometry.attributes.aBaseSize.needsUpdate = true;
180
+ geometry.attributes.aPhase.needsUpdate = true;
181
+ });
182
+ return _jsx("points", { geometry: geometry, material: material, frustumCulled: false });
183
+ };
184
+ export default BubblesCursor;
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ import type { CometCursorConfig } from '@ntalmagor/3drize-core';
3
+ import type { CursorTypeProps } from './CursorController';
4
+ declare const CometCursor: React.FC<CursorTypeProps<CometCursorConfig>>;
5
+ export default CometCursor;
6
+ //# sourceMappingURL=CometCursor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CometCursor.d.ts","sourceRoot":"","sources":["../../../src/components/cursor/CometCursor.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAqC,MAAM,OAAO,CAAC;AAG1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAgE1D,QAAA,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAAC,iBAAiB,CAAC,CA0J7D,CAAC;AAEF,eAAe,WAAW,CAAC"}