134 lines
4.0 KiB
TypeScript
134 lines
4.0 KiB
TypeScript
"use client";
|
|
import { Canvas, useFrame } from "@react-three/fiber";
|
|
import { Float, OrbitControls, Points, PointMaterial } from "@react-three/drei";
|
|
import { useMemo, useRef } from "react";
|
|
import * as THREE from "three";
|
|
|
|
function StarsBackground() {
|
|
const ref = useRef<THREE.Points | null>(null);
|
|
const positions = useMemo(() => {
|
|
const p = new Float32Array(6000);
|
|
for (let i = 0; i < 6000; i += 3) {
|
|
p[i] = (Math.random() - 0.5) * 60;
|
|
p[i + 1] = (Math.random() - 0.5) * 60;
|
|
p[i + 2] = (Math.random() - 0.5) * 60;
|
|
}
|
|
return p;
|
|
}, []);
|
|
useFrame((_, delta) => {
|
|
if (ref.current) ref.current.rotation.y += delta * 0.015;
|
|
});
|
|
return (
|
|
<group rotation={[0, 0, Math.PI / 8]}>
|
|
<Points ref={ref} positions={positions} stride={3} frustumCulled>
|
|
<PointMaterial
|
|
transparent
|
|
color="#60a5fa"
|
|
size={0.06}
|
|
sizeAttenuation
|
|
depthWrite={false}
|
|
/>
|
|
</Points>
|
|
</group>
|
|
);
|
|
}
|
|
|
|
function DataCore() {
|
|
const coreRef = useRef<THREE.Group | null>(null);
|
|
const orbitRef = useRef<THREE.Group | null>(null);
|
|
const nodesRef = useRef<THREE.Group | null>(null);
|
|
|
|
// Pre-generate node positions on a tilted orbit
|
|
const nodes = useMemo(() => {
|
|
const arr: { position: [number, number, number] }[] = [];
|
|
const radius = 2.4;
|
|
for (let i = 0; i < 14; i++) {
|
|
const a = (i / 14) * Math.PI * 2;
|
|
const tilt = 0.45; // slight vertical variation
|
|
arr.push({ position: [Math.cos(a) * radius, Math.sin(a) * tilt, Math.sin(a) * radius] });
|
|
}
|
|
return arr;
|
|
}, []);
|
|
|
|
useFrame((_, delta) => {
|
|
if (coreRef.current) coreRef.current.rotation.y += delta * 0.15;
|
|
if (orbitRef.current) orbitRef.current.rotation.y -= delta * 0.1;
|
|
if (nodesRef.current) nodesRef.current.rotation.y += delta * 0.22;
|
|
});
|
|
|
|
return (
|
|
<group position={[0, 0, -4]}>
|
|
{/* Central sphere */}
|
|
<mesh ref={coreRef}>
|
|
<icosahedronGeometry args={[1.05, 2]} />
|
|
<meshStandardMaterial
|
|
color="#3b82f6"
|
|
emissive="#1e40af"
|
|
emissiveIntensity={0.85}
|
|
metalness={0.35}
|
|
roughness={0.3}
|
|
/>
|
|
</mesh>
|
|
|
|
{/* Subtle inner glow shell */}
|
|
<mesh>
|
|
<sphereGeometry args={[1.35, 32, 32]} />
|
|
<meshBasicMaterial color="#60a5fa" transparent opacity={0.08} />
|
|
</mesh>
|
|
|
|
{/* Rotating analytic rings */}
|
|
<group ref={orbitRef}>
|
|
{[
|
|
{ r: 1.75, rot: [Math.PI / 2, 0, 0] },
|
|
{ r: 2.0, rot: [0.35, 0.6, 0] },
|
|
{ r: 2.25, rot: [0.9, 0.2, 0.4] },
|
|
].map((cfg, i) => (
|
|
<mesh key={i} rotation={cfg.rot as [number, number, number]}>
|
|
<torusGeometry args={[cfg.r, 0.015, 8, 160]} />
|
|
<meshStandardMaterial
|
|
color="#64748b"
|
|
emissive="#1e293b"
|
|
emissiveIntensity={0.25}
|
|
metalness={0.2}
|
|
roughness={0.4}
|
|
/>
|
|
</mesh>
|
|
))}
|
|
</group>
|
|
|
|
{/* Orbiting nodes */}
|
|
<group ref={nodesRef}>
|
|
{nodes.map((n, idx) => (
|
|
<mesh key={idx} position={n.position}>
|
|
<sphereGeometry args={[0.09, 16, 16]} />
|
|
<meshStandardMaterial
|
|
color="#93c5fd"
|
|
emissive="#1d4ed8"
|
|
emissiveIntensity={0.6}
|
|
metalness={0.3}
|
|
roughness={0.35}
|
|
/>
|
|
</mesh>
|
|
))}
|
|
</group>
|
|
</group>
|
|
);
|
|
}
|
|
|
|
export default function Scene() {
|
|
return (
|
|
<div className="fixed inset-0 -z-10">
|
|
<Canvas camera={{ position: [0, 0, 16], fov: 65 }}>
|
|
<StarsBackground />
|
|
<ambientLight intensity={0.5} />
|
|
<pointLight position={[8, 6, 8]} intensity={1.4} />
|
|
<pointLight position={[-6, -4, -6]} intensity={0.6} color="#1e3a8a" />
|
|
<OrbitControls enableZoom={false} enablePan={false} autoRotate autoRotateSpeed={0.4} />
|
|
<Float speed={1.2} floatIntensity={1.4} rotationIntensity={0.6}>
|
|
<DataCore />
|
|
</Float>
|
|
</Canvas>
|
|
</div>
|
|
);
|
|
}
|