Initial Commit
Commit pertama, memindahkan repository dari github ke gitea.
This commit is contained in:
commit
13a47fbebf
41 files changed
+12327
No files matched your search
@@ -0,0 +1,252 @@
|
||||
"use client";
|
||||
|
||||
import React, { useMemo, useRef } from "react";
|
||||
import { Canvas, useFrame } from "@react-three/fiber";
|
||||
import * as THREE from "three";
|
||||
|
||||
// 1. Refined, clean Starfield (neat, elegant points of light)
|
||||
function StarField({ count = 650 }: { count?: number }) {
|
||||
const pointsRef = useRef<THREE.Points>(null!);
|
||||
|
||||
const [positions, colors] = useMemo(() => {
|
||||
const pos = new Float32Array(count * 3);
|
||||
const col = new Float32Array(count * 3);
|
||||
|
||||
const colorWhite = new THREE.Color("#ffffff");
|
||||
const colorIcy = new THREE.Color("#93c5fd");
|
||||
const colorSilver = new THREE.Color("#e2e8f0");
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const radius = 30 + Math.random() * 80;
|
||||
const theta = Math.random() * 2 * Math.PI;
|
||||
const phi = Math.acos(2 * Math.random() - 1);
|
||||
|
||||
pos[i * 3] = radius * Math.sin(phi) * Math.cos(theta);
|
||||
pos[i * 3 + 1] = radius * Math.sin(phi) * Math.sin(theta);
|
||||
pos[i * 3 + 2] = radius * Math.cos(phi);
|
||||
|
||||
const r = Math.random();
|
||||
const chosen = r > 0.7 ? colorIcy : r > 0.3 ? colorSilver : colorWhite;
|
||||
|
||||
col[i * 3] = chosen.r;
|
||||
col[i * 3 + 1] = chosen.g;
|
||||
col[i * 3 + 2] = chosen.b;
|
||||
}
|
||||
|
||||
return [pos, col];
|
||||
}, [count]);
|
||||
|
||||
useFrame((state, delta) => {
|
||||
if (!pointsRef.current) return;
|
||||
pointsRef.current.rotation.y += delta * 0.005;
|
||||
pointsRef.current.rotation.x += delta * 0.002;
|
||||
|
||||
const targetX = (state.pointer.x * Math.PI) / 60;
|
||||
const targetY = (state.pointer.y * Math.PI) / 60;
|
||||
pointsRef.current.rotation.y += (targetX - pointsRef.current.rotation.y) * 0.02;
|
||||
pointsRef.current.rotation.x += (-targetY - pointsRef.current.rotation.x) * 0.02;
|
||||
});
|
||||
|
||||
return (
|
||||
<points ref={pointsRef}>
|
||||
<bufferGeometry>
|
||||
<bufferAttribute attach="attributes-position" args={[positions, 3]} />
|
||||
<bufferAttribute attach="attributes-color" args={[colors, 3]} />
|
||||
</bufferGeometry>
|
||||
<pointsMaterial
|
||||
size={0.7}
|
||||
vertexColors
|
||||
transparent
|
||||
opacity={0.8}
|
||||
sizeAttenuation
|
||||
depthWrite={false}
|
||||
blending={THREE.AdditiveBlending}
|
||||
/>
|
||||
</points>
|
||||
);
|
||||
}
|
||||
|
||||
// 2. Bright, Glowing Background Planet (Astraea - Upper Right)
|
||||
function BackgroundPlanetAstraea() {
|
||||
const groupRef = useRef<THREE.Group>(null!);
|
||||
const planetRef = useRef<THREE.Mesh>(null!);
|
||||
const atmosphereRef = useRef<THREE.Mesh>(null!);
|
||||
|
||||
useFrame((state, delta) => {
|
||||
if (planetRef.current) {
|
||||
planetRef.current.rotation.y += delta * 0.05;
|
||||
}
|
||||
if (atmosphereRef.current) {
|
||||
atmosphereRef.current.rotation.y -= delta * 0.03;
|
||||
}
|
||||
if (groupRef.current) {
|
||||
// Subtle floating parallax
|
||||
const targetX = 16 + (state.pointer.x * 2.5);
|
||||
const targetY = 9 + (state.pointer.y * 2.0);
|
||||
groupRef.current.position.x += (targetX - groupRef.current.position.x) * 0.02;
|
||||
groupRef.current.position.y += (targetY - groupRef.current.position.y) * 0.02;
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<group ref={groupRef} position={[16, 9, -16]}>
|
||||
{/* Bright Planet Core Sphere */}
|
||||
<mesh ref={planetRef}>
|
||||
<sphereGeometry args={[4.2, 48, 48]} />
|
||||
<meshStandardMaterial
|
||||
color="#38bdf8"
|
||||
emissive="#0284c7"
|
||||
emissiveIntensity={0.85}
|
||||
roughness={0.25}
|
||||
metalness={0.7}
|
||||
/>
|
||||
</mesh>
|
||||
|
||||
{/* Radiant Atmospheric Halo Rim */}
|
||||
<mesh ref={atmosphereRef}>
|
||||
<sphereGeometry args={[4.6, 32, 32]} />
|
||||
<meshBasicMaterial
|
||||
color="#7dd3fc"
|
||||
transparent
|
||||
opacity={0.35}
|
||||
side={THREE.BackSide}
|
||||
blending={THREE.AdditiveBlending}
|
||||
/>
|
||||
</mesh>
|
||||
|
||||
{/* Outer Luminous Glow Aura Ring */}
|
||||
<mesh rotation={[Math.PI / 3, 0.4, 0]}>
|
||||
<ringGeometry args={[4.8, 6.8, 48]} />
|
||||
<meshBasicMaterial
|
||||
color="#38bdf8"
|
||||
transparent
|
||||
opacity={0.22}
|
||||
side={THREE.DoubleSide}
|
||||
/>
|
||||
</mesh>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
// 3. Bright, Glowing Background Planet (Kore - Lower Left with Ring)
|
||||
function BackgroundPlanetKore() {
|
||||
const groupRef = useRef<THREE.Group>(null!);
|
||||
const planetRef = useRef<THREE.Mesh>(null!);
|
||||
const ringRef = useRef<THREE.Mesh>(null!);
|
||||
|
||||
useFrame((state, delta) => {
|
||||
if (planetRef.current) {
|
||||
planetRef.current.rotation.y += delta * 0.04;
|
||||
}
|
||||
if (ringRef.current) {
|
||||
ringRef.current.rotation.z += delta * 0.02;
|
||||
}
|
||||
if (groupRef.current) {
|
||||
// Gentle floating parallax
|
||||
const targetX = -18 - (state.pointer.x * 3.0);
|
||||
const targetY = -12 - (state.pointer.y * 2.5);
|
||||
groupRef.current.position.x += (targetX - groupRef.current.position.x) * 0.02;
|
||||
groupRef.current.position.y += (targetY - groupRef.current.position.y) * 0.02;
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<group ref={groupRef} position={[-18, -12, -22]}>
|
||||
{/* Bright Violet Core Planet */}
|
||||
<mesh ref={planetRef}>
|
||||
<sphereGeometry args={[5.5, 48, 48]} />
|
||||
<meshStandardMaterial
|
||||
color="#a855f7"
|
||||
emissive="#6366f1"
|
||||
emissiveIntensity={0.75}
|
||||
roughness={0.3}
|
||||
metalness={0.8}
|
||||
/>
|
||||
</mesh>
|
||||
|
||||
{/* Atmospheric Halo */}
|
||||
<mesh>
|
||||
<sphereGeometry args={[5.9, 32, 32]} />
|
||||
<meshBasicMaterial
|
||||
color="#c084fc"
|
||||
transparent
|
||||
opacity={0.3}
|
||||
side={THREE.BackSide}
|
||||
blending={THREE.AdditiveBlending}
|
||||
/>
|
||||
</mesh>
|
||||
|
||||
{/* Luminous Majestic Planetary Ring */}
|
||||
<mesh ref={ringRef} rotation={[Math.PI / 2.3, 0.35, 0]}>
|
||||
<ringGeometry args={[6.8, 10.2, 64]} />
|
||||
<meshStandardMaterial
|
||||
color="#c084fc"
|
||||
emissive="#a855f7"
|
||||
emissiveIntensity={0.6}
|
||||
transparent
|
||||
opacity={0.45}
|
||||
side={THREE.DoubleSide}
|
||||
/>
|
||||
</mesh>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
// 4. Distant Luminous Stellar Moon Orb
|
||||
function DistantLuminousOrb() {
|
||||
const orbRef = useRef<THREE.Mesh>(null!);
|
||||
|
||||
useFrame((state, delta) => {
|
||||
if (!orbRef.current) return;
|
||||
const t = state.clock.getElapsedTime();
|
||||
orbRef.current.position.y = 14 + Math.sin(t * 0.8) * 0.6;
|
||||
orbRef.current.position.x = -8 + Math.cos(t * 0.5) * 0.8;
|
||||
});
|
||||
|
||||
return (
|
||||
<mesh ref={orbRef} position={[-8, 14, -18]}>
|
||||
<sphereGeometry args={[1.2, 24, 24]} />
|
||||
<meshStandardMaterial
|
||||
color="#34d399"
|
||||
emissive="#10b981"
|
||||
emissiveIntensity={1.2}
|
||||
roughness={0.1}
|
||||
/>
|
||||
</mesh>
|
||||
);
|
||||
}
|
||||
|
||||
export default function CosmicGalaxy() {
|
||||
return (
|
||||
<div className="fixed inset-0 pointer-events-none z-0 overflow-hidden">
|
||||
<Canvas
|
||||
camera={{ position: [0, 0, 32], fov: 50 }}
|
||||
dpr={[1, 1.5]}
|
||||
gl={{ antialias: true, alpha: true, powerPreference: "high-performance" }}
|
||||
className="w-full h-full"
|
||||
>
|
||||
<color attach="background" args={["#0a0a12"]} />
|
||||
|
||||
{/* Cinematic Starlight Illumination */}
|
||||
<ambientLight intensity={0.5} />
|
||||
<directionalLight position={[20, 20, 10]} intensity={1.8} color="#e0f2fe" />
|
||||
<directionalLight position={[-20, -15, -5]} intensity={1.0} color="#c084fc" />
|
||||
|
||||
{/* Clean, Neat Starfield */}
|
||||
<StarField count={650} />
|
||||
|
||||
{/* Bright Circular Planet-Like Objects in the Background */}
|
||||
<BackgroundPlanetAstraea />
|
||||
<BackgroundPlanetKore />
|
||||
<DistantLuminousOrb />
|
||||
</Canvas>
|
||||
|
||||
{/* Atmospheric depth vignettes (subtle gradient, no harsh boxes) */}
|
||||
<div className="absolute inset-0 bg-radial from-transparent via-[#0a0a12]/30 to-[#0a0a12] pointer-events-none" />
|
||||
|
||||
{/* Soft circular glowing light fountains behind the planets */}
|
||||
<div className="absolute -top-10 right-10 w-96 h-96 bg-cyan-500/10 rounded-full blur-[100px] pointer-events-none" />
|
||||
<div className="absolute -bottom-10 left-10 w-[450px] h-[450px] bg-purple-600/10 rounded-full blur-[120px] pointer-events-none" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
const CosmicGalaxy = dynamic(() => import("./CosmicGalaxy"), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
export default function CosmicGalaxyDynamic() {
|
||||
return <CosmicGalaxy />;
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
"use client";
|
||||
|
||||
import React, { useRef } from "react";
|
||||
import { Canvas, useFrame } from "@react-three/fiber";
|
||||
import { Float } from "@react-three/drei";
|
||||
import * as THREE from "three";
|
||||
|
||||
function CosmicArtifact() {
|
||||
const groupRef = useRef<THREE.Group>(null!);
|
||||
const ring1Ref = useRef<THREE.Mesh>(null!);
|
||||
const ring2Ref = useRef<THREE.Mesh>(null!);
|
||||
const ring3Ref = useRef<THREE.Mesh>(null!);
|
||||
const coreRef = useRef<THREE.Mesh>(null!);
|
||||
const outerGeomRef = useRef<THREE.Mesh>(null!);
|
||||
|
||||
useFrame((state, delta) => {
|
||||
if (!groupRef.current) return;
|
||||
|
||||
// Dual-axis rotation for the rings (gyroscope / celestial sphere effect)
|
||||
if (ring1Ref.current) {
|
||||
ring1Ref.current.rotation.x += delta * 0.4;
|
||||
ring1Ref.current.rotation.y += delta * 0.2;
|
||||
}
|
||||
if (ring2Ref.current) {
|
||||
ring2Ref.current.rotation.y += delta * 0.35;
|
||||
ring2Ref.current.rotation.z += delta * 0.25;
|
||||
}
|
||||
if (ring3Ref.current) {
|
||||
ring3Ref.current.rotation.z += delta * 0.3;
|
||||
ring3Ref.current.rotation.x += delta * 0.15;
|
||||
}
|
||||
|
||||
// Core pulsing & rotation
|
||||
if (coreRef.current) {
|
||||
coreRef.current.rotation.y += delta * 0.5;
|
||||
const t = state.clock.getElapsedTime();
|
||||
const scale = 1 + Math.sin(t * 2) * 0.05;
|
||||
coreRef.current.scale.set(scale, scale, scale);
|
||||
}
|
||||
|
||||
// Outer wireframe geodesic
|
||||
if (outerGeomRef.current) {
|
||||
outerGeomRef.current.rotation.y -= delta * 0.15;
|
||||
outerGeomRef.current.rotation.x += delta * 0.08;
|
||||
}
|
||||
|
||||
// Interactive pointer sway
|
||||
const targetX = (state.pointer.x * Math.PI) / 8;
|
||||
const targetY = (state.pointer.y * Math.PI) / 8;
|
||||
groupRef.current.rotation.y += (targetX - groupRef.current.rotation.y) * 0.05;
|
||||
groupRef.current.rotation.x += (-targetY - groupRef.current.rotation.x) * 0.05;
|
||||
});
|
||||
|
||||
return (
|
||||
<Float speed={2} rotationIntensity={0.6} floatIntensity={1}>
|
||||
<group ref={groupRef}>
|
||||
{/* Core glowing sphere */}
|
||||
<mesh ref={coreRef}>
|
||||
<sphereGeometry args={[1.2, 32, 32]} />
|
||||
<meshStandardMaterial
|
||||
color="#38bdf8"
|
||||
emissive="#6366f1"
|
||||
emissiveIntensity={0.8}
|
||||
roughness={0.15}
|
||||
metalness={0.9}
|
||||
wireframe={false}
|
||||
/>
|
||||
</mesh>
|
||||
|
||||
{/* Inner light source */}
|
||||
<pointLight color="#38bdf8" intensity={4} distance={8} />
|
||||
<pointLight color="#a855f7" intensity={3} distance={10} position={[0, -2, 0]} />
|
||||
|
||||
{/* Outer wireframe icosahedron/geodesic */}
|
||||
<mesh ref={outerGeomRef}>
|
||||
<icosahedronGeometry args={[2.0, 1]} />
|
||||
<meshBasicMaterial
|
||||
color="#818cf8"
|
||||
wireframe
|
||||
transparent
|
||||
opacity={0.3}
|
||||
/>
|
||||
</mesh>
|
||||
|
||||
{/* Celestial Orbital Ring 1 */}
|
||||
<mesh ref={ring1Ref}>
|
||||
<torusGeometry args={[2.5, 0.025, 16, 100]} />
|
||||
<meshStandardMaterial
|
||||
color="#38bdf8"
|
||||
emissive="#38bdf8"
|
||||
emissiveIntensity={1.5}
|
||||
roughness={0.1}
|
||||
/>
|
||||
</mesh>
|
||||
|
||||
{/* Celestial Orbital Ring 2 (Cross tilt) */}
|
||||
<mesh ref={ring2Ref} rotation={[Math.PI / 3, 0, 0]}>
|
||||
<torusGeometry args={[3.0, 0.02, 16, 100]} />
|
||||
<meshStandardMaterial
|
||||
color="#c084fc"
|
||||
emissive="#a855f7"
|
||||
emissiveIntensity={1.3}
|
||||
roughness={0.1}
|
||||
/>
|
||||
</mesh>
|
||||
|
||||
{/* Celestial Orbital Ring 3 (Outer edge) */}
|
||||
<mesh ref={ring3Ref} rotation={[-Math.PI / 4, Math.PI / 6, 0]}>
|
||||
<torusGeometry args={[3.4, 0.015, 16, 120]} />
|
||||
<meshStandardMaterial
|
||||
color="#67e8f9"
|
||||
emissive="#06b6d4"
|
||||
emissiveIntensity={1.0}
|
||||
roughness={0.1}
|
||||
/>
|
||||
</mesh>
|
||||
|
||||
{/* Orbital Starlight Satellite Node 1 */}
|
||||
<mesh position={[2.6, 0.4, 0]}>
|
||||
<sphereGeometry args={[0.08, 16, 16]} />
|
||||
<meshBasicMaterial color="#38bdf8" />
|
||||
</mesh>
|
||||
|
||||
{/* Orbital Starlight Satellite Node 2 */}
|
||||
<mesh position={[-2.2, 1.8, 0.5]}>
|
||||
<sphereGeometry args={[0.07, 16, 16]} />
|
||||
<meshBasicMaterial color="#f472b6" />
|
||||
</mesh>
|
||||
|
||||
{/* Orbital Starlight Satellite Node 3 */}
|
||||
<mesh position={[0.8, -2.8, -0.6]}>
|
||||
<sphereGeometry args={[0.09, 16, 16]} />
|
||||
<meshBasicMaterial color="#a78bfa" />
|
||||
</mesh>
|
||||
</group>
|
||||
</Float>
|
||||
);
|
||||
}
|
||||
|
||||
export default function HeroCosmicOrb() {
|
||||
return (
|
||||
<div className="relative w-full h-[380px] sm:h-[450px] lg:h-[550px] flex items-center justify-center">
|
||||
{/* Background radial glow */}
|
||||
<div className="absolute inset-0 flex items-center justify-center pointer-events-none">
|
||||
<div className="w-64 h-64 sm:w-80 sm:h-80 bg-indigo-500/20 rounded-full blur-[80px]" />
|
||||
<div className="w-48 h-48 sm:w-60 sm:h-60 bg-cyan-400/20 rounded-full blur-[60px]" />
|
||||
</div>
|
||||
|
||||
<Canvas
|
||||
camera={{ position: [0, 0, 7.5], fov: 45 }}
|
||||
dpr={[1, 1.5]}
|
||||
gl={{ antialias: true, alpha: true }}
|
||||
className="w-full h-full cursor-grab active:cursor-grabbing"
|
||||
>
|
||||
<ambientLight intensity={0.4} />
|
||||
<directionalLight position={[10, 10, 5]} intensity={1.5} color="#e0e7ff" />
|
||||
<directionalLight position={[-10, -10, -5]} intensity={0.8} color="#c084fc" />
|
||||
<CosmicArtifact />
|
||||
</Canvas>
|
||||
|
||||
{/* Holographic coordinates overlay badge */}
|
||||
<div className="absolute bottom-4 left-1/2 -translate-x-1/2 sm:left-6 sm:translate-x-0 px-3 py-1 rounded-full glass-panel border border-cyan-500/20 flex items-center gap-2 pointer-events-none text-[11px] text-cyan-300 font-mono tracking-wider shadow-lg">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-cyan-400 animate-pulse" />
|
||||
<span>CORE ARTIFACT // AG-01 ACTIVE</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,385 @@
|
||||
"use client";
|
||||
|
||||
import React, { useRef, useMemo, useState } from "react";
|
||||
import { Canvas, useFrame } from "@react-three/fiber";
|
||||
import * as THREE from "three";
|
||||
|
||||
// 1. Central Star (Helios / Core Sun) with radiant corona
|
||||
function CentralStar() {
|
||||
const sunRef = useRef<THREE.Mesh>(null!);
|
||||
const coronaRef = useRef<THREE.Mesh>(null!);
|
||||
const flareRef = useRef<THREE.Mesh>(null!);
|
||||
|
||||
useFrame((state, delta) => {
|
||||
const t = state.clock.getElapsedTime();
|
||||
if (sunRef.current) {
|
||||
sunRef.current.rotation.y += delta * 0.2;
|
||||
const pulse = 1 + Math.sin(t * 2.5) * 0.03;
|
||||
sunRef.current.scale.set(pulse, pulse, pulse);
|
||||
}
|
||||
if (coronaRef.current) {
|
||||
coronaRef.current.rotation.y -= delta * 0.15;
|
||||
coronaRef.current.rotation.z += delta * 0.08;
|
||||
const pulse2 = 1.2 + Math.cos(t * 1.8) * 0.05;
|
||||
coronaRef.current.scale.set(pulse2, pulse2, pulse2);
|
||||
}
|
||||
if (flareRef.current) {
|
||||
flareRef.current.rotation.z -= delta * 0.2;
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<group>
|
||||
{/* Intense Core Sun */}
|
||||
<mesh ref={sunRef}>
|
||||
<sphereGeometry args={[0.75, 32, 32]} />
|
||||
<meshStandardMaterial
|
||||
color="#38bdf8"
|
||||
emissive="#60a5fa"
|
||||
emissiveIntensity={2.5}
|
||||
roughness={0.2}
|
||||
/>
|
||||
</mesh>
|
||||
|
||||
{/* Atmospheric Corona Layer */}
|
||||
<mesh ref={coronaRef}>
|
||||
<sphereGeometry args={[0.95, 24, 24]} />
|
||||
<meshBasicMaterial
|
||||
color="#818cf8"
|
||||
transparent
|
||||
opacity={0.3}
|
||||
wireframe
|
||||
/>
|
||||
</mesh>
|
||||
|
||||
{/* Solar Flare Halo Disk */}
|
||||
<mesh ref={flareRef} rotation={[Math.PI / 2, 0, 0]}>
|
||||
<ringGeometry args={[0.9, 1.45, 32]} />
|
||||
<meshBasicMaterial
|
||||
color="#38bdf8"
|
||||
transparent
|
||||
opacity={0.2}
|
||||
side={THREE.DoubleSide}
|
||||
/>
|
||||
</mesh>
|
||||
|
||||
{/* Radiant Solar Lights */}
|
||||
<pointLight color="#67e8f9" intensity={4} distance={20} decay={2} />
|
||||
<pointLight color="#c084fc" intensity={2.5} distance={15} position={[0, 1, 0]} decay={2} />
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
// 2. Orbital Path Ring (crisp, faint geometric trajectory)
|
||||
function OrbitTrack({ radius, color = "#475569" }: { radius: number; color?: string }) {
|
||||
const lineGeometry = useMemo(() => {
|
||||
const points: THREE.Vector3[] = [];
|
||||
const segments = 120;
|
||||
for (let i = 0; i <= segments; i++) {
|
||||
const theta = (i / segments) * Math.PI * 2;
|
||||
points.push(new THREE.Vector3(Math.cos(theta) * radius, 0, Math.sin(theta) * radius));
|
||||
}
|
||||
return new THREE.BufferGeometry().setFromPoints(points);
|
||||
}, [radius]);
|
||||
|
||||
return (
|
||||
<primitive object={new THREE.Line(
|
||||
lineGeometry,
|
||||
new THREE.LineBasicMaterial({ color, transparent: true, opacity: 0.3 })
|
||||
)} />
|
||||
);
|
||||
}
|
||||
|
||||
// 3. Orbiting Planet with optional Rings & Moons
|
||||
interface PlanetProps {
|
||||
name: string;
|
||||
radius: number;
|
||||
size: number;
|
||||
speed: number;
|
||||
color: string;
|
||||
emissive?: string;
|
||||
hasRings?: boolean;
|
||||
ringRadius?: [number, number];
|
||||
ringColor?: string;
|
||||
hasMoon?: boolean;
|
||||
initialAngle?: number;
|
||||
onHover?: (name: string | null) => void;
|
||||
}
|
||||
|
||||
function Planet({
|
||||
name,
|
||||
radius,
|
||||
size,
|
||||
speed,
|
||||
color,
|
||||
emissive = "#000000",
|
||||
hasRings = false,
|
||||
ringRadius = [1.2, 1.8],
|
||||
ringColor = "#c084fc",
|
||||
hasMoon = false,
|
||||
initialAngle = 0,
|
||||
onHover,
|
||||
}: PlanetProps) {
|
||||
const planetGroupRef = useRef<THREE.Group>(null!);
|
||||
const planetMeshRef = useRef<THREE.Mesh>(null!);
|
||||
const moonGroupRef = useRef<THREE.Group>(null!);
|
||||
const angleRef = useRef(initialAngle);
|
||||
|
||||
useFrame((_, delta) => {
|
||||
angleRef.current += delta * speed * 0.35;
|
||||
const x = Math.cos(angleRef.current) * radius;
|
||||
const z = Math.sin(angleRef.current) * radius;
|
||||
|
||||
if (planetGroupRef.current) {
|
||||
planetGroupRef.current.position.set(x, 0, z);
|
||||
}
|
||||
if (planetMeshRef.current) {
|
||||
planetMeshRef.current.rotation.y += delta * 1.2;
|
||||
}
|
||||
if (moonGroupRef.current) {
|
||||
moonGroupRef.current.rotation.y += delta * 2.5;
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<group ref={planetGroupRef}>
|
||||
{/* The Planet Sphere */}
|
||||
<mesh
|
||||
ref={planetMeshRef}
|
||||
onPointerOver={(e) => {
|
||||
e.stopPropagation();
|
||||
onHover?.(name);
|
||||
}}
|
||||
onPointerOut={() => onHover?.(null)}
|
||||
>
|
||||
<sphereGeometry args={[size, 24, 24]} />
|
||||
<meshStandardMaterial
|
||||
color={color}
|
||||
emissive={emissive}
|
||||
emissiveIntensity={0.6}
|
||||
roughness={0.3}
|
||||
metalness={0.7}
|
||||
/>
|
||||
</mesh>
|
||||
|
||||
{/* Planetary Rings */}
|
||||
{hasRings && (
|
||||
<mesh rotation={[Math.PI / 2.5, 0.2, 0]}>
|
||||
<ringGeometry args={[size * ringRadius[0], size * ringRadius[1], 48]} />
|
||||
<meshStandardMaterial
|
||||
color={ringColor}
|
||||
emissive={ringColor}
|
||||
emissiveIntensity={0.5}
|
||||
transparent
|
||||
opacity={0.65}
|
||||
side={THREE.DoubleSide}
|
||||
/>
|
||||
</mesh>
|
||||
)}
|
||||
|
||||
{/* Orbiting Moon */}
|
||||
{hasMoon && (
|
||||
<group ref={moonGroupRef}>
|
||||
<mesh position={[size * 1.9, 0.15, 0]}>
|
||||
<sphereGeometry args={[size * 0.28, 16, 16]} />
|
||||
<meshStandardMaterial
|
||||
color="#e2e8f0"
|
||||
emissive="#94a3b8"
|
||||
emissiveIntensity={0.4}
|
||||
roughness={0.5}
|
||||
/>
|
||||
</mesh>
|
||||
</group>
|
||||
)}
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
// 4. Asteroid Belt (subtle, clean, not overcrowded)
|
||||
function AsteroidBelt({ count = 180, innerR = 3.4, outerR = 4.2 }: { count?: number; innerR?: number; outerR?: number }) {
|
||||
const beltRef = useRef<THREE.Points>(null!);
|
||||
|
||||
const [positions, colors] = useMemo(() => {
|
||||
const pos = new Float32Array(count * 3);
|
||||
const col = new Float32Array(count * 3);
|
||||
const colorA = new THREE.Color("#94a3b8");
|
||||
const colorB = new THREE.Color("#38bdf8");
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const r = innerR + Math.random() * (outerR - innerR);
|
||||
const theta = Math.random() * Math.PI * 2;
|
||||
const y = (Math.random() - 0.5) * 0.3;
|
||||
|
||||
pos[i * 3] = Math.cos(theta) * r;
|
||||
pos[i * 3 + 1] = y;
|
||||
pos[i * 3 + 2] = Math.sin(theta) * r;
|
||||
|
||||
const c = Math.random() > 0.4 ? colorA : colorB;
|
||||
col[i * 3] = c.r;
|
||||
col[i * 3 + 1] = c.g;
|
||||
col[i * 3 + 2] = c.b;
|
||||
}
|
||||
|
||||
return [pos, col];
|
||||
}, [count, innerR, outerR]);
|
||||
|
||||
useFrame((_, delta) => {
|
||||
if (beltRef.current) {
|
||||
beltRef.current.rotation.y += delta * 0.06;
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<points ref={beltRef}>
|
||||
<bufferGeometry>
|
||||
<bufferAttribute attach="attributes-position" args={[positions, 3]} />
|
||||
<bufferAttribute attach="attributes-color" args={[colors, 3]} />
|
||||
</bufferGeometry>
|
||||
<pointsMaterial
|
||||
size={0.045}
|
||||
vertexColors
|
||||
transparent
|
||||
opacity={0.55}
|
||||
sizeAttenuation
|
||||
/>
|
||||
</points>
|
||||
);
|
||||
}
|
||||
|
||||
// 5. Composite Solar System Scene with smooth interactive tilt
|
||||
function SolarSystemScene({ onHoverPlanet }: { onHoverPlanet: (name: string | null) => void }) {
|
||||
const systemRef = useRef<THREE.Group>(null!);
|
||||
|
||||
useFrame((state) => {
|
||||
if (!systemRef.current) return;
|
||||
// Subtle interactive tilt following pointer
|
||||
const targetRotX = 0.55 + (state.pointer.y * Math.PI) / 24;
|
||||
const targetRotY = -0.25 + (state.pointer.x * Math.PI) / 20;
|
||||
systemRef.current.rotation.x += (targetRotX - systemRef.current.rotation.x) * 0.04;
|
||||
systemRef.current.rotation.y += (targetRotY - systemRef.current.rotation.y) * 0.04;
|
||||
});
|
||||
|
||||
return (
|
||||
<group ref={systemRef} rotation={[0.55, -0.25, 0]}>
|
||||
{/* Central Star */}
|
||||
<CentralStar />
|
||||
|
||||
{/* Planetary Tracks — calibrated so outermost radius is 6.4 */}
|
||||
<OrbitTrack radius={1.7} color="#38bdf8" />
|
||||
<OrbitTrack radius={2.7} color="#818cf8" />
|
||||
<OrbitTrack radius={5.0} color="#c084fc" />
|
||||
<OrbitTrack radius={6.4} color="#34d399" />
|
||||
|
||||
{/* Asteroid Belt between Orbit II and Orbit III */}
|
||||
<AsteroidBelt count={190} innerR={3.5} outerR={4.3} />
|
||||
|
||||
{/* Planet 1: Chronos (Molten amber rocky core) */}
|
||||
<Planet
|
||||
name="CHRONOS // ORBIT I (TIME ENGINE)"
|
||||
radius={1.7}
|
||||
size={0.16}
|
||||
speed={1.5}
|
||||
color="#fb923c"
|
||||
emissive="#ea580c"
|
||||
initialAngle={0.5}
|
||||
onHover={onHoverPlanet}
|
||||
/>
|
||||
|
||||
{/* Planet 2: Aetheria (Teal Gem with Moon) */}
|
||||
<Planet
|
||||
name="AETHERIA // ORBIT II (FLAGSHIP SECTOR)"
|
||||
radius={2.7}
|
||||
size={0.25}
|
||||
speed={1.0}
|
||||
color="#22d3ee"
|
||||
emissive="#0891b2"
|
||||
hasMoon={true}
|
||||
initialAngle={2.2}
|
||||
onHover={onHoverPlanet}
|
||||
/>
|
||||
|
||||
{/* Planet 3: Nebula-IX (Ringed Gas Giant) */}
|
||||
<Planet
|
||||
name="NEBULA-IX // ORBIT III (CREATIVE GAS GIANT)"
|
||||
radius={5.0}
|
||||
size={0.42}
|
||||
speed={0.55}
|
||||
color="#a855f7"
|
||||
emissive="#7e22ce"
|
||||
hasRings={true}
|
||||
ringRadius={[1.3, 1.9]}
|
||||
ringColor="#c084fc"
|
||||
hasMoon={true}
|
||||
initialAngle={4.1}
|
||||
onHover={onHoverPlanet}
|
||||
/>
|
||||
|
||||
{/* Planet 4: Void-Zero (Outer Starlight World) */}
|
||||
<Planet
|
||||
name="VOID-ZERO // ORBIT IV (DEEP EXPERIMENTAL)"
|
||||
radius={6.4}
|
||||
size={0.3}
|
||||
speed={0.35}
|
||||
color="#34d399"
|
||||
emissive="#059669"
|
||||
hasMoon={true}
|
||||
initialAngle={5.6}
|
||||
onHover={onHoverPlanet}
|
||||
/>
|
||||
</group>
|
||||
);
|
||||
}
|
||||
|
||||
export default function HeroSolarSystem() {
|
||||
const [hoveredPlanet, setHoveredPlanet] = useState<string | null>(null);
|
||||
|
||||
return (
|
||||
<div className="relative w-full aspect-square max-w-[540px] max-h-[540px] sm:max-h-[580px] flex items-center justify-center select-none mx-auto overflow-visible">
|
||||
{/* Background soft ambient radial lighting */}
|
||||
<div className="absolute inset-0 flex items-center justify-center pointer-events-none">
|
||||
<div className="w-64 h-64 sm:w-80 sm:h-80 bg-cyan-500/10 rounded-full blur-[80px]" />
|
||||
<div className="w-56 h-56 sm:w-72 sm:h-72 bg-purple-600/10 rounded-full blur-[80px]" />
|
||||
</div>
|
||||
|
||||
{/*
|
||||
Camera position: [0, 11, 16] with fov: 42
|
||||
At distance ~19.4, visible height is 15.6 units and width is >18 units.
|
||||
Outermost orbit radius is 6.4 (diameter 12.8).
|
||||
-> 100% Guaranteed ZERO CROPPING at all angles.
|
||||
*/}
|
||||
<Canvas
|
||||
camera={{ position: [0, 11, 16], fov: 42 }}
|
||||
dpr={[1, 1.5]}
|
||||
gl={{ antialias: true, alpha: true }}
|
||||
className="w-full h-full cursor-grab active:cursor-grabbing overflow-visible"
|
||||
>
|
||||
<ambientLight intensity={0.4} />
|
||||
<directionalLight position={[10, 15, 10]} intensity={1.2} color="#ffffff" />
|
||||
<directionalLight position={[-10, -10, -5]} intensity={0.6} color="#818cf8" />
|
||||
<SolarSystemScene onHoverPlanet={setHoveredPlanet} />
|
||||
</Canvas>
|
||||
|
||||
{/* Interactive HUD Telemetry Card */}
|
||||
<div className="absolute bottom-2 left-1/2 -translate-x-1/2 sm:left-2 sm:translate-x-0 glass-panel px-3.5 py-1.5 rounded-xl border border-cyan-500/30 flex items-center gap-2.5 text-[11px] font-mono tracking-wider shadow-[0_0_20px_rgba(56,189,248,0.2)] pointer-events-none">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="w-2 h-2 rounded-full bg-cyan-400 animate-ping" />
|
||||
<span className="text-cyan-300 font-bold">AGHATIS ORBITAL</span>
|
||||
</div>
|
||||
<span className="text-slate-500">|</span>
|
||||
<span className="text-slate-300">
|
||||
{hoveredPlanet ? (
|
||||
<span className="text-cyan-400 font-bold">{hoveredPlanet}</span>
|
||||
) : (
|
||||
"4 ACTIVE ORBITS • TILT TO EXPLORE"
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Orbit Speed & System Badge */}
|
||||
<div className="absolute top-2 right-2 hidden sm:flex items-center gap-1.5 px-2.5 py-1 rounded-full bg-indigo-950/40 border border-indigo-500/30 text-[10px] font-mono text-indigo-300">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-indigo-400" />
|
||||
<span>SIMULATION: 1.0X</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user