Roger L. Kraft

CS 59000-03 - Programming Assignment 1

This assignment makes use of the files contained in this zip file and the files from the Java renderer. This assignment is due Tuesday, February 11.

This assignment is about the two ends of the 3D graphics rendering pipeline, what goes into the beginning of the pipeline and what comes out of the end of the pipeline. So there are two parts to this assignment.

Roughly, what goes into a graphics pipeline is a data structure (usually called a "Scene" data structure) that describes the geometry of what the renderer should draw. What comes out of the graphics pipeline is a data structure describing the two-dimensional pixel array that represents the final image drawn by the renderer. For the first part of this assignment, you will write a program that reads data from a simple OBJ file and builds the appropriate data structure that can be given to the renderer. For the second part of this assignment, you will write a program that manipulates the pixel array data structure that comes out of the renderer. (There are demo versions of each part of this assignment in the folder demo_programs.)

As we have said in class, a "scene" is mainly a collection of triangles in 3-dimensional space. The triangles combine to form the shapes that we see in the scene. A "scene data structure" is mainly a container for these triangles. An OBJ file is also a kind of container for a list of triangles. In the first part of this assignment you will write a program that reads the triangle descriptions stored in an OBJ file and instantiates Triangle objects that get stored in a Scene object.

First, let's look a bit at the structure of an OBJ file. For the purpose of this assignment, an OBJ file is a text file that contains three kinds of lines: lines that begin with the character 'v', lines that begin with the character 'f', and lines that begin with the character '#'. (In the zip file there is a sub folder called obj that contains several simple OBJ files. You should look at the contents of some of those files using a text editor.)

A line in an OBJ file that begins with '#' is a comment line and can be ignored.

A line in an OBJ file that begins with 'v' is a line that describes a vertex in 3-dimensional space. The 'v' will always be followed on the line by three doubles, the x, y, and z coordinates of the vertex.

A line in an OBJ file that begins with 'f' is a line that describes a "face", which for now will mean a triangle. The 'f' will be followed on the line by three positive integers. The integers are the indices of the three vertices that make up the triangle. The "index" of a vertex is the order in which the vertex was listed in the OBJ file. So a line like this

f  2  4  1

would represent a triangle made up of the 2nd vertex read from the file, the 4th vertex read from the file, and the 1st vertex read from the file.

As an quick exercise, draw a picture of the geometry represented by the following six lines in an OBJ file.

v  0.0  0.0  0.0
v -1.0  0.0  0.0
v  0.0  1.0  0.0
v  0.0  0.0  1.0
f  1  3  4
f  3  1  2

In the zip file there is a file Part_1_ver1.java that you need to complete. This program is supposed to read the OBJ file whose name is on the command line and build a Scene object containing Triangle objects that represent the data from the OBJ file. In Part_1_ver1.java there is an outline of what you need to do. You need to open the OBJ file and read it line by line (or, better yet, token by token). For every line that starts with a 'v' character, use the data on that line to build a Vertex object and store it in an ArrayList container. For every line that starts with a 'f' character, use the data on that line to build a Triangle object and store it in the Scene object.

The Scene, Triangle, and Vertex classes are all defined in the Scene sub folder of the Java renderer.

When you create a new Triangle object, give it a randomly chosen color (by giving all three vertices in the triangle the same randomly generated color). Giving each triangle a different color makes it easier to see the triangles in the final rendered image. (If all the triangles have the same color, the final image tends to just look like a flat blob.)

As you read through an OBJ file, you will need to "parse" the data in the file. An OBJ file has such a simple structure that parsing it is not difficult. The main tool to use is Java's Scanner class. In particular, look at the following methods.

Test your version of Part_1_ver1.java first on the simplest OBJ files, triangles.obj, triangleStrip.obj, tetrahedron.obj, and cubeT.obj. When those all work, try a slightly more complicated file, icosahedron.obj. When that works, try a really complicated file, horse.obj.

OBJ files can represent faces that are more complicated than just triangles. For example, use the OBJ file viewer viewobj.exe to see the geometry of the small_rhombicosidodecahedron.obj. You should see that it has faces that are triangles, squares, and pentagons. Now open the file small_rhombicosidodecahedron.obj in a text editor and look at the lines of text that define the faces. You should see that some faces have three vertices, some four, and some five.

In the zip file there is a file Part_1_ver2.java that you need to complete. This program should improve Part_1_ver1.java so that it can handle OBJ files that have non-triangular faces. But the Scene data structure can only contain triangles, it cannot store squares or pentagons. So whenever your program reads a face that is non-triangular, your program must "triangulate" the face. The easiest way to do that is to divide the face into a "triangle fan". Here is an image that gives you an idea of how to triangulate a six sided face. Notice how the first vertex of the face is used as a base point for a "fan" of triangles. You can do this with any face that has more than three sides.

triangle fan

When you build a triangle fan, give each triangle a randomly chosen color (as in Part_1_ver1.java). This makes it easier for you to see how the faces are cut up into triangles. (But it makes seeing the geometry of the whole geometric surface difficult. So Part_1_ver3.java fixes that.)

Test your version of Part_1_ver2.java first on the OBJ file, cubeS.obj. When that works, try a slightly more complicated file, dodecahedron.obj. When that works, try the more complicated file, great_rhombicosidodecahedron.obj.

In the zip file there is a file Part_1_ver3.java that you need to complete. The only change from Part_1_ver2.java is that now each face should get a random color. So all of the triangles in one face should get the same color. You will no longer be able to see the triangulation of each face, but you will be more able to see the geometric shape of the whole object. Run the file small_rhombicosidodecahedron.obj through the two demo programs Part_1_ver2_demo.class and Part_1_ver3_demo.class and compare the differences. Do the same for great_rhombicosidodecahedron.obj.

For part 2 of this assignment, you will write a program that modifies the contents of the framebuffer after the renderer has rendered an image into the framebuffer. In the zip file there is a file Part_2.java that you need to complete. Your program should draw a black border around the image created by the renderer. (Run the demo program Part_2_demo.class on the file horse.obj to see an example.)

The framebuffer is an array of pixel data. For each pixel in the final image, the framebuffer holds three integer values, one that represents the red component of the pixel's color, one integer that represents the green component, and an integer that represents the blue component of the pixel's color. If a framebuffer has dimensions n pixels by m pixels, then the framebuffer hold 3*m*n integers.

Your program needs to find white "background" pixels in the framebuffer that are next to colored pixels in the image (in other words, your program needs to find the edge of the image) and turn those edge pixels black. Your program should essentially be a pair of nested loops that iterate through the whole framebuffer, looking for pixels on the edge of the image. You need to find a (somewhat complicated) boolean expression that is true when a white pixel has one of its (eight) neighbors in the image. Change any pixel that satisfies this boolean from white to black. That will give you a one-pixel wide black border around the image.

But a one pixel wide border is not too noticeable. The border in the demo program is two pixels wide. To get a wider border, iterate your code on the result of your first pass over the framebuffer. The result of the second pass over the framebuffer should leave a two pixel wide border around the image. (A third pass should give a three pixel wide black border.)

To find out more about the FrameBuufer class and what methods it provides, look at its source code in the folder SceneRenderer in the zip file Java renderer.

Turn in a zip file called CS590Hw1Surname.zip (where Surname is your last name) containing your versions of Part_1_ver1.java, Part_1_ver2.java, Part_1_ver3.java and Part_2.java.

This assignment is due Tuesday, February 11.