megaman

Project 2: Modeler

Assigned: April 19
Due: May 3
Artifact: May 6

Help Sessions: April 22, 6pm; April 23, 5pm in Sieg 228

Project TA: Eugene (ehsu@cs)




Quick Links

  1. W2K Skeleton Code and example (zip)
  2. FLTK headers and library (zip)
  3. How to use and extend the modeler
  4. Sample executables

Project Description

In this project you define a 3D model and controls for moving it, and then display and operate the model.  The project skeleton contains the framework code for the UI as well as some functions for drawing primitives. You will specify your model in a separate source file that will be compiled and linked in with the existing code.

Project Objectives

In this project you will use OpenGL to create and animate a character of your own design. You will become familiar with 3D hierarchical modeling and transformations.

What is a Hierarchical Model?

A hierarchical model is a way of grouping together shapes and attributes to form a complex object. Parts of the object are defined in relationship to each other as opposed to their position in some absolute coordinate system. Think of each object as a tree, with nodes decreasing in complexity as you move from root to leaf. Each node can be treated as a single object, so that when you modify a node you end up modifying all its children together. Hierarchical modeling is a very common way to structure 3D scenes and objects, and is found in many other contexts.

Project Requirements

First of all, you must come up with a character. This character can be composed solely of primitive shapes (box, cylinder, sphere, and triangle) (code for drawing these primitives is provided in the skeleton). It should use at least ten primitives and at least four levels of hierarchy. Use glTranslate(), glRotate() and glScale() calls to place these primitives in space. You must also use glPushMatrix() and glPopMatrix() to nest your matrix transformations. The modeler skeleton provides an easy way to create sliders and fetch their values for use in your model.

Additionally, one of your sliders should function as an animation control. As you scrub the slider back and forth, your character should perform some simple animation. This slider should control multiple joints or components of your model. For example, you might have your character waving, which would require that you control the shoulder and elbow joints.

In the Camera class, there is a function called applyViewingTransform() that set up the transformation for placing and orienting the camera. Currently, it is implemented using the gluLookAt() function. Your job is to implement this function yourself. You may choose to do this using glTranslate()s and glRotate()s, or alternately you can build the matrix yourself and use glMultMatrix() to apply the transformation. applyViewingTransform() takes three parameters. The documentation of the gluLookAt() describes its functionality as follows:

gluLookAt creates a viewing matrix derived from an eye point, a reference point indicating the center of the scene, and an up vector. The matrix maps the reference point to the negative z axis and the eye point to the origin, so that, when a typical projection matrix is used, the center of the scene maps to the center of the viewport. Similarly, the direction described by the up vector projected onto the viewing plane is mapped to the positive y axis so that it points upward in the viewport. The up vector must not be parallel to the line of sight from the eye to the reference point.

Finally, you are required to complete two bells (or equivalent) from the list below. Feel free to come up with your own ideas and pass them by us. If they're cool, we'll count them as bells and whistles.

Other than these requirements, you have complete artistic freedom on this, so be creative!

WARNING: We strongly discourage editing the modelerapp and modelerdraw classes. For the Animator project, you will be re-using your new model source file and plugging it into a different application. Thus, if your model library depends on changes or additions you make to the modelerapp or modelerdraw classes, it may not be compatible with the Animator skeleton application. You should be able to implement almost all of the bells and whistles inside your new model source file. Some changes can safely be made to the modelerview class for adding extra camera control functionality; the appropriate source files are documented to indicate where your code should be added.

Bells and Whistles

One bell is worth two whistles.

[whistle] Change the default light source to illuminate your scene more dramatically. It should be controllable by a slider (perhaps by changing its position or color).

[whistle] Come up with another whistle and implement it.  A whistle is something that extends the use of one of the things you are already doing.  It is part of the basic model construction, but extended or cloned and modified in an interesting way.

[bell] Use a texture map on all or part of your character. (The safest way to do this is to implement your own primitives inside your model file that do texture mapping.)

[bell] Build a complex shape as a set of polygonal faces, using triangles (either the provided primitive or straight OpenGL triangles) to render it. Examples of things that don't count as complex: a pentagon, a square, a circle. Examples of what does count: dodecahedron, 2D function plot (z = sin(x2 + y)), etc.

[bell] Make an additional "animated" sequence your character can perform. Although you can try to use a timed callback, an easier solution is just to increment values each time your model's draw() function is called. If you use the menu option to turn on animation, your draw() function will be executed at around 30 times per second.

[bell+whistle] Add some widgets that control adjustable parameters to your model so that you can create individual-looking instances of your character.  Try to make these actually different individuals, not just "the red guy" and "the blue guy."

[bell+whistle]The camera code has a constrained up vector -- modify the code to (1) handle camera twists (the framework is already there) and (2) ensure that controlling the camera remains intuitive. Note: in the past, we've seen many (incorrect) solutions that simply perform the twist without making the mouse control intuitive. The camera rotation control (left click) should always pivot the camera about an axis that is vertical with respect to the viewer.

[bell][bell] Add a function in your model file for drawing a new type of primitive. The following examples will definitely garner two bells; if you come up with your own primitive, you will be awarded one or two bells based on its coolness.

  1. Swept surfaces -- given two curves, sweep one profile curve along the path defined by the other. These are also known as "generalized cylinders" when the profile curve is closed. This isn't quite as simple as it may first sound, as it requires the profile curve to change its orientation as it sweeps over the path curve. See this page for some uses of generalized cylinders. This document may be helpful as well.
  2. Surfaces of rotation - given a curve and an axis, draw the surface that results from sweeping the curve around the axis. This is really nice for making pottery :).
  3. Rail surfaces - see Watt, p. 41.

[bell][bell] (Variable) Use some sort of procedural modeling (such as an L-system) to generate all or part of your character. Have parameters of the procedural modeler controllable by the user via control widgets.

[bell][bell] In addition to mood cycling, have your character react differently to UI controls depending on what mood they are in.  Again, there is some weight in this item because the character reactions are supposed to make sense in a story telling way.  Think about the mood that the character is in, think about the things that you might want the character to do, and then provide a means for expressing and controlling those actions.

[bell][bell][bell] One difficulty with hierarchical modeling using simple primitives, as you've been doing, is that it often results in very blocky shapes. Metaballs been used successfully to alleviate this issue. Build your model out of metaballs. For an additional bell, the placement of the metaballs should depend on some sort of interactically controllable hierarchy. Try out a demo application.

[bell][bell][bell][bell] If you have a sufficiently complex model, you'll soon realize what a pain it is to have to play with all the sliders to pose your character correctly. Implement a method of adjusting the joint angles, etc., directly though the viewport. For instance, clicking on the shoulder of a human model might select it and activate a sphere around the joint. Click-dragging the sphere then should rotate the shoulder joint intuitively. For the elbow joint, however, a sphere would be quite unintuitive, as the elbow can only rotate about one axis. For ideas, you may want to play with the Maya 3D modeling/animation package, which is installed on the workstations in 228. Credit depends on quality of implementation.

[bell][bell][bell][bell] Another method to build organic shapes is subdivision surfaces. Implement these for use in your model. You may want to visit this to get some starter code.

Disclaimer: please consult the course staff before spending any serious time on these. They are quite difficult, and credit can vary depending on the quality of your method and implementation.

Inverse kinematics

The hierarchical model that you created is controlled by forward kinematics; that is, the positions of the parts vary as a function of joint angles. More mathematically stated, the positions of the joints are computed as a function of the degrees of freedom (these DOFs are most often rotations). The problem is inverse kinematics is to determine the DOFs of a model to satisfy a set of positional constraints, subject to the DOF constraints of the model (a knee on a human model, for instance, should not bend backwards).

This is a significantly harder problem than forward kinematics. Aside from the complicated math involved, many inverse kinematics problems do unique solutions. Imagine a human model, with the feet constrained to the ground. Now we wish to place the hand, say, about five feet off the ground. We need to figure out the value of every joint angle in the body to achieve the desired pose. Clearly, there are an infinite number of solutions. Which one is "best"?

Now imagine that we wish to place the hand 15 feet off the ground. It's fairly unlikely that a realistic human model can do this with its feet still planted on the ground. But inverse kinematics must provide a good solution anyway. How is a good solution defined?

Your solver should be fully general and not rely on your specific model (although you can assume that the degrees of freedom are all rotational). Additionally, you should modify your user interface to allow interactive control of your model though the inverse kinematics solver. The solver should run quickly enough to respond to mouse movement.

If you're interested in implementing this, you will probably want to consult the CSE558 lecture notes.

View-dependent adaptive polygon meshes

The primitives that you are using in your model are all built from simple two dimensional polygons. That's how most everything is handled in the OpenGL graphics world. Everything ends up getting reduced to triangles.

Building a highly detailed polygonal model often requires millions of triangles. This can be a huge burden on the graphics hardware. One approach to alleviating this problem is to draw the model using varying levels of detail. In the modeler application, this can be done by specifying the quality (poor, low, medium, high). This unfortunately is a fairly hacky solution to a more general problem.

First, implement a method for controlling the level of detail of an arbitrary polygonal model. You will probably want to devise some way of representing the model in a file. Ideally, you should not need to load the entire file into memory if you're drawing a low-detail representation.

Now the question arises: how much detail do we need to make a visually nice image? This depends on a lot of factors. Farther objects can be drawn with fewer polygons, since they're smaller on screen. See Hugues Hoppe's work on View-dependent refinement of progressive meshes for some cool demos of this. Implement this or a similar method, making sure that your user interface supplies enough information to demonstrates the benefit of using your method. There are many other criteria to consider that you may want to use, such as lighting and shading (dark objects require less detail than light ones; objects with matte finishes require less detail than shiny objects).

Hierarchical models from polygon meshes

Many 3D models come in the form of static polygon meshes. That is, all the geometry is there, but there is no inherent hierarchy. These models may come from various sources, for instance 3D scans. Implement a system to easily give the model some sort of hierarchical structure. This may be through the user interface, or perhaps by fitting an model with a known hierarchical structure to the polygon mesh (see this for one way you might do this). If you choose to have a manual user interface, it should be very intuitive.

Through your implementation, you should be able to specify how the deformations at the joints should be done. On a model of a human, for instance, a bending elbow should result in the appropriate deformation of the mesh around the elbow (and, if you're really ambitious, some bulging in the biceps).