Thursday, January 17, 2013

Better Orbits with Three.js Frames of Reference

‹prev | My Chain | next›

Yesterday, I was able to pick back up with Three.js by coding up a simulation of the Moon travelling around the Earth. I eased myself back in by copying a simulation of the Earth passing Mars in their respective orbits. The result was a working solution, but perhaps not the best solution. Today I hope to see if I can do better.

In the Mars simulation, I positioned both Earth and Mars with absolute X & Y coordinates, with the Sun at (0,0). I accomplished this with simple sines and cosines primarily because I needed those absolute coordinates to calculate the orientation of the Earth camera pointing at Mars. Also, I like tormenting kids with trig in 3D Game Programming for Kids.

Anyhow, for the Moon simulator, I repeated the absolute X & Y coordinates for the Moon, though I offset them by the Earth's position to give the appearance of orbiting. It worked, but that's not an effective 3D programming technique. The proper way to do this is create a new earth-centric frame of reference for the Moon. In Three.js, this is done by creating an empty Object3D object and adding it to the Earth:
  var moon_orbit = new THREE.Object3D();
  earth.add(moon_orbit);
I then add the Moon to this frame of reference and move the Moon 100 units away from the center of this new frame of reference:
  moon_orbit.add(moon);
  moon.position.set(0, 100, 0);
The order of those two operations does not matter in Three.js, but it is easier to follow as shown.

It occurs to me that I can stick the earth-cam-looking-at-the-moon into this same frame of reference. When this frame of reference turns to give the Moon the semblance of an orbit, the camera will turn right along with it:
  moon_orbit.add(earth_cam);
  earth_cam.rotation.set(Math.PI/2, 0, 0);
Rotating this frame of reference is pretty simple. In my animate() function, I update the rotation around the Z axis (pointing out of the screen) a tiny bit on each call:
  function animate() {
    requestAnimationFrame(animate);
    renderer.render(scene, camera);

    if (pause) return;
    time = time + speed;
    var e_angle = time * 0.001;
    earth.position.set(350* Math.cos(e_angle), 350* Math.sin(e_angle), 0);

    var m_angle = time * 0.01;
    moon_orbit.rotation.set(0, 0, m_angle);
  }  
That is a darn sight easier to read than the sine and cosine for the Earth. It also much improves on setting the X & Y coordinates as I was doing yesterday:
    // Ew.
    moon.position.set(
      100* Math.cos(m_angle) + earth.position.x, 
      100* Math.sin(m_angle) + earth.position.y, 
      0
    );
Most importantly, this is another example that I can use to teach kids about frame of reference in the book.

Obviously, I can improve upon the Earth's orbit as well, but I may leave it as-is in the book to make the transition to or from the Mars simulator easier. That or I could leave it as a challenge for the more industrious readers.

The end result code and simulation are available in editable format. Once the code is hidden, the controls are: C (or Space) to toggle the camera, P to pause, and 1, 2, or 3 to change the speed of the simulation.

This stuff is starting to come back to me. I make pick up with an entirely new game tomorrow. Or I may add planet and satellite rotation to the mix.


Day #633

1 comment: