# Practical 3 - Elements of solution¶

This page provides elements of solution for the Hierarchical Modeling practical. The scene graph with the base and arm only is detailed. Addition of the forearm is left as exercise.

## What is this cylinder?¶

The `Cylinder`

class loads the `cylinder.obj`

mesh file. Peek at this file to see its structure. You will see for all vertices their coordinates (v), texture coordinates (vt), and normal (vn); followed by the triangles (f) indexes. Vertex coordinates reveal that:

the cylinder is aligned along the Y axis

its radius is 1

it height is 2 with the lower base on y = -1 and the upper one on y = +1. The cylinder is then centered on the origin.

Here are two views of the cylinder mesh at scale factor 1 and then 0.5, along with the world referential axis:

Note

The RGB axes (X in red, Y in green, Z in blue) are normalized, with a length of 1. Use the class `Axis`

with a flat color shader to add them in your scene.

## Step 0: base¶

A node is simply created with a scaling transform to have a flat cylinder; its single child is the original `Cylinder`

instance. A parent `RotationControlNode`

enable to turn the base around the Y axis.

```
cylinder = Cylinder(shader)
theta = 35.0 # base horizontal rotation angle
base_shape = Node(transform=scale(.5, .1, .5))
base_shape.add(cylinder)
transform_base = RotationControlNode(glfw.KEY_LEFT, glfw.KEY_RIGHT, (0, 1, 0), angle=theta)
transform_base.add(base_shape)
viewer.add(transform_base)
```

The images below show the base at each node, after scaling then after scaling and rotation (for clarity, the base referential is display with axes of length 1, not scaled). On the right, the base scene graph.

## Which transforms are applied, in which order?¶

In exercise 1 the model matrix of a node was first combined with the node’s transform, then passed to the node’s children.

```
class Node:
...
def draw(self, projection, view, model):
""" Recursive draw, passing down updated model matrix. """
self.world_transform = model @ self.transform
for child in self.children:
child.draw(model=self.world_transform, **other_uniforms)
```

For a given node:

`model`

is the pose of its**parent**in the world referential. For a hierarchy root node,`model`

is either the identity or the position of the whole object.`self.transform`

is the pose of the node**with respect to its parent**`self.children`

is the list of children, geometries or other nodes, which pose is defined**relatively to the current node**

The order of this transform combination is of most importance! The combination `model @ self.transform`

means that `self.transform`

is first apply to each child, and then `model`

is applied.

For the base cylinder above, the `model`

transform passed to the `transform_base`

is by default the identity. Thus, the total transform applied (in the vertex shader) to each vertex of the cylinder is:

```
// in pseudo-code GLSL code
gl_Position =
projection * view * identity * rotation((0, 1, 0), theta) * scale(.5, .1, .5) * vec4(position, 1);
```

Note

The order of transforms multiplication is fundamental. Two ways (at least) are possible in terms or reasoning: in terms of a *Grand, Fixed Coordinate System* or via a *Moving a Local Coordinate System*

This is well explained in the (old) first OpenGL book, a.k.a. “The red book”. Go to chapter 3 and search for “Thinking about Transformations”. Functions of the good-old fixed pipeline are not the same, but you will get the point.

## Step 1: arm¶

Now we can create the arm:

```
phi1 = 25.0 # arm angle
arm_shape = Node(transform=translate(0, .5, 0) @ scale(.1, .5, .1))
arm_shape.add(cylinder)
rotation_arm = RotationControlNode(glfw.KEY_PAGE_UP, glfw.KEY_PAGE_DOWN, (0, 0, 1), angle=phi1)
rotation_arm.add(arm_shape)
viewer.add(rotation_arm)
```

With a scale only, the cylinder is still centered on the origin. Thus, it is then translated along the Y axis (note that the translation length must include the scaling factor! Only 0.5 here, not 1).

The translation and scaling are here combined in the node’s transform, but you could also use two different nodes.

Finally, a parent

`RotationControlNode`

enables to incline the arm. The rotation is centered on the (parent) control node referential, not on the (child) cylinder referential.

The arm scene graph is:

## Step 2: final scene¶

If we simply add the two objects `transform_base`

and `rotation_arm`

in the viewer, there are two problems:

the arm is not correctly oriented (not affected by the base rotation of angle theta)

when the base is turned, the arm is not moved accordingly

Thus we just have to link the two objects, the arm being a child node of the base:

```
transform_base.add(rotation_arm)
viewer.add(transform_base) # only this root node is added to the viewer
```

Below are the final code and scene graph. Note that a single instance of `Cylinder`

is shared by the two branches.

```
cylinder = Cylinder(shader)
theta = 35.0 # base horizontal rotation angle
phi1 = 25.0 # arm angle
base_shape = Node(transform=scale(.5, .1, .5))
base_shape.add(cylinder)
arm_shape = Node(transform=translate(0, .5, 0) @ scale(.1, .5, .1))
arm_shape.add(cylinder)
rotation_arm = RotationControlNode(glfw.KEY_PAGE_UP, glfw.KEY_PAGE_DOWN, (0, 0, 1), angle=phi1)
rotation_arm.add(arm_shape)
transform_base = RotationControlNode(glfw.KEY_LEFT, glfw.KEY_RIGHT, (0, 1, 0), angle=theta)
transform_base.add(base_shape)
transform_base.add(rotation_arm)
viewer.add(transform_base) # only this root node is added to the viewer
```

Note

Now it’s you turn!

To make sure everything is understood, add a forearm to this scene!