@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.
- package/dist/components/Bubbles.d.ts +8 -0
- package/dist/components/Bubbles.d.ts.map +1 -0
- package/dist/components/Bubbles.js +106 -0
- package/dist/components/Clouds.js +18 -10
- package/dist/components/CreatedObject.js +1 -1
- package/dist/components/Earth.d.ts +18 -0
- package/dist/components/Earth.d.ts.map +1 -0
- package/dist/components/Earth.js +43 -0
- package/dist/components/Glyphs.d.ts +8 -0
- package/dist/components/Glyphs.d.ts.map +1 -0
- package/dist/components/Glyphs.js +58 -0
- package/dist/components/GridHelper.js +1 -1
- package/dist/components/Moon.d.ts.map +1 -1
- package/dist/components/Moon.js +20 -6
- package/dist/components/ObjectNode.d.ts.map +1 -1
- package/dist/components/ObjectNode.js +23 -5
- package/dist/components/PathRenderer.d.ts +0 -2
- package/dist/components/PathRenderer.d.ts.map +1 -1
- package/dist/components/PathRenderer.js +4 -1
- package/dist/components/ProjectLoader.d.ts.map +1 -1
- package/dist/components/ProjectLoader.js +1 -7
- package/dist/components/SceneBuilder.d.ts.map +1 -1
- package/dist/components/SceneBuilder.js +2 -1
- package/dist/components/SkySystem.d.ts.map +1 -1
- package/dist/components/SkySystem.js +2 -2
- package/dist/components/Skybox.js +1 -1
- package/dist/components/SparklesBurst.d.ts +8 -0
- package/dist/components/SparklesBurst.d.ts.map +1 -0
- package/dist/components/SparklesBurst.js +62 -0
- package/dist/components/cursor/AuraCursor.d.ts +6 -0
- package/dist/components/cursor/AuraCursor.d.ts.map +1 -0
- package/dist/components/cursor/AuraCursor.js +30 -0
- package/dist/components/cursor/AuroraCursor.d.ts +6 -0
- package/dist/components/cursor/AuroraCursor.d.ts.map +1 -0
- package/dist/components/cursor/AuroraCursor.js +207 -0
- package/dist/components/cursor/BubblesCursor.d.ts +6 -0
- package/dist/components/cursor/BubblesCursor.d.ts.map +1 -0
- package/dist/components/cursor/BubblesCursor.js +184 -0
- package/dist/components/cursor/CometCursor.d.ts +6 -0
- package/dist/components/cursor/CometCursor.d.ts.map +1 -0
- package/dist/components/cursor/CometCursor.js +191 -0
- package/dist/components/cursor/CrosshairCursor.d.ts +6 -0
- package/dist/components/cursor/CrosshairCursor.d.ts.map +1 -0
- package/dist/components/cursor/CrosshairCursor.js +142 -0
- package/dist/components/cursor/CursorActions.d.ts +8 -0
- package/dist/components/cursor/CursorActions.d.ts.map +1 -0
- package/dist/components/cursor/CursorActions.js +124 -0
- package/dist/components/cursor/CursorController.d.ts +10 -0
- package/dist/components/cursor/CursorController.d.ts.map +1 -0
- package/dist/components/cursor/CursorController.js +5 -0
- package/dist/components/cursor/CursorEffects.d.ts +16 -0
- package/dist/components/cursor/CursorEffects.d.ts.map +1 -0
- package/dist/components/cursor/CursorEffects.js +110 -0
- package/dist/components/cursor/FerrofluidCursor.d.ts +6 -0
- package/dist/components/cursor/FerrofluidCursor.d.ts.map +1 -0
- package/dist/components/cursor/FerrofluidCursor.js +102 -0
- package/dist/components/cursor/GlyphRingCursor.d.ts +6 -0
- package/dist/components/cursor/GlyphRingCursor.d.ts.map +1 -0
- package/dist/components/cursor/GlyphRingCursor.js +123 -0
- package/dist/components/cursor/LightningCursor.d.ts +6 -0
- package/dist/components/cursor/LightningCursor.d.ts.map +1 -0
- package/dist/components/cursor/LightningCursor.js +28 -0
- package/dist/components/cursor/LiquidMetalCursor.d.ts +6 -0
- package/dist/components/cursor/LiquidMetalCursor.d.ts.map +1 -0
- package/dist/components/cursor/LiquidMetalCursor.js +61 -0
- package/dist/components/cursor/OrbCursor.d.ts +6 -0
- package/dist/components/cursor/OrbCursor.d.ts.map +1 -0
- package/dist/components/cursor/OrbCursor.js +32 -0
- package/dist/components/cursor/PlasmaWhipCursor.d.ts +6 -0
- package/dist/components/cursor/PlasmaWhipCursor.d.ts.map +1 -0
- package/dist/components/cursor/PlasmaWhipCursor.js +134 -0
- package/dist/components/cursor/SparklesCursor.d.ts +6 -0
- package/dist/components/cursor/SparklesCursor.d.ts.map +1 -0
- package/dist/components/cursor/SparklesCursor.js +151 -0
- package/dist/components/cursor/TeslaCursor.d.ts +6 -0
- package/dist/components/cursor/TeslaCursor.d.ts.map +1 -0
- package/dist/components/cursor/TeslaCursor.js +223 -0
- package/dist/components/cursor/clickActions/BoltFlashAction.d.ts +11 -0
- package/dist/components/cursor/clickActions/BoltFlashAction.d.ts.map +1 -0
- package/dist/components/cursor/clickActions/BoltFlashAction.js +62 -0
- package/dist/components/cursor/clickActions/GlyphInscriptionAction.d.ts +11 -0
- package/dist/components/cursor/clickActions/GlyphInscriptionAction.d.ts.map +1 -0
- package/dist/components/cursor/clickActions/GlyphInscriptionAction.js +114 -0
- package/dist/components/cursor/clickActions/ImplosionAction.d.ts +11 -0
- package/dist/components/cursor/clickActions/ImplosionAction.d.ts.map +1 -0
- package/dist/components/cursor/clickActions/ImplosionAction.js +93 -0
- package/dist/components/cursor/clickActions/PhaseShiftAction.d.ts +11 -0
- package/dist/components/cursor/clickActions/PhaseShiftAction.d.ts.map +1 -0
- package/dist/components/cursor/clickActions/PhaseShiftAction.js +71 -0
- package/dist/components/cursor/clickActions/PlasmaBombAction.d.ts +11 -0
- package/dist/components/cursor/clickActions/PlasmaBombAction.d.ts.map +1 -0
- package/dist/components/cursor/clickActions/PlasmaBombAction.js +98 -0
- package/dist/components/cursor/clickActions/RealityTearAction.d.ts +11 -0
- package/dist/components/cursor/clickActions/RealityTearAction.d.ts.map +1 -0
- package/dist/components/cursor/clickActions/RealityTearAction.js +92 -0
- package/dist/components/cursor/clickActions/ShockwaveAction.d.ts +11 -0
- package/dist/components/cursor/clickActions/ShockwaveAction.d.ts.map +1 -0
- package/dist/components/cursor/clickActions/ShockwaveAction.js +55 -0
- package/dist/components/cursor/clickActions/SparklesBurstAction.d.ts +11 -0
- package/dist/components/cursor/clickActions/SparklesBurstAction.d.ts.map +1 -0
- package/dist/components/cursor/clickActions/SparklesBurstAction.js +99 -0
- package/dist/components/cursor/clickActions/VoronoiFractureAction.d.ts +11 -0
- package/dist/components/cursor/clickActions/VoronoiFractureAction.d.ts.map +1 -0
- package/dist/components/cursor/clickActions/VoronoiFractureAction.js +149 -0
- package/dist/hooks/useContinuousEffects.js +2 -7
- package/dist/hooks/useMeshController.d.ts.map +1 -1
- package/dist/hooks/useMeshController.js +78 -28
- package/dist/hooks/useSequenceAnimation.d.ts.map +1 -1
- package/dist/hooks/useSequenceAnimation.js +38 -17
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/utils/CameraSingleton.d.ts +8 -1
- package/dist/utils/CameraSingleton.d.ts.map +1 -1
- package/dist/utils/CameraSingleton.js +22 -1
- package/dist/utils/cursorPicking.d.ts +8 -0
- package/dist/utils/cursorPicking.d.ts.map +1 -0
- package/dist/utils/cursorPicking.js +35 -0
- 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"}
|