CS 488/688: Introduction to Computer Graphics

Spring 2022 Assignment 3: Puppet


Summary

In this assignment you will create a hierarchical, articulated 3D humanoid model, which we'll call a "puppet". By "articulated" we mean that the puppet has a number of hinge-like joints whose angles can be controlled interactively, for example bending an arm at the elbow. By "hierarchical" we mean that the puppet is encoded in a hierarchical data structure that represents parent/child relationships between body parts; for example, when the elbow bends, the hand and fingers stay attached to the lower arm and rotate with it. The whole puppet will be constructed from transformed instances of simple geometric primitives, and will be lit interactively, using a combination of lights and materials that make the 3D structure clear and comprehensible. You must also support interactive undo/redo of joint manipulations.

The on-screen puppet is the visualization of a many-layered tree data structure. The "skeleton" of the tree represents the functional, hierarchical relationships between the body parts in the puppet. Thus, the root node is tied to the torso of the puppet, and gives the model-to-world transform of the puppet as a whole. The root has two children as part of this skeleton: a subtree anchored at the shoulders, containing further subtrees for the arms and head, and another anchored at the hips, containing subtrees for the legs. The skeleton might be visualized loosely as follows:

The actual tree you construct will have many more internal nodes. For example, you'll likely want to include intermediate transformation nodes that represent the geometric relationships between body parts and their parents (for example, that the lower arm has its centre of rotation at the elbow, and is positioned at the end of the upper arm). In addition, you'll naturally want to associate geometry with these abstract body parts! Most of these nodes are likely to have subtrees coming off of them with rigid arrangements of transformed primitives, giving actual form to the puppet. (We provide you with a small class hierarchy to implement the required tree nodes, consisting of a SceneNode base class with its children JointNode and GeometryNode; those classes can be used in lots of ways inconsistent with the description here, but we recommend you stick to our suggested approach.)

Your final puppet might look something like this:

The figure on the left shows the full overlapping outlines of scaled spheres; the one on the right has hidden surfaces removed. You are welcome—indeed encouraged—to improve upon this basic structure to create your own interesting puppet. Feel free to add more geometry and more skeletal structure (and hence, more points of articulation). You can add fancier joint types, new geometric primitives, custom materials and lighting, and just about anything else you like (the provided shaders do Gouraud shading with a diffuse lighting model). Have a look at the online CS 488 gallery for past examples of wild puppets. Just make sure your puppet fulfills a few minimal requirements:

Modelling

The structure of the puppet is not contained in the source code of the application, but in an external file that is interpreted when the program starts. Rather than creating our own file format for describing puppets, we introduce Lua as a scripting language. We're already using Lua (as part of premake4) to create Makefiles, and the language has been used extensively in games. Note the potential benefit here: because the puppet is defined procedurally, you can take advantage of standard programming abstractions to streamline your design (for example, think about writing a function to generate an arm, and then calling it twice). We will continue to use Lua to describe scenes in Assignment 4.

To begin constructing a puppet, we need a root for our modelling hierarchy. We can get one by asking for a transform node and assigning the result to a Lua variable. The name passed to the function is useful for debugging purposes.

myroot = gr.node('root')

Geometry nodes hold and can render mesh data. In order to create a geometry node, you pass in the ID of the mesh you want to use, and a node name as above. Then you need to connect it to a parent node:

torso = gr.mesh('sphere', 'my_torso')
myroot:add_child(torso)

Note the use of a colon above. In Lua, ":" is used to call a member functions on an instance and "." is reserved to call regular functions or static class functions. We might then set the material properties of and transform the torso:

torso:set_material(gr.material(...))
torso:translate(1.0, 2.0, 3.0)

Finally, we simply return the root node of the puppet:

return myroot

Conceptually, the transformation at a node is applied to both its geometry and its children, and matrices deeper in the tree are premultiplied by matrices higher in the tree.

As mentioned earlier, the tree shown above shows only the functional relationships between puppet components. Your tree will need to have additional joint nodes, containing transformations and joint rotation ranges. You will transform these joint nodes to manipulate the puppet. You may also need extra nodes to prevent child nodes from being affected by transformations meant to modify only the geometry of a parent node. Watch out, in particular, for scales. You generally will not want these in the middle of a chain of transformations.

We define the following set of modelling functions for you, as the tools we expect you'll need to complete the assignment:

We have created the "glue code" for you (i.e., the code that defines these Lua functions and makes them available in your puppet script), and bound these to functions and methods within the scene graph. You may also add Lua bindings to the assignment if you want to support other new features, but make sure the functions described above continue to work as written.

As an intermediate milestone, you can test your implementation with the provided file a3mark.lua, which should render to produce the image a3mark.png. We will use this script to test some of the functionality of your assignment ourselves.

The interface

As always, your application should include a GUI panel implemented in ImGui, supporting at least the following features. Most features have associated keyboard shortcuts indicated in parentheses. You may, but are not required to include these shortcuts in the actual names of the items as they appear in the GUI.

Note that in the assignment code, the M key will show and hide the GUI panel entirely. It can be useful to hide the GUI panel for saving glamour shots of your puppet.

Donated code

The code provided at the start of the term contains everything you need to get started with Assignment 3, including a standardized installation of the Lua scripting language in the code/ folder.

In the A3/ folder, you'll find what should be a familiar layout of files by this point in the course. You shouldn't need to make any changes to the startup code (main.cpp) or the Lua glue code (lua488.hpp, scene_lua.hpp, and scene_lua.cpp), but you're likely to end up touching the other source files. Of course, you may wish to change other files if you extend the basic application. That's fine, as long as the program works as expected on our sample input. If you're unsure whether a change is acceptable, feel free to ask on Piazza or by email.

The A3 executable requires a command-line argument as input, specifying the filename of a puppet Lua file. The Assets/ directory includes several sample files, including a3mark.lua, which we'll use as part of marking.

Deliverables

Prepare and upload a ZIP file of the A3/ directory, omitting unnecessary files (executables, build files, etc.). Make sure to include a puppet.lua file the A3/Assets directory, which contains the model you are officially submitting. (You can include other Lua files if you want.) In your directory, include a README file and a screenshot (one that includes the GUI panel). If you omit either of these files, you will receive a deduction. An assignment without a screenshot will not qualify for bonus marks.

In your README, please include two additional bits of documentation:

Objective list

Every assignment includes a list of objectives. Your mark in the assignment will be based primarily on the achievement of these objectives, with possible deductions outside the list if necessary.

Assignment 3 objectives