NasaMarsApp / scene.js
sd4m's picture
Create scene.js
ebef549 verified
// /src/three/scene.js
import * as THREE from 'three';
export class MarsScene {
constructor(containerEl, options = {}) {
this.container = containerEl;
this.width = containerEl.clientWidth;
this.height = containerEl.clientHeight;
this.scene = new THREE.Scene();
this.camera = new THREE.PerspectiveCamera(45, this.width / this.height, 0.1, 100);
this.camera.position.set(0, 0, 2.5);
this.renderer = new THREE.WebGLRenderer({ antialias: true });
this.renderer.setSize(this.width, this.height);
this.container.appendChild(this.renderer.domElement);
this._setupLights();
this._loadMarsTexture(options.marsUrl).then(texture => {
this._makeMars(texture);
}).catch(err => {
console.error('Error cargando textura Marte', err);
// fallback: color material
const mat = new THREE.MeshStandardMaterial({ color: 0x884400 });
this._makeMars(mat);
});
this._makeStarfield(1000);
this.rotationSpeed = 0.01; // rad/s
window.addEventListener('resize', () => this._onResize());
this.lastTime = performance.now();
this._animate();
}
_setupLights() {
const ambient = new THREE.AmbientLight(0xffffff, 0.5);
this.scene.add(ambient);
const dir = new THREE.DirectionalLight(0xffffff, 0.8);
dir.position.set(5, 5, 5);
this.scene.add(dir);
}
async _loadMarsTexture(url) {
const loader = new THREE.TextureLoader();
return new Promise((resolve, reject) => {
loader.load(
url,
tex => {
tex.encoding = THREE.sRGBEncoding;
resolve(tex);
},
undefined,
err => {
reject(err);
}
);
});
}
_makeMars(textureOrMaterial) {
let mat;
if (textureOrMaterial instanceof THREE.Texture) {
mat = new THREE.MeshStandardMaterial({ map: textureOrMaterial });
} else {
mat = textureOrMaterial;
}
const geo = new THREE.SphereGeometry(1, 64, 64);
this.marsMesh = new THREE.Mesh(geo, mat);
this.scene.add(this.marsMesh);
}
_makeStarfield(count) {
const geom = new THREE.BufferGeometry();
const positions = new Float32Array(count * 3);
for (let i = 0; i < count; i++) {
const r = THREE.MathUtils.randFloat(5, 20);
const theta = THREE.MathUtils.randFloatSpread(360);
const phi = THREE.MathUtils.randFloatSpread(360);
const x = r * Math.sin(theta) * Math.cos(phi);
const y = r * Math.sin(theta) * Math.sin(phi);
const z = r * Math.cos(theta);
positions[3*i] = x;
positions[3*i+1] = y;
positions[3*i+2] = z;
}
geom.setAttribute('position', new THREE.BufferAttribute(positions, 3));
const mat = new THREE.PointsMaterial({ color: 0xffffff, size: 0.02 });
const stars = new THREE.Points(geom, mat);
this.scene.add(stars);
}
_onResize() {
this.width = this.container.clientWidth;
this.height = this.container.clientHeight;
this.camera.aspect = this.width / this.height;
this.camera.updateProjectionMatrix();
this.renderer.setSize(this.width, this.height);
}
_animate() {
requestAnimationFrame(() => this._animate());
const now = performance.now();
const dt = (now - this.lastTime) / 1000;
this.lastTime = now;
if (this.marsMesh) {
this.marsMesh.rotation.y += this.rotationSpeed * dt;
}
this.renderer.render(this.scene, this camera);
}
// Exponer método para “limpiar” (quizás animar o cambiar material)
showCleanState() {
if (this.marsMesh && this.marsMesh.material) {
// un shader o cambio leve: por ejemplo emitir luz o brillo
this.marsMesh.material.emissive = new THREE.Color(0x003300);
this.marsMesh.material.emissiveIntensity = 0.2;
}
}
}