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.04); const venusData = createPlanet(0.9, 'venus.jpg', 11, 0.015); const earthData = createPlanet(1, 'earth.jpg', 15, 0.01); const marsData = createPlanet(0.7, 'mars.jpg', 20, 0.008); const jupiterData = createPlanet(3, 'jupiter.jpg', 30, 0.004); const saturnData = createPlanet(2.5, 'saturn.jpg', 40, 0.002); const uranusData = createPlanet(2, 'uranus.jpg', 50, 0.001); const neptuneData = createPlanet(1.9, 'neptune.jpg', 60, 0.0005); // --- 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); 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 = `
Mass: ${data.mass}
Radius: ${data.radius}
Orbital Speed: ${data.orbital_speed}
`; } } 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 // Animation loop function animate() { requestAnimationFrame(animate); // Update controls controls.update(); // Rotation sun.rotation.y += 0.001; const maxTrailPoints = 200; // Adjust for longer/shorter trails const worldPosition = new THREE.Vector3(); planets.forEach(p => { p.pivot.rotation.y += p.orbitSpeed; p.planet.rotation.y += 0.01; // Add self-rotation // 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); }); 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); });