Revert to version 79dd707
This commit is contained in:
parent
26e8d7ac7d
commit
44b0413929
@ -3,21 +3,6 @@ body {
|
||||
overflow: hidden;
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
#date-container {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
color: white;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
font-family: sans-serif;
|
||||
font-size: 1.2em;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
canvas {
|
||||
display: block;
|
||||
}
|
||||
@ -57,78 +42,40 @@ canvas {
|
||||
border-color: #007bff;
|
||||
}
|
||||
|
||||
#time-controls {
|
||||
#controls-container {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
left: 20px;
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
z-index: 10;
|
||||
color: white;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
#time-controls button {
|
||||
background-color: #333;
|
||||
color: white;
|
||||
border: 1px solid #555;
|
||||
padding: 5px 10px;
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
#time-controls button:hover {
|
||||
background-color: #555;
|
||||
}
|
||||
|
||||
#time-controls span {
|
||||
margin-left: 10px;
|
||||
font-size: 0.9em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.planet-label {
|
||||
position: absolute;
|
||||
color: white;
|
||||
font-family: sans-serif;
|
||||
font-size: 12px;
|
||||
text-shadow: 1px 1px 2px black;
|
||||
pointer-events: none; /* So they don't interfere with mouse controls */
|
||||
z-index: 11;
|
||||
}
|
||||
|
||||
#controls-top-left {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
#scale-container, #focus-container {
|
||||
color: white;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
font-family: sans-serif;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
#scale-container label {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
#focus-container label {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
#focus-container select {
|
||||
#controls-container button {
|
||||
background-color: #333;
|
||||
color: white;
|
||||
border: 1px solid #555;
|
||||
padding: 10px 20px;
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#controls-container button:hover {
|
||||
background-color: #555;
|
||||
}
|
||||
|
||||
#time-container {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
color: white;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
font-family: monospace;
|
||||
font-size: 1.2em;
|
||||
z-index: 10;
|
||||
}
|
||||
@ -1,328 +1,383 @@
|
||||
import * as THREE from 'three';
|
||||
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
||||
|
||||
// --- SCENE, CAMERA, RENDERER, CONTROLS ---
|
||||
// Scene
|
||||
const scene = new THREE.Scene();
|
||||
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 50000);
|
||||
|
||||
// Camera
|
||||
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
||||
camera.position.set(0, 25, 45);
|
||||
camera.lookAt(scene.position);
|
||||
|
||||
const renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||
// 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;
|
||||
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 = 1;
|
||||
controls.maxDistance = 20000;
|
||||
controls.minDistance = 10;
|
||||
controls.maxDistance = 500;
|
||||
|
||||
// --- LIGHTING ---
|
||||
const pointLight = new THREE.PointLight(0xffffff, 5, 60000);
|
||||
// Lighting
|
||||
const pointLight = new THREE.PointLight(0xffffff, 5, 3000); // Increased intensity and distance
|
||||
scene.add(pointLight);
|
||||
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
|
||||
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6); // Increased ambient light
|
||||
scene.add(ambientLight);
|
||||
|
||||
// --- TEXTURES & ASSETS ---
|
||||
|
||||
// Texture Loader
|
||||
const textureLoader = new THREE.TextureLoader();
|
||||
|
||||
// --- STARS BACKGROUND ---
|
||||
// Stars
|
||||
const starVertices = [];
|
||||
for (let i = 0; i < 15000; i++) {
|
||||
const x = (Math.random() - 0.5) * 25000;
|
||||
const y = (Math.random() - 0.5) * 25000;
|
||||
const z = (Math.random() - 0.5) * 25000;
|
||||
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: 1.5 });
|
||||
|
||||
const starMaterial = new THREE.PointsMaterial({
|
||||
color: 0xffffff,
|
||||
size: 0.7
|
||||
});
|
||||
|
||||
const stars = new THREE.Points(starGeometry, starMaterial);
|
||||
scene.add(stars);
|
||||
|
||||
// --- SCALE CONSTANTS ---
|
||||
const SCALES = {
|
||||
artistic: {
|
||||
sunSize: 5,
|
||||
planetScale: 1,
|
||||
orbitScale: 1,
|
||||
moonOrbitScale: 1,
|
||||
},
|
||||
realistic: {
|
||||
sunSize: 0.5,
|
||||
planetScale: 0.01,
|
||||
orbitScale: 100,
|
||||
moonOrbitScale: 0.1,
|
||||
}
|
||||
};
|
||||
let currentScale = 'artistic';
|
||||
|
||||
// --- CELESTIAL BODY DATA & CREATION ---
|
||||
const celestialBodies = [];
|
||||
|
||||
// --- SUN ---
|
||||
// --- Sun ---
|
||||
const sunTexture = textureLoader.load('assets/textures/sun.jpg');
|
||||
const sunMaterial = new THREE.MeshBasicMaterial({ map: sunTexture });
|
||||
const sun = new THREE.Mesh(new THREE.SphereGeometry(1, 32, 32), sunMaterial);
|
||||
sun.name = 'Sun';
|
||||
const sun = new THREE.Mesh(new THREE.SphereGeometry(5, 32, 32), sunMaterial);
|
||||
scene.add(sun);
|
||||
celestialBodies.push({ name: 'Sun', object: sun, data: { radius: 696340 } }); // Real radius in km
|
||||
|
||||
function createPlanet(name, textureFile, data) {
|
||||
// --- Planets & Orbits ---
|
||||
const segments = 128;
|
||||
const orbitMaterial = new THREE.LineBasicMaterial({ color: 0xffffff, opacity: 0.5, transparent: true });
|
||||
|
||||
function createPlanet(radius, textureFile, distance, orbitalPeriodDays) {
|
||||
const texture = textureLoader.load(`assets/textures/${textureFile}?v=${Date.now()}`);
|
||||
const geometry = new THREE.SphereGeometry(data.radius, 32, 32);
|
||||
const material = new THREE.MeshBasicMaterial({ map: texture });
|
||||
const geometry = new THREE.SphereGeometry(radius, 32, 32);
|
||||
const material = new THREE.MeshBasicMaterial({ map: texture }); // Reverted
|
||||
const planet = new THREE.Mesh(geometry, material);
|
||||
planet.name = name;
|
||||
|
||||
const pivot = new THREE.Object3D();
|
||||
sun.add(pivot);
|
||||
pivot.add(planet);
|
||||
planet.position.x = distance;
|
||||
|
||||
const planetObj = {
|
||||
name,
|
||||
object: planet,
|
||||
pivot,
|
||||
data,
|
||||
};
|
||||
celestialBodies.push(planetObj);
|
||||
return planetObj;
|
||||
// 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, orbitalPeriodDays, trail, trailPoints };
|
||||
}
|
||||
|
||||
function createMoon(name, color, data, parentPlanetObject) {
|
||||
const geometry = new THREE.SphereGeometry(data.radius, 32, 32);
|
||||
const material = new THREE.MeshBasicMaterial({ color });
|
||||
const moon = new THREE.Mesh(geometry, material);
|
||||
moon.name = name;
|
||||
const mercuryData = createPlanet(0.4, 'mercury.jpg', 7, 88);
|
||||
const venusData = createPlanet(0.9, 'venus.jpg', 11, 225);
|
||||
const earthData = createPlanet(1, 'earth.jpg', 15, 365);
|
||||
const marsData = createPlanet(0.7, 'mars.jpg', 20, 687);
|
||||
const jupiterData = createPlanet(3, 'jupiter.jpg', 30, 4333);
|
||||
const saturnData = createPlanet(2.5, 'saturn.jpg', 40, 10759);
|
||||
const uranusData = createPlanet(2, 'uranus.jpg', 50, 30687);
|
||||
const neptuneData = createPlanet(1.9, 'neptune.jpg', 60, 60190);
|
||||
|
||||
const pivot = new THREE.Object3D();
|
||||
parentPlanetObject.add(pivot);
|
||||
pivot.add(moon);
|
||||
|
||||
const moonObj = {
|
||||
name,
|
||||
object: moon,
|
||||
pivot,
|
||||
data,
|
||||
parent: parentPlanetObject
|
||||
};
|
||||
celestialBodies.push(moonObj);
|
||||
return moonObj;
|
||||
}
|
||||
|
||||
function createRings(innerRadius, outerRadius, color, opacity, tilt) {
|
||||
const ringGeometry = new THREE.RingGeometry(innerRadius, outerRadius, 64);
|
||||
const ringMaterial = new THREE.MeshBasicMaterial({
|
||||
color: color,
|
||||
// --- 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: opacity
|
||||
});
|
||||
const ring = new THREE.Mesh(ringGeometry, ringMaterial);
|
||||
ring.rotation.x = tilt;
|
||||
return ring;
|
||||
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);
|
||||
}
|
||||
|
||||
const celestialData = {
|
||||
mercury: { radius: 0.4, distance: 7, period: 88 },
|
||||
venus: { radius: 0.9, distance: 11, period: 225 },
|
||||
earth: { radius: 1, distance: 15, period: 365 },
|
||||
mars: { radius: 0.7, distance: 20, period: 687 },
|
||||
jupiter: { radius: 3, distance: 30, period: 4333 },
|
||||
saturn: { radius: 2.5, distance: 40, period: 10759 },
|
||||
uranus: { radius: 2, distance: 50, period: 30687 },
|
||||
neptune: { radius: 1.9, distance: 60, period: 60190 },
|
||||
moon: { radius: 0.25, distance: 2, period: 27.3 },
|
||||
io: { radius: 0.2, distance: 3.5, period: 1.769 },
|
||||
europa: { radius: 0.2, distance: 4.0, period: 3.551 },
|
||||
ganymede: { radius: 0.3, distance: 4.5, period: 7.155 },
|
||||
callisto: { radius: 0.3, distance: 5.0, period: 16.689 },
|
||||
titan: { radius: 0.4, distance: 6.0, period: 15.95 },
|
||||
triton: { radius: 0.35, distance: 4.0, period: 5.88 },
|
||||
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',
|
||||
}
|
||||
};
|
||||
|
||||
// --- INSTANTIATE BODIES ---
|
||||
const mercury = createPlanet('Mercury', 'mercury.jpg', celestialData.mercury);
|
||||
const venus = createPlanet('Venus', 'venus.jpg', celestialData.venus);
|
||||
const earth = createPlanet('Earth', 'earth.jpg', celestialData.earth);
|
||||
const mars = createPlanet('Mars', 'mars.jpg', celestialData.mars);
|
||||
const jupiter = createPlanet('Jupiter', 'jupiter.jpg', celestialData.jupiter);
|
||||
const saturn = createPlanet('Saturn', 'saturn.jpg', celestialData.saturn);
|
||||
const uranus = createPlanet('Uranus', 'uranus.jpg', celestialData.uranus);
|
||||
const neptune = createPlanet('Neptune', 'neptune.jpg', celestialData.neptune);
|
||||
// --- Selection Outline ---
|
||||
let outlineMesh;
|
||||
const outlineMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.BackSide });
|
||||
const outlineScale = 1.15;
|
||||
|
||||
createMoon('Moon', 0x888888, celestialData.moon, earth.object);
|
||||
createMoon('Io', 0xffff00, celestialData.io, jupiter.object);
|
||||
createMoon('Europa', 0xffa500, celestialData.europa, jupiter.object);
|
||||
createMoon('Ganymede', 0x00ff00, celestialData.ganymede, jupiter.object);
|
||||
createMoon('Callisto', 0x0000ff, celestialData.callisto, jupiter.object);
|
||||
createMoon('Titan', 0xFFAC1C, celestialData.titan, saturn.object);
|
||||
createMoon('Triton', 0xADD8E6, celestialData.triton, neptune.object);
|
||||
function setOutline(object) {
|
||||
if (outlineMesh && outlineMesh.parent) {
|
||||
outlineMesh.parent.remove(outlineMesh);
|
||||
}
|
||||
|
||||
// --- RINGS ---
|
||||
saturn.object.add(createRings(3.5, 5, 0xffffff, 0.6, -Math.PI / 2.5));
|
||||
jupiter.object.add(createRings(3.5, 4.5, 0x8c7853, 0.4, -Math.PI / 2));
|
||||
uranus.object.add(createRings(2.5, 3.5, 0xadd8e6, 0.3, -Math.PI / 2));
|
||||
neptune.object.add(createRings(2.4, 3, 0xadd8e6, 0.2, -Math.PI / 2.5));
|
||||
|
||||
// --- UI & CONTROLS ---
|
||||
const scaleToggle = document.getElementById('scale-toggle');
|
||||
const focusSelect = document.getElementById('focus-select');
|
||||
const dateDisplay = document.getElementById('date-container');
|
||||
const slowDownButton = document.getElementById('slow-down');
|
||||
const pauseToggleButton = document.getElementById('pause-toggle');
|
||||
const speedUpButton = document.getElementById('speed-up');
|
||||
const speedDisplay = document.getElementById('speed-display');
|
||||
|
||||
// Populate Focus Dropdown
|
||||
celestialBodies.forEach(body => {
|
||||
const option = document.createElement('option');
|
||||
option.value = body.name;
|
||||
option.textContent = body.name;
|
||||
focusSelect.appendChild(option);
|
||||
});
|
||||
|
||||
function focusOnObject(name) {
|
||||
const body = celestialBodies.find(b => b.name === name);
|
||||
if (!body) return;
|
||||
|
||||
const target = new THREE.Vector3();
|
||||
body.object.getWorldPosition(target);
|
||||
controls.target.copy(target);
|
||||
|
||||
const size = body.object.geometry.parameters.radius * body.object.scale.x;
|
||||
const offset = size * 5;
|
||||
|
||||
const cameraPosition = new THREE.Vector3(target.x + offset, target.y + offset / 2, target.z + offset);
|
||||
camera.position.copy(cameraPosition);
|
||||
controls.update();
|
||||
outlineMesh = new THREE.Mesh(object.geometry, outlineMaterial);
|
||||
outlineMesh.scale.set(outlineScale, outlineScale, outlineScale);
|
||||
object.add(outlineMesh);
|
||||
}
|
||||
|
||||
focusSelect.addEventListener('change', (event) => {
|
||||
focusOnObject(event.target.value);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
scaleToggle.addEventListener('change', (event) => {
|
||||
const scale = event.target.checked ? 'realistic' : 'artistic';
|
||||
updateScale(scale);
|
||||
});
|
||||
// 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
|
||||
|
||||
// --- TIME CONTROLS ---
|
||||
let simulationSpeed = 1; // Represents days per second
|
||||
|
||||
// --- Controls Logic ---
|
||||
let isPaused = false;
|
||||
let currentDate = new Date();
|
||||
const pauseButton = document.getElementById('pause-button');
|
||||
const speedToggleButton = document.getElementById('speed-toggle-button');
|
||||
|
||||
function updateSpeedDisplay() {
|
||||
speedDisplay.textContent = `Speed: ${simulationSpeed}x`;
|
||||
}
|
||||
|
||||
pauseToggleButton.addEventListener('click', () => {
|
||||
pauseButton.addEventListener('click', () => {
|
||||
isPaused = !isPaused;
|
||||
pauseToggleButton.textContent = isPaused ? 'Resume' : 'Pause';
|
||||
pauseButton.textContent = isPaused ? 'Play' : 'Pause';
|
||||
});
|
||||
|
||||
slowDownButton.addEventListener('click', () => {
|
||||
simulationSpeed /= 2;
|
||||
updateSpeedDisplay();
|
||||
});
|
||||
const speeds = [1, 100, 500, 1000, 10000];
|
||||
let currentSpeedIndex = 0;
|
||||
let timeScale = speeds[currentSpeedIndex]; // Start with 1x speed
|
||||
|
||||
speedUpButton.addEventListener('click', () => {
|
||||
simulationSpeed *= 2;
|
||||
updateSpeedDisplay();
|
||||
speedToggleButton.addEventListener('click', () => {
|
||||
currentSpeedIndex = (currentSpeedIndex + 1) % speeds.length;
|
||||
timeScale = speeds[currentSpeedIndex];
|
||||
speedToggleButton.textContent = 'Speed: ' + timeScale + 'x';
|
||||
});
|
||||
|
||||
|
||||
// --- SCALE UPDATE LOGIC ---
|
||||
function updateScale(scaleType) {
|
||||
currentScale = scaleType;
|
||||
const scaleConfig = SCALES[scaleType];
|
||||
// --- Time & Simulation ---
|
||||
const clock = new THREE.Clock();
|
||||
const timeContainer = document.getElementById('time-container');
|
||||
let simulationTime = new Date();
|
||||
const TIME_SPEED_FACTOR = 60; // 1 real second = 1 simulation minute at 1x
|
||||
const MS_IN_A_DAY = 24 * 60 * 60 * 1000;
|
||||
const MOON_ORBIT_DAYS = 27.3;
|
||||
|
||||
sun.scale.setScalar(scaleConfig.sunSize);
|
||||
|
||||
celestialBodies.forEach(body => {
|
||||
if (body.name === 'Sun') return;
|
||||
|
||||
const { object, pivot, data } = body;
|
||||
object.scale.setScalar(scaleConfig.planetScale);
|
||||
|
||||
if (pivot) {
|
||||
if (body.parent) {
|
||||
object.position.x = data.distance * scaleConfig.moonOrbitScale;
|
||||
} else {
|
||||
object.position.x = data.distance * scaleConfig.orbitScale;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (scaleType === 'realistic') {
|
||||
controls.maxDistance = 400000;
|
||||
controls.minDistance = 0.01;
|
||||
} else {
|
||||
controls.maxDistance = 1000;
|
||||
controls.minDistance = 1;
|
||||
}
|
||||
focusOnObject(focusSelect.value);
|
||||
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}`;
|
||||
}
|
||||
|
||||
// Initial setup
|
||||
updateScale('artistic');
|
||||
updateSpeedDisplay();
|
||||
|
||||
// --- LABELS ---
|
||||
const labels = celestialBodies.map(body => ({
|
||||
object: body.object,
|
||||
element: document.getElementById(`label-${body.name.toLowerCase()}`),
|
||||
})).filter(label => label.element);
|
||||
|
||||
// --- ANIMATION LOOP ---
|
||||
const clock = new THREE.Clock();
|
||||
let simulationTime = 0;
|
||||
const ORBIT_SPEED_FACTOR = 0.1; // Slows down overall orbit speed
|
||||
|
||||
// Animation loop
|
||||
function animate() {
|
||||
requestAnimationFrame(animate);
|
||||
const deltaTime = clock.getDelta();
|
||||
|
||||
// Get time delta for frame-rate independent animation
|
||||
let delta = clock.getDelta();
|
||||
// Clamp the delta to a maximum value to prevent large jumps, e.g., on the first frame or after tab refocus.
|
||||
delta = Math.min(delta, 0.1);
|
||||
|
||||
// Update controls
|
||||
controls.update();
|
||||
|
||||
if (!isPaused) {
|
||||
const timePassed = deltaTime * simulationSpeed;
|
||||
simulationTime += timePassed;
|
||||
// Advance current date by days
|
||||
currentDate.setDate(currentDate.getDate() + timePassed);
|
||||
// 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 => {
|
||||
const orbitalPeriodMs = p.orbitalPeriodDays * MS_IN_A_DAY;
|
||||
const angleIncrement = (incrementMs / orbitalPeriodMs) * (2 * Math.PI);
|
||||
p.pivot.rotation.y += angleIncrement;
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
// Update Date Display
|
||||
dateDisplay.textContent = currentDate.toDateString();
|
||||
|
||||
sun.rotation.y += 0.0005 * simulationSpeed * deltaTime;
|
||||
|
||||
celestialBodies.forEach(body => {
|
||||
if (!body.pivot || !body.data.period) return;
|
||||
|
||||
const angle = (simulationTime * ORBIT_SPEED_FACTOR) / body.data.period * (2 * Math.PI);
|
||||
body.pivot.rotation.y = angle;
|
||||
|
||||
body.object.rotation.y += 0.05 * deltaTime * simulationSpeed;
|
||||
p.trail.geometry.setFromPoints(p.trailPoints);
|
||||
});
|
||||
|
||||
// Update labels
|
||||
labels.forEach(labelData => {
|
||||
const { object, element } = labelData;
|
||||
if (!element) return;
|
||||
const vector = new THREE.Vector3();
|
||||
object.getWorldPosition(vector);
|
||||
vector.project(camera);
|
||||
// Moon's orbit (also scaled by timeScale)
|
||||
const moonOrbitalPeriodMs = MOON_ORBIT_DAYS * MS_IN_A_DAY;
|
||||
const moonAngleIncrement = (incrementMs / moonOrbitalPeriodMs) * (2 * Math.PI);
|
||||
moonPivot.rotation.y += moonAngleIncrement;
|
||||
}
|
||||
|
||||
const x = (vector.x * 0.5 + 0.5) * window.innerWidth;
|
||||
const y = (vector.y * -0.5 + 0.5) * window.innerHeight;
|
||||
// Update time display every frame
|
||||
updateTimeDisplay();
|
||||
|
||||
element.style.transform = `translate(-50%, -50%) translate(${x}px, ${y}px)`;
|
||||
});
|
||||
|
||||
controls.update();
|
||||
renderer.render(scene, camera);
|
||||
}
|
||||
|
||||
|
||||
animate();
|
||||
|
||||
// Handle window resize
|
||||
@ -331,3 +386,6 @@ window.addEventListener('resize', () => {
|
||||
camera.updateProjectionMatrix();
|
||||
renderer.setSize(window.innerWidth, window.innerHeight);
|
||||
});
|
||||
|
||||
// Initial call to set time
|
||||
updateTimeDisplay();
|
||||
|
||||
42
index.php
42
index.php
@ -7,27 +7,7 @@
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||
</head>
|
||||
<body>
|
||||
<div id="date-container"></div>
|
||||
<div id="controls-top-left">
|
||||
<div id="scale-container">
|
||||
<input type="checkbox" id="scale-toggle">
|
||||
<label for="scale-toggle">Realistic Scale</label>
|
||||
</div>
|
||||
<div id="focus-container">
|
||||
<label for="focus-select">Focus on:</label>
|
||||
<select id="focus-select">
|
||||
<!-- Populated by JS -->
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="time-controls">
|
||||
<button id="slow-down">Slower</button>
|
||||
<button id="pause-toggle">Pause</button>
|
||||
<button id="speed-up">Faster</button>
|
||||
<span id="speed-display">Speed: 1x</span>
|
||||
</div>
|
||||
|
||||
<div id="time-container"></div>
|
||||
<div id="stats-container">
|
||||
<div id="stats-buttons">
|
||||
<button data-planet="mercury">Mercury</button>
|
||||
@ -41,7 +21,10 @@
|
||||
</div>
|
||||
<div id="stats-info"></div>
|
||||
</div>
|
||||
|
||||
<div id="controls-container">
|
||||
<button id="pause-button">Pause</button>
|
||||
<button id="speed-toggle-button">Speed: 1x</button>
|
||||
</div>
|
||||
<script type="importmap">
|
||||
{
|
||||
"imports": {
|
||||
@ -50,21 +33,6 @@
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<div class="planet-label" id="label-sun">Sun</div>
|
||||
<div class="planet-label" id="label-mercury">Mercury</div>
|
||||
<div class="planet-label" id="label-venus">Venus</div>
|
||||
<div class="planet-label" id="label-earth">Earth</div>
|
||||
<div class="planet-label" id="label-mars">Mars</div>
|
||||
<div class="planet-label" id="label-jupiter">Jupiter</div>
|
||||
<div class="planet-label" id="label-saturn">Saturn</div>
|
||||
<div class="planet-label" id="label-uranus">Uranus</div>
|
||||
<div class="planet-label" id="label-neptune">Neptune</div>
|
||||
<div class="planet-label" id="label-io">Io</div>
|
||||
<div class="planet-label" id="label-europa">Europa</div>
|
||||
<div class="planet-label" id="label-ganymede">Ganymede</div>
|
||||
<div class="planet-label" id="label-callisto">Callisto</div>
|
||||
<div class="planet-label" id="label-titan">Titan</div>
|
||||
<div class="planet-label" id="label-triton">Triton</div>
|
||||
<script type="module" src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
x
Reference in New Issue
Block a user