File size: 3,766 Bytes
ebef549
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// /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;
    }
  }
}