34034-vm/assets/js/main.js
Flatlogic Bot 069a494d39 asteroids
2025-09-12 15:21:14 +00:00

384 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// Scene
const scene = new THREE.Scene();
// Camera
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 25, 45);
camera.lookAt(scene.position);
// Renderer
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Controls
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled
controls.dampingFactor = 0.05;
controls.screenSpacePanning = false;
controls.minDistance = 10;
controls.maxDistance = 500;
// Lighting
const pointLight = new THREE.PointLight(0xffffff, 5, 3000); // Increased intensity and distance
scene.add(pointLight);
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6); // Increased ambient light
scene.add(ambientLight);
// Texture Loader
const textureLoader = new THREE.TextureLoader();
// Stars
const starVertices = [];
for (let i = 0; i < 10000; i++) {
const x = (Math.random() - 0.5) * 2000;
const y = (Math.random() - 0.5) * 2000;
const z = (Math.random() - 0.5) * 2000;
starVertices.push(x, y, z);
}
const starGeometry = new THREE.BufferGeometry();
starGeometry.setAttribute('position', new THREE.Float32BufferAttribute(starVertices, 3));
const starMaterial = new THREE.PointsMaterial({
color: 0xffffff,
size: 0.7
});
const stars = new THREE.Points(starGeometry, starMaterial);
scene.add(stars);
// --- Sun ---
const sunTexture = textureLoader.load('assets/textures/sun.jpg');
const sunMaterial = new THREE.MeshBasicMaterial({ map: sunTexture });
const sun = new THREE.Mesh(new THREE.SphereGeometry(5, 32, 32), sunMaterial);
scene.add(sun);
// --- Planets & Orbits ---
const segments = 128;
const orbitMaterial = new THREE.LineBasicMaterial({ color: 0xffffff, opacity: 0.5, transparent: true });
function createPlanet(radius, textureFile, distance, orbitSpeed) {
const texture = textureLoader.load(`assets/textures/${textureFile}?v=${Date.now()}`);
const geometry = new THREE.SphereGeometry(radius, 32, 32);
const material = new THREE.MeshBasicMaterial({ map: texture }); // Reverted
const planet = new THREE.Mesh(geometry, material);
const pivot = new THREE.Object3D();
sun.add(pivot);
pivot.add(planet);
planet.position.x = distance;
// Trail setup
const trailPoints = [];
const trailGeometry = new THREE.BufferGeometry();
const trailMaterial = new THREE.LineBasicMaterial({ color: 0xffffff, opacity: 0.5, transparent: true });
const trail = new THREE.Line(trailGeometry, trailMaterial);
scene.add(trail);
return { planet, pivot, orbitSpeed, trail, trailPoints };
}
const mercuryData = createPlanet(0.4, 'mercury.jpg', 7, 0.0297);
const venusData = createPlanet(0.9, 'venus.jpg', 11, 0.0116);
const earthData = createPlanet(1, 'earth.jpg', 15, 0.00717); // Adjusted for 365-day year
const marsData = createPlanet(0.7, 'mars.jpg', 20, 0.0038);
const jupiterData = createPlanet(3, 'jupiter.jpg', 30, 0.0006);
const saturnData = createPlanet(2.5, 'saturn.jpg', 40, 0.00024);
const uranusData = createPlanet(2, 'uranus.jpg', 50, 0.000085);
const neptuneData = createPlanet(1.9, 'neptune.jpg', 60, 0.000043);
// --- Saturn's Rings ---
const ringGeometry = new THREE.RingGeometry(3.5, 5, 64);
const ringMaterial = new THREE.MeshBasicMaterial({
color: 0xffffff,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.6
});
const ring = new THREE.Mesh(ringGeometry, ringMaterial);
ring.rotation.x = -Math.PI / 2.5; // Tilt the rings
saturnData.planet.add(ring);
// --- Moon ---
const moonGeometry = new THREE.SphereGeometry(0.25, 32, 32);
const moonMaterial = new THREE.MeshBasicMaterial({ color: 0x888888 });
const moon = new THREE.Mesh(moonGeometry, moonMaterial);
const moonPivot = new THREE.Object3D();
earthData.planet.add(moonPivot);
moonPivot.add(moon);
moon.position.x = 2;
// --- Asteroid Belt ---
const asteroidCount = 1500;
const asteroidGeometry = new THREE.SphereGeometry(0.05, 8, 8); // A small sphere for asteroids
const asteroidMaterial = new THREE.MeshStandardMaterial({ color: 0x999999, roughness: 0.9 });
const asteroidMesh = new THREE.InstancedMesh(asteroidGeometry, asteroidMaterial, asteroidCount);
const beltMinRadius = 23;
const beltMaxRadius = 28;
const beltHeight = 1.5; // Vertical thickness
const dummy = new THREE.Object3D();
const asteroidData = [];
for (let i = 0; i < asteroidCount; i++) {
const radius = Math.random() * (beltMaxRadius - beltMinRadius) + beltMinRadius;
const angle = Math.random() * Math.PI * 2;
const y = (Math.random() - 0.5) * beltHeight;
const orbitSpeed = (Math.random() * 0.002 + 0.0005); // Slower than Mars, faster than Jupiter
const rotationSpeed = Math.random() * 0.05;
asteroidData.push({ radius, angle, y, orbitSpeed, rotationSpeed });
dummy.position.set(
Math.cos(angle) * radius,
y,
Math.sin(angle) * radius
);
dummy.updateMatrix();
asteroidMesh.setMatrixAt(i, dummy.matrix);
}
scene.add(asteroidMesh);
const planets = [mercuryData, venusData, earthData, marsData, jupiterData, saturnData, uranusData, neptuneData];
// Planet Data
const planetData = {
[earthData.planet.uuid]: {
name: 'Earth',
mass: '5.97 × 10^24 kg',
radius: '6,371 km',
orbital_speed: '29.78 km/s',
},
[mercuryData.planet.uuid]: {
name: 'Mercury',
mass: '3.285 × 10^23 kg',
radius: '2,439.7 km',
orbital_speed: '47.36 km/s',
},
[venusData.planet.uuid]: {
name: 'Venus',
mass: '4.867 × 10^24 kg',
radius: '6,051.8 km',
orbital_speed: '35.02 km/s',
},
[marsData.planet.uuid]: {
name: 'Mars',
mass: '6.39 × 10^23 kg',
radius: '3,389.5 km',
orbital_speed: '24.07 km/s',
},
[jupiterData.planet.uuid]: {
name: 'Jupiter',
mass: '1.898 × 10^27 kg',
radius: '69,911 km',
orbital_speed: '13.07 km/s',
},
[saturnData.planet.uuid]: {
name: 'Saturn',
mass: '5.683 × 10^26 kg',
radius: '58,232 km',
orbital_speed: '9.69 km/s',
},
[uranusData.planet.uuid]: {
name: 'Uranus',
mass: '8.681 × 10^25 kg',
radius: '25,362 km',
orbital_speed: '6.81 km/s',
},
[neptuneData.planet.uuid]: {
name: 'Neptune',
mass: '1.024 × 10^26 kg',
radius: '24,622 km',
orbital_speed: '5.43 km/s',
}
};
// --- Selection Outline ---
let outlineMesh;
const outlineMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.BackSide });
const outlineScale = 1.15;
function setOutline(object) {
if (outlineMesh && outlineMesh.parent) {
outlineMesh.parent.remove(outlineMesh);
}
outlineMesh = new THREE.Mesh(object.geometry, outlineMaterial);
outlineMesh.scale.set(outlineScale, outlineScale, outlineScale);
object.add(outlineMesh);
}
// Stats Panel
const statsInfo = document.getElementById('stats-info');
const statsButtons = document.getElementById('stats-buttons');
function updateStats(planetUUID) {
const data = planetData[planetUUID];
if (data) {
statsInfo.innerHTML = `
<h3>${data.name}</h3>
<p>Mass: ${data.mass}</p>
<p>Radius: ${data.radius}</p>
<p>Orbital Speed: ${data.orbital_speed}</p>
`;
}
}
statsButtons.addEventListener('click', (event) => {
if (event.target.tagName === 'BUTTON') {
const planetName = event.target.dataset.planet;
let planetObject, planetUUID;
statsButtons.querySelectorAll('button').forEach(btn => btn.classList.remove('active'));
event.target.classList.add('active');
if (planetName === 'earth') {
planetObject = earthData.planet;
planetUUID = earthData.planet.uuid;
} else if (planetName === 'mercury') {
planetObject = mercuryData.planet;
planetUUID = mercuryData.planet.uuid;
} else if (planetName === 'venus') {
planetObject = venusData.planet;
planetUUID = venusData.planet.uuid;
} else if (planetName === 'mars') {
planetObject = marsData.planet;
planetUUID = marsData.planet.uuid;
} else if (planetName === 'jupiter') {
planetObject = jupiterData.planet;
planetUUID = jupiterData.planet.uuid;
} else if (planetName === 'saturn') {
planetObject = saturnData.planet;
planetUUID = saturnData.planet.uuid;
} else if (planetName === 'uranus') {
planetObject = uranusData.planet;
planetUUID = uranusData.planet.uuid;
} else if (planetName === 'neptune') {
planetObject = neptuneData.planet;
planetUUID = neptuneData.planet.uuid;
}
if (planetUUID) {
updateStats(planetUUID);
if (planetObject) {
setOutline(planetObject);
}
}
}
});
// Display Earth's stats by default and set button to active
updateStats(earthData.planet.uuid);
statsButtons.querySelector('button[data-planet="earth"]').classList.add('active');
setOutline(earthData.planet); // Initial outline for Earth
// --- Controls Logic ---
let isPaused = false;
const pauseButton = document.getElementById('pause-button');
const speedToggleButton = document.getElementById('speed-toggle-button');
pauseButton.addEventListener('click', () => {
isPaused = !isPaused;
pauseButton.textContent = isPaused ? 'Play' : 'Pause';
});
const speeds = [1, 100, 500];
let currentSpeedIndex = 0;
let timeScale = speeds[currentSpeedIndex]; // Start with 1x speed
speedToggleButton.addEventListener('click', () => {
currentSpeedIndex = (currentSpeedIndex + 1) % speeds.length;
timeScale = speeds[currentSpeedIndex];
speedToggleButton.textContent = 'Speed: ' + timeScale + 'x';
});
// --- Time & Simulation ---
const clock = new THREE.Clock();
const timeContainer = document.getElementById('time-container');
let simulationTime = new Date();
const ORBIT_SPEED_MULTIPLIER = 0.1;
const TIME_SPEED_FACTOR = 3600; // 1 real second = 1 simulation hour at 1x
function updateTimeDisplay() {
const hours = String(simulationTime.getHours()).padStart(2, '0');
const minutes = String(simulationTime.getMinutes()).padStart(2, '0');
const seconds = String(simulationTime.getSeconds()).padStart(2, '0');
const day = String(simulationTime.getDate()).padStart(2, '0');
const month = String(simulationTime.getMonth() + 1).padStart(2, '0'); // Month is 0-indexed
const year = simulationTime.getFullYear();
timeContainer.textContent = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
}
// Animation loop
function animate() {
requestAnimationFrame(animate);
// Get time delta for frame-rate independent animation
const delta = clock.getDelta();
// Update controls
controls.update();
if (!isPaused) {
// Advance simulation time using milliseconds for precision
const incrementMs = delta * 1000 * timeScale * TIME_SPEED_FACTOR;
simulationTime.setTime(simulationTime.getTime() + incrementMs);
// Sun's self-rotation (can be slower and independent of timeScale)
sun.rotation.y += 0.005 * delta;
const maxTrailPoints = 200;
const worldPosition = new THREE.Vector3();
planets.forEach(p => {
// Orbit speed is now correctly scaled by timeScale
p.pivot.rotation.y += p.orbitSpeed * ORBIT_SPEED_MULTIPLIER * delta * timeScale;
// Planet's self-rotation (can also be constant)
p.planet.rotation.y += 0.05 * delta;
// Update trail
p.planet.getWorldPosition(worldPosition);
p.trailPoints.push(worldPosition.clone());
if (p.trailPoints.length > maxTrailPoints) {
p.trailPoints.shift();
}
p.trail.geometry.setFromPoints(p.trailPoints);
});
// Moon's orbit (also scaled by timeScale)
moonPivot.rotation.y += 0.1 * ORBIT_SPEED_MULTIPLIER * delta * timeScale;
}
// Update time display every frame
updateTimeDisplay();
renderer.render(scene, camera);
}
animate();
// Handle window resize
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// Initial call to set time
updateTimeDisplay();