import React, { useRef, useMemo, useEffect } from 'react'; import { Canvas, useFrame } from '@react-three/fiber'; import * as THREE from 'three'; // Add global declaration to fix TypeScript errors with React Three Fiber elements declare module 'react' { namespace JSX { interface IntrinsicElements { points: any; bufferGeometry: any; bufferAttribute: any; pointsMaterial: any; ambientLight: any; } } } declare global { namespace JSX { interface IntrinsicElements { points: any; bufferGeometry: any; bufferAttribute: any; pointsMaterial: any; ambientLight: any; } } } // --- WARP EFFECT --- const WarpParticles = () => { const count = 2000; const mesh = useRef(null); // Particles setup const particles = useMemo(() => { const temp = []; for (let i = 0; i < count; i++) { const x = (Math.random() - 0.5) * 30; const y = (Math.random() - 0.5) * 60; // Taller spread const z = (Math.random() - 0.5) * 20; temp.push(x, y, z); } return new Float32Array(temp); }, [count]); useFrame((state, delta) => { if (!mesh.current) return; const time = state.clock.getElapsedTime(); // Logic: // 1. Initial burst (0-1s): Speed = 3.0 // 2. Slow down (1s+): Speed = 0.5 (2x slower than a standard '1.0' baseline) let targetSpeed = 0.5; // Slow cruising speed if (time < 1.0) { targetSpeed = 4.0; // Fast entry } else if (time < 1.8) { // Smooth deceleration phase const t = (time - 1.0) / 0.8; targetSpeed = THREE.MathUtils.lerp(4.0, 0.5, t); } const positions = mesh.current.geometry.attributes.position.array as Float32Array; const moveY = targetSpeed * delta * 10; // Scale speed to movement for (let i = 0; i < count; i++) { // Move particles up positions[i * 3 + 1] += moveY; // Seamless wrap around if (positions[i * 3 + 1] > 30) { positions[i * 3 + 1] -= 60; // Subtract height to wrap seamlessly // Randomize X/Z on respawn to create new star patterns positions[i * 3] = (Math.random() - 0.5) * 30; positions[i * 3 + 2] = (Math.random() - 0.5) * 20; } } mesh.current.geometry.attributes.position.needsUpdate = true; // Stretch effect based on speed // Higher speed = more vertical stretch mesh.current.scale.y = 1 + targetSpeed * 0.5; }); return ( ); }; export const WarpOverlay = () => { return (
); }; // --- SAND EFFECT --- const SandParticles = () => { const count = 4000; const mesh = useRef(null); const particles = useMemo(() => { const temp = []; for (let i = 0; i < count; i++) { const x = (Math.random() - 0.5) * 25; const y = (Math.random() - 0.5) * 25; const z = (Math.random() - 0.5) * 10; temp.push(x, y, z); } return new Float32Array(temp); }, [count]); useFrame((state) => { if (!mesh.current) return; const positions = mesh.current.geometry.attributes.position.array as Float32Array; const time = state.clock.getElapsedTime(); for (let i = 0; i < count; i++) { const ix = i * 3; const iy = i * 3 + 1; const iz = i * 3 + 2; // Chaotic wind movement positions[ix] += Math.sin(time * 0.5 + positions[iy] * 0.5) * 0.02; positions[iy] += Math.cos(time * 0.3 + positions[ix] * 0.5) * 0.01; // Wrap around if (Math.abs(positions[ix]) > 12) positions[ix] *= -0.9; if (Math.abs(positions[iy]) > 12) positions[iy] *= -0.9; } mesh.current.geometry.attributes.position.needsUpdate = true; mesh.current.rotation.y = time * 0.05; }); return ( ); }; export const SandOverlay = () => { return (
); };