# Assignment 2 - Hierarchical Transforms

Due Wednesday 2/12 at 11:59 pm. You must work individually.

## Goal

Learn and apply hierarchical 3D transformations using the matrix stack.

## Associated Labs

• Lab 0 (required): Setting Up Your Development Environment
• Lab 3 (optional, tedious!): Transformation Matrices
• Lab 4 (recommended): Matrix Stack

## Overview

Write a program that allows you to create a robot that you can manipulate with the keyboard. You are free to create your own robotic character, but there should be at least 10 components in the robot, and the hierarchy should not be flat. For example, in the figures below, we have the following hierarchy:

• Torso
• Upper left arm
• Lower left arm
• Upper right arm
• Lower right arm
• Upper left leg
• Lower left leg
• Upper right leg
• Lower right leg

When a parent component is transformed, all of its descendants should be transformed appropriately. The keyboard control should be as follows:

• ‘.’ (period): traverse the hierarchy forward
• ‘,’ (comma): traverse the hierarchy backward
• ‘x’, ‘X’: increment/decrement x angle
• ‘y’, ‘Y’: increment/decrement y angle
• ‘z’, ‘Z’: increment/decrement z angle

By pressing the period and comma keys, you should be able to select different components in the hierarchy. You must draw the selected component so that it is distinguishable from unselected components. The x/X, y/Y, and z/Z keys should change the rotation angle of the selected component. In the left figure above, the torso is the selected component, and in the right figure, one of the lower legs is the selected component. Here, the selected component is drawn darker than the unselected components, but you can color it the way you want or draw it slightly bigger.

## Step 1

Start from your Lab 0 code base.

1. Create your `A2` project folder and copy the `L00` files and folders into it.
2. Modify `CMakeLists.txt` to change the project name (line 4).
3. Add GLM calls so that you can draw transformed squares. (If you’re not sure how to do this, you may want to start with Lab 4.) You should go through the various helper classes (e.g., Shape, Program, etc.) to understand roughly what they are doing.
4. Add support for keyboard input (x/X, y/Y, z/Z) by using the `glfwSetCharCallback()` function.

## Step 2

Create a class that represents a component. This class should contain the necessary member variables so that you can make a tree data structure out of these components. The root of the tree should represent the torso, which means that transforming the torso transforms everything else.

In addition to the member variables required for the tree hierarchy, the class should also have the following:

• A `vec3` representing the translation of the component’s joint with respect to the parent component’s joint.
• A `vec3` representing the current joint angles about the X, Y, and Z axes of the component’s joint. (You may want to start with Z-rotations only.)
• A `vec3` representing the translation of the component’s mesh with respect to its joint.
• A `vec3` representing the X, Y, and Z scaling factors for the mesh.
• A member method for drawing itself and its children.
• Any additional variable(s) and method(s) you see fit.

The drawing code should be recursive - in other words, in the `render()` function in `main.cpp`, there should be a single draw call on the root component, and all the other components should be drawn recursively from the root. In the main `render()` function, you should create an instance of the matrix stack class and pass it to the root component’s drawing function. Make sure to pass the matrix stack by reference or as a (smart) pointer.

The component’s rendering method should simply take the current state of the component and draw it. You should not create the robot hierarchy in this method. In other words, the scene setup must be done in main’s `init()` rather than in main’s `render()`. Whenever the keyboard is pressed, the `char_callback()` function is called. Update the robot’s joint angles inside this function.

For this assignment, the 3D rotation of the joint should be represented simply as a concatenation of three separate rotation matrices about the x-, y-, and z-axes: `Rx * Ry * Rz`. The position of the joint should not be at the center of the box. For example, the elbow joint should be positioned between the upper and lower arms.

You must draw the selected component differently from unselected components. For example, in the images above, the selected component is darker than the other components. You can either change the color (like above) or change the size.

• Changing the color: This requires you to slightly modify the vertex shader. Assuming you are starting with L4, the last line in the vertex shader is computing the attribute variable `vCol`, which is the vertex color. You will have to modify this line so that the output color becomes different. You will need to pass in a uniform variable so that you can tell if the component you are currently drawing is selected or not.
• Changing the size: This requires you to modify the modelview matrix depending on whether the current component is selected or not.

The traversal of the tree with the period and comma keys should be in depth-first or breadth-first order. Do not hardcode this traversal order - your code should be set up so that it works with any tree.

## HINT: Debugging OpenGL & GLSL

• Set the Program class to be verbose by calling the `setVerbose()` function. If there is a GLSL compilation error, then you will see the error in the console. For example, if the varying variables of the vertex shader and the fragment shaders do not match up, it will tell you so. Make sure to set verbose to be false after debugging.

• Use `GLSL::checkError(GET_FILE_LINE);` to find which OpenGL call caused an error. This function will assert if there were any OpenGL errors before getting to this line. You can use this to winnow down which OpenGL function is causing an error. For example, if you put this line at the top, the middle, and the bottom of your function, and if the assertion happens in the middle, you know that the error must be happening in the top half of your function. Once find exactly which OpenGL call is causing the error, you can Google the OpenGL function to figure out what caused the error. For example, maybe one of the arguments should not have been zero or null.

• The GLSL compiler will silently optimize away any variables that are not used in the shader. If you try to access these variables at runtime, the program will crash, since these variables no longer exist in the shader. In this lab, when you move the computation of the normal to the GPU, the `aNor` variable no longer needs to be passed to the GPU, since it is computed in the shader. Therefore, you will have to comment out any reference to `aNor` from your C++ runtime code. Or, you can trick the GLSL compiler from optimizing away `aNor` by using it and disgarding it as follows:

``````  vec3 nor = aNor.xyz;
nor.x = ...;
nor.y = ...;
nor.z = ...;``````

## Bonus

• Put a sphere (sphere2.obj) at each joint.
• Animate a running/walking/etc. model by bending some or all of the joints with time, using `glfwGetTime()`. Animated joints do not need to be controlled with the keyboard.

## Point breakdown

• 20 points for proper use of `Program` and `Shape` classes.
• 55 points for a functioning hierarchical robot with recursive, object-oriented design.
• 15 points for being able to select different components with the keyboard and for showing the current selection with a different color or size.
• 10 points for coding style and general execution.
• 2.5 points bonus for spheres at the joints.
• 2.5 points bonus for animation.

Total: 105 points

## What to hand in

Failing to follow these points may decrease your “general execution” score. On Linux/Mac, make sure that your code compiles and runs by typing:

``````> mkdir build
> cd build
> cmake ..
> make
> ./A2 ../resources``````

If you’re on Windows, make sure that you can build your code using the same procedure as in Lab 0.

• Make sure the arguments are exactly as specified.
• Include a README file (ascii or PDF) that includes:
• Hand in `src/`, `resources/`, `CMakeLists.txt`, and your readme file. The resources folder should contain the obj files.
• Do not hand in the build directory, the executable, old save files `(*.~)`, or object files `(*.o)`.
• Create a single zip file of all the required files. The filename of this zip file should be `USERNAME.zip` (e.g., `sueda.zip`). The zip file should extract everything into a folder named `USERNAME/` (e.g. `sueda/`).
• When you unzip this file, it should extract `src/`, `resources/`, `CMakeLists.txt`, and your README file to the current directory.
• Use the standard `.zip` format (not `.gz`, `.7z`, `.rar`, etc.).