In this project you will implement a simple model viewer that uses a simplified X3D scene graph as well as some simple programmable shaders.
You are given support code for this project that should compile under Windows and Mac OSX (There is a Linux makefile in the archive but the CAEN Linux workstations do not have good graphics drivers, hence the programmable GPU shaders will not work there). Support code links against Glut and Expat libraries. It parses an input X3D file, partially constructs a scene graph inserting various X3D nodes into it, and then passes the scene to the Glut renderer. The X3D file format is described in the X3D specs. Most of your tasks will be modifying the following source files (look for words YOUR CODE HERE):
X3Cylinder::Render() function. 10ptsX3Transform::Render() function. 15ptsX3PointLight::SetupLights() and
X3Transform::SetupLights() functions. 15ptsX3Material::Render() function. 10ptsX3IndexedFaceSet::Render()
and X3IndexedFaceSet::Add(). 10ptsModify X3Cylinder::Render() function to render cylinders using all the provided data fields of the cylinder node (see X3DSpec 13.3.3 for details). Also, look at how the same function is implemented for the Cone primitive.
Our simple X3D viewer and parser do not have a camera class. All the viewing information is stored within the X3Viewpoint node. In the distributed support code, the camera is positioned at the location read from the Viewpoint position attribute (all the other attributes of Viewpoint node are ignored in our viewer). The camera is pointing towards the origin of the world coordinate system. The world coordinate system is set up to have Y-axis pointing upwards. The horizontal motion of the mouse (when left button is pressed) causes the camera to rotate around the Y-axis. You will need to implement zooming in and out (mapped to the vertical motion of when the right mouse button is pressed), as well as vertical camera motion (in such a way that the scene tilts, mapped to the vertical motion when the left mouse button is pressed). Keep the camera pointing towards the origin of the world coordinate system (optionally, you can also compute the center of the bounding box of the whole scene and point towards that point). You will need to modify the main.cpp file as well as the methods and fields of X3Viewpoint class.
The scene graph represents the scene as a directed acyclic graph (it is not a simple tree since some nodes can be reused multiple times using DEF/USE node attributes). The traversal of this graph is performed when the scene is rendered. The grouping nodes such as Scene, Group, and Transform traverse their children so that everybody gets to render itself. The Transform node adds the ability to specify the transformation between the child coordinate system and the parent coordinate system so that children can be specified with respect to their parent nodes. See X3D specs section 10.4.4 for more details -- the X3Transform class has all transforms except for the scaleOrientation transform which we skip.
In our viewer, we shall only implement point lights. All the point lights will have effects on the whole scene. Therefore, a separate pass is needed to setup light positions (and other parameters) before the scene is rendered. Each point light can be located within child nodes of the hierarchy and is then transformed by the current modeling transformation (it can thus be specified easily to be near some particular object). The recursive traversal of the lights is done by using SetupLight methods of the nodes. The grouping nodes SetupLight methods are very similar to their Render methods. You will need to modify the SetupLight for the X3Transform class to get proper transformations, as well as set up OpenGL lighting parameters within X3PointLight::SetupLights() method to match X3D specs section 17.2.2.4. In our simplified viewer, we have no fog, and no spot light factor. Be careful to match X3D lighting parameters onto OpenGL light and material parameters (note for instance that shininess in X3D gets multiplied by 128, and check the colors and intensities as well). If no lights are specified within the file a default light is added to the scene (see DisplayCallback function). Make sure to assign properties to the light with proper id = GL_LIGHT0 + light_index.
While working on the lights setup from the previous section, you will also need to setup material properties within X3Material::Render method. Note that lights are setup once before rendering the whole scene, while material will vary from node to node.
IndexedFaceSet represents a polygonal mesh node (see X3D specs section 13.3.6). It contains a list of vertex indices and a coordinate node that stores the XYZ point coordinate list into which the indices refer. Polygon index lists are separated by -1. Many models mostly use quads and triangles. Thus, after reading the index list we process it to extract two separate lists of quads and triangles (stored in the appropriate fields of the X3IndexedFaceSet class). In our simplified X3D handling we shall assume that only coordinates are given (no normals, no colors, so everything non-coordinate is skipped in the parser). However, we need normals in order to compute lighting. Therefore, you will need to fill in the vector of normals: it works as follows -- loop through each polygon (first triangles, then quads), and compute the normal for each polygon, then add that normal to the normals accumulated for each vertex of the polygon. After you went through all the polygons, normalize the normals for each vertex (thus you will average all the normals of the faces that contain each vertex).
You will also need to implement mesh rendering within the Render method of the X3IndexedFaceSet class. First draw triples of vertices in a loop for each triangle (specifying glNormal) before each vertex call and using GL_TRIANGLES rendering mode. Then, proceed to quads using GL_QUADS. (Look inside X3Box::Render for an example of what you need to do except that you will have a loop between glBegin and glEnd calls and you will have a normal specified before every vertex.) Once you are done with this task, you will be able to see the famous graphics teapot model.
The last two programming task will be done in a C-like language. First, you will need to implement per-pixel version of the lighting model. Most of the work will be done in the fragment shader phong.fs. The vertex shader (phong.vs) will prepare the data for it. The final color should be assigned to glFragColor.rgb vector in the fragment shader. In this task, you can assume that there are exactly two lights (0 and 1) enabled in the scene.
The "interesting" shader task is an open-ended task and you can implement any nice or weird looking shader. Examples of interesting shaders may include the brick procedural texture or some cartoonish looking textures (note that the provided interesting.vs/fs already implements some simple cartoon shading so you need to come up with something a bit more interesting).
First of all, download the support code archive
The executable should be called with one command-line argument -- the name of the X3D scene file (there are a bunch of them provided in the data folder, there are many on the web, and some graphics packages can export X3D).
The following keystrokes are defined in the glut window (you may
map more operations to keys inside KeyboardCallback
function but you should not change the predefined keys below).
The code needs to be linked against glut and Expat libraries, and the compilation requires the corresponding header files. The Mac OSX machines in the CSE building lab seem to have both installed. For the Windows version, you will need to copy common/bin/libexpat.dll to somewhere in your path. If you have a Linux machine with good drivers then you may use Makefile.linux file provided with the project archive.
The project files are for Visual Studio 2003, let Visual Studio 2005 convert it -- it should work fine. You can submit 2003 or 2005 projects. Copy libexpat.dll to somewhere on you path. Specify the X3D file name in the Debugging options, or run the program from a command line.
Download the file p2.tgz into some folder. Open console and go to that folder. Then type
Now this should bring up a glut window with a cube in it. Rotate it with the mouse, and check that if you press "i" it turns yellow. Then you are all set.
tar xvzf p2.tgz
cd p2/proj2
make
./proj2 ../data/dbox.x3d
Make sure your code compiles and works on Mac OSX machines in the downstairs CSE labe or on CAEN Windows machines. You will need to turn in your source files together with appropriate Makefile (and/or MSVC project files) and a write-up in text format (readme.txt) that discusses:
We'll post the detailed submission instructions in the very near future.
Do not submit any binary files (object, executable, or image) with your project.
The timestamp on your source files and your writeup will indicate the time of submission and if this is past the deadline your submission will be considered late. Therefore, you are allowed multiple 'submissions' as long as you respect the deadline. The submission folders will also be closed for writing right after the deadline.
Test the compilation: Your submission must compile without errors and warnings on supported platforms. Mention the platform in your writeup. Code that doesn't compile will be heavily penalized.