Files
greywall-landing-page/components/hackathons/shield-scene.tsx
2026-03-30 16:51:54 -04:00

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>
)
}