transforms.py
, submitted on Canvas.robot_scene.py
, submitted on Canvas.my_scene.py
, submitted on Canvas.scenes.pdf
, a file with the following, submitted on Canvas:
Note that it's important to always use exactly the specified filename when handing in work for a CS class. (Canvas may automatically append a number to your filename; that's okay.) For instructions on creating a screenshot file, see HW0.
In this assignment, you'll make two 3D scenes (one that I design, and one that you design) by constructing scenegraph that pose all the objects appropriately in the world frame.
Download all the starter code:
transforms.py
— Starter code for your implementations of the four elementary transformations.robot_scene.py
— Starter code for your first 3D scene: a robotic arm.my_scene.py
— Starter code for your own custom scene.scenegraph.py
— A module that defines a bunch of classes for scenegraphs. There's extensive documentation; read it to understand how to use it in your robot_scene.py, and try running it from the command line too._scenegraph_base.py
— Private base classes for the scenegraph stuff. You don't have to look at this at all.gfx_helper_mayavi.py
— Your old friend for 3D rendering.meshes.py
from the previous assignment, and modify it so that all the vertex arrays use homogeneous coordinates. You may also wait and use my HW3 solution file, which I'll post here with modifications for homogeneous coordinates and convenient orientations.our_shapes.py
, a module full of everybody's custom shape meshes.Fill in the body of each method in transforms.py
, so that your scene code can use it to pose objects in the scene. Make sure you test this thoroughly; if there are mistakes here, they'll be difficult to diagnose just from looking at rendered scenes!
If you need to perform any matrix multiplications in your code, remember that np.dot()
(or the .dot()
method of a numpy array) is the tool that does this — *
instead means a plain, elementwise multiplication!
When you rotate around a given vector, all the rotation happens within parallel planes that are orthogonal (normal) to that vector. For example, rotation about the Z axis is rotation strictly in the X–Y plane; the Z coordinate of each point is left unchanged. In turn, this means that rotations around the axes of our space have simpler matrices: they're really just 2-D rotations, and they look like the identity matrix in the row and column corresponding to the rotation axis. Here's the matrix for a rotation about Z by $\theta$ radians, for example:
$$ \begin{bmatrix} \cos(\theta) & -\sin(\theta) & 0 & 0 \\ \sin(\theta) & \cos(\theta) & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} $$
So it would be really nice to define our rotations as a composition of fundamental rotations around the X, Y, and Z axes of the space.
Euler angles are a set of parameterizations of rotations, using three angles. However, there's no one fixed, universal sense of what “Euler angles” are — there are many different ways (24 of them, in fact) to use three angles to express a single rotation. Somehow, the meaning of the three angles must be decided, either by the person telling them to you or by a predetermined convention. In this class we've fixed a convention, and specified it using the terms “yaw”, “pitch”, and “roll”: yaw goes around the object's Z axis, pitch around the “yawed” X axis, and roll around the “yawed-and-pitched” Y axis.
That sounds easy enough: we'd apply the rotations as $M_{roll} \cdot M_{pitch} \cdot M_{yaw}$, right? The trouble is that the matrices for pitch and roll, defined this way, would be about the transformed axes, which are not necessarily aligned to the space. These matrices would be really difficult to write out.
Instead, and maybe counterintuitively, we should apply the transformations in reverse order: $M_{yaw} \cdot M_{pitch} \cdot M_{roll}$. Think carefully about this: first, we roll around the Y axis, and then we pitch around the X axis (which transforms the Y axis that we already rolled around). Finally, we yaw around the Z axis, which transforms both the Y and X axes that we already transformed around. Done this way, each of the three matrices may be defined around a principal axis of the space on which it acts, and so twelve of its entries are just 0 or 1.
(Compare this discussion of the order of application to the similar choice of whether to rotate or translate first, when making one object “orbit” around the origin. The correct approach is $M_r \cdot M_t$: first translate [out along the X axis, for example], then rotate [around the origin of the whole space]. Another way of looking at it: we're rotating the coordinate frame, and then translating within that rotated coordinate frame along the rotated X axis.)
As a final note: the rotation angles are taken to be “right-handed” rotations around each axis: a small positive angle corresponds to rotation counterclockwise when looking straight down the given axis. (So a rotation with no yaw and no pitch, but a small positive roll, would cause the X axis to move downward along -Z, and the Z axis to move in the +X direction.) When constructing your rotation matrices, think carefully about what the labels and orientations are for those parallel planes. For example, when you look down the X axis, the thing pointing to the right is the positive-Y axis, and the thing pointing up is the positive-Z axis. What do you get when you look down the Y axis? What about the Z axis?
The first scene you'll build is a near-duplicate of the one we made in class: a robotic arm. Here are the specifications:
Follow these steps, most of which are documented in TODO comments in robot_scene.py:
makeScenegraph()
. It may be helpful to look at the test function in scenegraph.py.makeScenegraph()
with argument values of your choice.getCompositeTransforms()
).(verts, tris)
2-tuples) that are the result of applying each transform to each corresponding shape. Be careful not to modify the original ShapeNode
objects. Again, look in scenegraph.py for guidance.Design your own scene. Here are the requirements:
our_meshes.py
).If your scene involves rotating an object about its center point, make sure you use a shape other than a sphere (and that you rotate tori and cylinders around axes other than their axis of symmetry). Otherwise, the action of your rotation will not be apparent in the scene.
Write out a paragraph description of your scene, and add it to your PDF. Sketching out your scene will probably help you think through the spatial relationships; if you'd like, you may augment your paragraph description with a copy of a sketch or two.
Design the scenegraph, draw it out on paper or a chalkboard, take a picture, and add that to your PDF.
my_scene.py
, flesh out makeScenegraph()
. This includes the arguments — at least three of the transforms must be controlled by arguments to this function. Write a good docstring, and fill in the body. This function should return the root node of the scenegraph.main()
, make an appropriate call to makeScenegraph()
.This assignment is worth 15 points.
Criterion | Points |
---|---|
scenes.pdf shows all drawings and screenshots. | 1 |
Each program runs from command line, displaying the scene in a window that stays open. | 1 |
scale() , shear() , and translate() return correct values. | 3 |
rotate() returns correct values. | 2 |
Both scenes: All transforms are encoded by the scenegraph. | 1 |
Both scenes: All functions are named correctly and have complete docstrings. | 1 |
Robot: makeScenegraph() produces correct output for several combinations of arguments. | 2 |
Robot: Instance transforms are applied correctly for all shapes in the scene. | 1 |
Your scene: makeScenegraph() produces correct output for several combinations of arguments. | 2 |
Your scene: Instance transforms are applied correctly for all shapes in the scene. | 1 |
Total | 15 |