Skip to main content
← 3D on the Web

Lesson 5 of 5

A World From An Array

Six planets orbiting a sun, none of them written by hand. Drag to look around. Click a planet to name it.

What you will learn

  • building objects in a loop
  • Group
  • parenting
  • userData
  • Raycaster
  • pointer events
  • orbiting a camera

Before you start

Light and Material, and the loops from the JavaScript course. This lesson is really a loop lesson that happens to be in 3D.

What this page does

  1. Six planets orbit a sun, each at its own distance and its own speed.
  2. Drag anywhere on the canvas to move the camera around the system.
  3. Click a planet. The line underneath names it and says how far away it is.

The data

var PLANETS = [
    { name: "Mercury", distance: 2.0, size: 0.16, speed: 1.60, colour: "#b0a89a" },
    { name: "Venus",   distance: 2.7, size: 0.28, speed: 1.18, colour: "#e8b06a" },
    { name: "Earth",   distance: 3.5, size: 0.30, speed: 1.00, colour: "#4f9de8" },
    ...
];

This is the same move as the times table in the JavaScript course. The data lives in one place and a loop turns it into output. It did not change when the output stopped being table cells and became planets. That is worth stopping on, because it is the most transferable idea in this course.

Orbits with no maths

There is no Math.cos anywhere near an orbit in this file. There is a trick instead, and it is one of the most useful things in all of 3D.

pivot  (an empty group, at the centre)
  └── planet  (moved out along x)
var pivot = new THREE.Group();
scene.add(pivot);

var planet = new THREE.Mesh(geometry, material);
planet.position.x = facts.distance;
pivot.add(planet);

The planet is a child of an empty group sitting at the centre. A child's position is measured from its parent, so turning the parent carries the child around with it in a circle. Turning the pivot is the whole orbit.

This is behind nearly every mechanical thing in 3D. Wheels on a car, an arm on a robot, a moon around a planet. Make the moon a child of the planet and it follows the planet around the sun for free, without a single line about where the planet is.

Run the system

Set the time to 0 before you try clicking a planet. A moving target is a harder target.

1

0 stops the whole system, which makes a planet much easier to click.

1.1

Clicking a thing in 3D

ray.setFromCamera(pointer, camera);
var hits = ray.intersectObjects(meshes);

if (hits.length > 0) {
    report.textContent = hits[0].object.userData.name;
}

A raycaster fires an imaginary line from the camera through the point you clicked, and reports everything it passes through, nearest first. It is how almost every 3D program answers the question what is under the mouse.

  • The click has to be converted from pixels into the -1 to 1 coordinates from lesson 1. It is the same coordinate system, still.
  • hits[0] is the nearest one. Behind it there may be a planet on the far side of the sun.

userData

planet.userData.name = facts.name;

Anything you put on userData travels with the object. When the raycaster hands a mesh back, the name is already attached to it. Without that, you would be searching an array to work out which of six planets you had been given.

Moving the camera

camera.position.x = Math.sin(turn) * Math.cos(tilt) * range;
camera.position.y = Math.sin(tilt) * range;
camera.position.z = Math.cos(turn) * Math.cos(tilt) * range;
camera.lookAt(0, 0, 0);

The camera always points at the middle, so moving it is only a matter of choosing an angle to sit at. Two angles and a distance is all it takes. The drag handler changes the two angles and calls this again.

three.js has a ready-made OrbitControls that does this and adds zoom, in a couple of lines. It is the natural next step. The reason to write it by hand once is that it is thirty lines, and now you know what those two lines would be doing.

Why the sun is Basic

The sun is the thing making the light. Shading it by light falling on it would be backwards, and it would end up with a dark side. MeshBasicMaterial ignores lighting, which is exactly right here, and it is the reason lesson 4 spent time on unlit materials.

The PointLight is at 0, 0, 0, which is already inside the sun. A point light sits at one spot and throws light in every direction, which is what a sun does.

Try these changes

Make these changes in the files beside this guide. Each one only uses what this lesson has already covered. Tick each one when it works.

Check yourself

Three quick questions on this lesson. Get them all right to finish the lesson and earn a pack.

Loading your answers

Teacher's versionHow to run this lesson, what to check, and any answers. Students do not need this.

How to teach it

  • Drag and click for a few minutes before opening any code.
  • Add a planet to the array as the very first code change, in front of the class. Nothing else is edited. That single moment is the lesson, and everything after it is detail.
  • Teach the pivot with two hands before any code: one hand is the pivot at the centre, the other is the planet held out at arm's length. Turn your body, not your arm.
  • The moon challenge is the one to push. A student who works out that the moon goes inside the planet has understood parenting properly.
  • Delete the moved < 6 check as a class and let the broken behaviour happen. It is a much better memory than a warning.
  • Say plainly that the distances are not to scale, and ask what the picture would look like if they were. It is a good question and the answer is that you would see almost nothing.