220 lines
6.4 KiB
TypeScript
220 lines
6.4 KiB
TypeScript
'use client'
|
|
|
|
import { useRef, useMemo } from 'react'
|
|
import { Canvas, useFrame } from '@react-three/fiber'
|
|
import { Float } from '@react-three/drei'
|
|
import * as THREE from 'three'
|
|
|
|
/* ─── Orbiting particles ─── */
|
|
|
|
function Particles({ count = 80 }: { count?: number }) {
|
|
const mesh = useRef<THREE.InstancedMesh>(null)
|
|
const dummy = useMemo(() => new THREE.Object3D(), [])
|
|
|
|
const particles = useMemo(() => {
|
|
return Array.from({ length: count }, (_, i) => ({
|
|
radius: 1.8 + Math.random() * 1.4,
|
|
speed: 0.15 + Math.random() * 0.3,
|
|
offset: (i / count) * Math.PI * 2,
|
|
y: (Math.random() - 0.5) * 2.5,
|
|
size: 0.015 + Math.random() * 0.025,
|
|
}))
|
|
}, [count])
|
|
|
|
useFrame(({ clock }) => {
|
|
if (!mesh.current) return
|
|
const t = clock.getElapsedTime()
|
|
particles.forEach((p, i) => {
|
|
const angle = p.offset + t * p.speed
|
|
dummy.position.set(
|
|
Math.cos(angle) * p.radius,
|
|
p.y + Math.sin(t * 0.5 + p.offset) * 0.3,
|
|
Math.sin(angle) * p.radius
|
|
)
|
|
dummy.scale.setScalar(p.size * (0.8 + Math.sin(t * 2 + p.offset) * 0.2))
|
|
dummy.updateMatrix()
|
|
mesh.current!.setMatrixAt(i, dummy.matrix)
|
|
})
|
|
mesh.current.instanceMatrix.needsUpdate = true
|
|
})
|
|
|
|
return (
|
|
<instancedMesh ref={mesh} args={[undefined, undefined, count]}>
|
|
<sphereGeometry args={[1, 8, 8]} />
|
|
<meshBasicMaterial color="#D95E2A" transparent opacity={0.6} />
|
|
</instancedMesh>
|
|
)
|
|
}
|
|
|
|
/* ─── Orbital rings ─── */
|
|
|
|
function OrbitalRing({ radius, speed, tilt }: { radius: number; speed: number; tilt: number }) {
|
|
const ref = useRef<THREE.Mesh>(null)
|
|
|
|
useFrame(({ clock }) => {
|
|
if (!ref.current) return
|
|
ref.current.rotation.z = tilt
|
|
ref.current.rotation.y = clock.getElapsedTime() * speed
|
|
})
|
|
|
|
return (
|
|
<mesh ref={ref}>
|
|
<torusGeometry args={[radius, 0.005, 16, 100]} />
|
|
<meshBasicMaterial color="#D95E2A" transparent opacity={0.15} />
|
|
</mesh>
|
|
)
|
|
}
|
|
|
|
/* ─── Shield geometry ─── */
|
|
|
|
function ShieldMesh() {
|
|
const ref = useRef<THREE.Group>(null)
|
|
|
|
const shieldShape = useMemo(() => {
|
|
const shape = new THREE.Shape()
|
|
// Shield outline
|
|
shape.moveTo(0, 1.3)
|
|
shape.bezierCurveTo(0.6, 1.2, 1.0, 0.9, 1.0, 0.4)
|
|
shape.bezierCurveTo(1.0, -0.2, 0.7, -0.8, 0, -1.3)
|
|
shape.bezierCurveTo(-0.7, -0.8, -1.0, -0.2, -1.0, 0.4)
|
|
shape.bezierCurveTo(-1.0, 0.9, -0.6, 1.2, 0, 1.3)
|
|
return shape
|
|
}, [])
|
|
|
|
const extrudeSettings = useMemo(() => ({
|
|
depth: 0.15,
|
|
bevelEnabled: true,
|
|
bevelThickness: 0.03,
|
|
bevelSize: 0.03,
|
|
bevelSegments: 3,
|
|
}), [])
|
|
|
|
useFrame(({ clock }) => {
|
|
if (!ref.current) return
|
|
ref.current.rotation.y = Math.sin(clock.getElapsedTime() * 0.3) * 0.15
|
|
})
|
|
|
|
return (
|
|
<Float speed={1.5} rotationIntensity={0.2} floatIntensity={0.3}>
|
|
<group ref={ref}>
|
|
{/* Shield body */}
|
|
<mesh position={[0, 0, -0.075]}>
|
|
<extrudeGeometry args={[shieldShape, extrudeSettings]} />
|
|
<meshStandardMaterial
|
|
color="#1a1a18"
|
|
metalness={0.7}
|
|
roughness={0.3}
|
|
emissive="#D95E2A"
|
|
emissiveIntensity={0.05}
|
|
/>
|
|
</mesh>
|
|
|
|
{/* Inner shield face */}
|
|
<mesh position={[0, 0, 0.08]}>
|
|
<shapeGeometry args={[shieldShape]} />
|
|
<meshStandardMaterial
|
|
color="#D95E2A"
|
|
metalness={0.5}
|
|
roughness={0.4}
|
|
transparent
|
|
opacity={0.15}
|
|
/>
|
|
</mesh>
|
|
|
|
{/* Shield edge glow */}
|
|
<mesh position={[0, 0, -0.075]}>
|
|
<extrudeGeometry args={[shieldShape, { ...extrudeSettings, depth: 0.16 }]} />
|
|
<meshBasicMaterial color="#D95E2A" transparent opacity={0.08} wireframe />
|
|
</mesh>
|
|
|
|
{/* Center node */}
|
|
<mesh position={[0, 0.1, 0.1]}>
|
|
<sphereGeometry args={[0.08, 16, 16]} />
|
|
<meshStandardMaterial color="#D95E2A" emissive="#D95E2A" emissiveIntensity={0.8} />
|
|
</mesh>
|
|
|
|
{/* Network nodes */}
|
|
{[
|
|
[0, 0.55, 0.1],
|
|
[-0.35, -0.15, 0.1],
|
|
[0.35, -0.15, 0.1],
|
|
[0, -0.55, 0.1],
|
|
].map((pos, i) => (
|
|
<mesh key={i} position={pos as [number, number, number]}>
|
|
<sphereGeometry args={[0.045, 12, 12]} />
|
|
<meshStandardMaterial color="#D95E2A" emissive="#D95E2A" emissiveIntensity={0.5} />
|
|
</mesh>
|
|
))}
|
|
</group>
|
|
</Float>
|
|
)
|
|
}
|
|
|
|
/* ─── Data stream lines flowing around ─── */
|
|
|
|
function DataStreams({ count = 12 }: { count?: number }) {
|
|
const ref = useRef<THREE.Group>(null)
|
|
|
|
const streams = useMemo(() => {
|
|
return Array.from({ length: count }, (_, i) => {
|
|
const angle = (i / count) * Math.PI * 2
|
|
const radius = 2.0 + Math.random() * 0.5
|
|
const points = Array.from({ length: 20 }, (_, j) => {
|
|
const t = j / 19
|
|
const a = angle + t * Math.PI * 0.5
|
|
return new THREE.Vector3(
|
|
Math.cos(a) * radius * (1 - t * 0.3),
|
|
(t - 0.5) * 3,
|
|
Math.sin(a) * radius * (1 - t * 0.3)
|
|
)
|
|
})
|
|
return { points, speed: 0.5 + Math.random() * 0.5 }
|
|
})
|
|
}, [count])
|
|
|
|
useFrame(({ clock }) => {
|
|
if (!ref.current) return
|
|
ref.current.rotation.y = clock.getElapsedTime() * 0.05
|
|
})
|
|
|
|
return (
|
|
<group ref={ref}>
|
|
{streams.map((stream, i) => {
|
|
const curve = new THREE.CatmullRomCurve3(stream.points)
|
|
return (
|
|
<mesh key={i}>
|
|
<tubeGeometry args={[curve, 20, 0.003, 4, false]} />
|
|
<meshBasicMaterial color="#D95E2A" transparent opacity={0.1} />
|
|
</mesh>
|
|
)
|
|
})}
|
|
</group>
|
|
)
|
|
}
|
|
|
|
/* ─── Main scene ─── */
|
|
|
|
export function ShieldScene() {
|
|
return (
|
|
<div className="w-full h-full min-h-[300px]">
|
|
<Canvas
|
|
camera={{ position: [0, 0, 5.5], fov: 42 }}
|
|
dpr={[1, 2]}
|
|
gl={{ antialias: true, alpha: true }}
|
|
style={{ background: 'transparent' }}
|
|
>
|
|
<ambientLight intensity={0.4} />
|
|
<pointLight position={[5, 5, 5]} intensity={0.8} color="#F9F9F7" />
|
|
<pointLight position={[-3, -2, 4]} intensity={0.3} color="#D95E2A" />
|
|
|
|
<ShieldMesh />
|
|
<Particles />
|
|
<DataStreams />
|
|
<OrbitalRing radius={2.2} speed={0.1} tilt={0.3} />
|
|
<OrbitalRing radius={2.8} speed={-0.07} tilt={-0.5} />
|
|
<OrbitalRing radius={1.6} speed={0.15} tilt={0.8} />
|
|
</Canvas>
|
|
</div>
|
|
)
|
|
}
|