Page 4: Primitive Objects and Basic Transformations
CS559 Spring 2023 Sample Solution
Objects
Objects in THREE are subclasses of Object3D. This same base class is used for all the things we put into the world: lights, camera, the actual “things” we see.
The nice part of this is that all objects get the same common functionality.
The main kind of “thing” we’ll put in the world is a Mesh. A Mesh basically is a collection of triangles (in the form of a Geometry
) combined with a Material
(that tells THREE how those triangles should look) and other information required to place them in the scene, such as transformations and hierarchy information.
To make a Mesh, we first need to create a Geometry
object, and then we can create a Mesh
by pairing it with a Material
. THREE has many different kinds of Geometry
objects built in (such as Boxes, Circles, Cones, Spheres, …); see the Documentation for the full list. We can also make our own Geometry by defining the triangles in the correct way. We’ll do that later in the class.
Box 1: Transformations
Objects in 3D have associated transformations as part of the Object3D
class. You can read about it here.
Each object has its own transformation. The transformation is between the coordinate system of the object’s parent and the coordinate system of the object itself (so everything is relative). If we place the object in the world (by adding it directly to the Scene
), the transformation is relative to the world coordinate system.
Each object has a matrix inside of it that stores its transformation. We can access that matrix. However, we usually don’t change the matrix directly: the matrix is computed as needed from the 3 transformations applied to the object: Translate, Scale, and Rotate.
One slightly confusing thing: objects have both “transformation” functions (that apply transformations to the current transformation) as well as the ability to set the position and/or rotation to a particular value. If you use a translate
command, the position gets updated - and the way it is updated is affected by the current rotation. Or, you can just set the position to a particular value.
The way to think of this is to differentiate “transformation” (something that changes something) from state (where something is). So, a transformation modifies the state.
06-04-01
In this example in
06-04-01.js (
06-04-01.html), the purple cube is first rotated by 45 degrees, and then its position is set (to have X = 2). The position given is the position the object gets. The red cube is also rotated 45 degrees, and then undergoes a translateX
which causes it to be moved by 2 units along the X axis. But since the X axis has been rotated, the motion is along the direction of the rotated X axis. The translateX
function (and its counterparts) all translate in the current coordinate system of the object.
The ability to either set positions and orientations or apply translations and rotations gives a lot of flexibility. Sometimes it’s easier to describe how you want the object to move, other times it’s easier to specify where you want it to be. Sometimes its confusing because THREE interprets each one in a different manner. To make things even more complicated, THREE is very flexible in how it handles transformations, allowing them to be specified in different ways and converting them between formats automatically. This can be convenient.
We’ll come back to explore transformations in 3D extensively in a later workbook.
Box 2: Loading Objects
If we want an object that isn’t built in, THREE gives us the ability to load it from a file.
A common file format for 3D models (as collections of triangles) is the obj
file format. The THREE loader is part of their “examples” - we include it in the parts of THREE we provide for class, but the documentation isn’t very complete.
The trick with loading an object is that it may take time. So when we start the loader, we also provide a function that gets called when loading is finished. This function needs to set the object up (put it into the scene, position and scale it, …). Here’s an example in 06-04-02.js ( 06-04-02.html):
06-04-02
The relevant code:
|
|
Notice that this code creates a loader, and then asks the loader to load the file ./objects/07-astronaut.obj
. In this call it passes another (anonymous) function that will be called when loading is complete. This function is passed the loaded object. Note how the function positions the object (setting its position), scales it, and places it into the scene. We didn’t have to rotate the object since it was already oriented the right way. Also, notice that we have to call render
to draw the object after the object is loaded. If render
is being called continuously (for example, in an animation loop), that can take care of making sure drawing happens after the object is loaded.
One other tricky thing: notice that the astronaut
variable is a local variable. If you want to access the astronaut after it’s loaded (for example, to animate it), you need to have some way to access it from elsewhere in your program. For example, you might want to store it in a global variable. Be careful: remember that your code continues to run while the object is getting loaded.
Box 3: Exercise 1, Stacking Boxes
In 06-04-03.js ( 06-04-03.html), there is a scene that has 5 boxes. Change the positions of the boxes so they are a stack (with the smaller boxes on top of the bigger boxes). Pay attention to both the initial sizes of the boxes and their scaling. The result should be a stack of 5 boxes on top of the ground (each box on top of the next larger box).
Now that we have objects, let’s discuss the cameras and lights on Page 5 (Lights, Camera, Action!).