In this assignment, you will build a 3-D wireframe renderer from the pieces of your previous assignment. You write a new parser to load the geometry, you will use your matrix library for transformations, and you will use your 2-D line drawer to rasterize the wireframe.
Homework 2 is due on Tuesday Oct. 21 at 11:59 pm

We will be using the OpenInventor file format to store our geometry for this lab. You can find several files in the homework 2 data. Here is a simple example:
# Inventor V2.1 ascii
# (Above line required for use in actual Inventor environments, for future)
# <-- Hash symbol means this is a comment until end of line
PerspectiveCamera { # Sets up Camera location, field of view, etc.
position 0 0 1
orientation 0 0 1 0
nearDistance 1
farDistance 10
left -1 # (Not true Inventor command, but we use it anyway).
right 1 # " "
top 1 # " "
bottom -1 # " "
}
Separator { # Defines a layer of scoping, to "group" geometric items.
Transform { # rotation, translation, scaling in any order, or absent
translation tx ty tz # 3 real numbers
rotation axisX axisY axisZ angle # 4 real numbers for axis-angle rep of rotn
# this angle is in radians
scaleFactor sx sy sz # 3 real numbers
} # end of Transform
.
. possibly more transforms.
.
# Note:
# Matrices for a series of transform blocks are set up on stacks.
# The matrix commands are post-multiplied onto the current matrix --
# The LAST command issued is the FIRST transformation applied to the object.
# All these Transform blocks are applied to the following object
# Creates list of 3D pts, named with integers starting at 0
Coordinate3 {
point [
# Each point is 3 real numbers. First point is indexed by 0
x0 y0 z0,
x1 y1 z1,
...
xn yn zn
] # Note that the Coordinate3 list is within the scope of the transform
}
# Uses the integer names of the pts to make polygonal faces
IndexedFaceSet {
coordIndex [
#Face 0, uses point numbers, -1 is terminator
face0point0, face0point1, ... -1,
face1point0, face1point1, ... -1,
...
faceNpoint0, faceNpoint1, ... -1
]
} # Note that the IndexedFaceSet (polyhedron) is in the same scope
} # End of Separator
A corresponding BNF grammar could look something like this (starting symbol is blocks, the notation [symbol] means 0 or 1 of symbol at that point, i.e. a ::= b [c] means a ::= b | b c):
blocks ::= block [blocks]
block ::= camerablock | sepblock
camerablock ::= PCAMERA open cameralines close
cameralines ::= cameraline [cameralines]
cameraline ::= POS triple | ORIENT quad | NDIST NUMBER
| FDIST NUMBER | LEFT NUMBER | RIGHT NUMBER
| TOP NUMBER | BOTTOM NUMBER
sepblock ::= SEPARATOR open sepitems close
sepitems ::= sepitem [sepitems]
sepitem ::= TRANSFORM open translines close
| COORD3 open POINT sqopen triples sqclose close
| IFACESET open ifslines close
translines ::= transline [translines]
transline ::= TRANSLAT triple
| SFACTOR triple
| ROT quad
ifslines ::= ifsline [ifslines]
ifsline ::= COORDINDEX sqopen singles sqclose
triple ::= NUMBER NUMBER NUMBER
triples ::= triple [COMMA triples]
quad ::= NUMBER NUMBER NUMBER NUMBER
open ::= LBRACE
close ::= RBRACE
sqopen ::= LBRACKET
sqclose ::= RBRACKET
A Separator block can contain multiple
Transform blocks. Each block can can have a translation,
rotation, or scaling. These are all optional and may be specified in
any order; however, there can be no more than one of each. When you
build a matrix from a transform block, scaling should be applied
first, then rotation, then translation. Thus the formula should be
S = TRS.
Just multiply the 1 or more transformation matrices and create a single object matrix . The first transform node encountered within a separator node is applied last, i.e. goes on the left side of all object transform matrices.
In the camera node, two sets of numbers, orientation and position, are specified. The first three numbers after orientation give you a rotation axis, and the last number is an angle. The three numbers after position describe a translation vector. The camera was first rotated by some angle around some axis (described by orientation) and then translated by some vector (described by position) with respect to the global coordinate system. This has an effect equivalent to applying the inverse transformation to every other point in the system. Let C = TR be the matrix describing the transform applied to the camera. There are two ways to get the inverse: you can either use the inverse function from your matrix library (C-1 = (TR)-1), or you can use the special formulae for inverses of translation and rotation matrices. If you choose to use the formulae, remember that when you invert a product of matrices, you get the product of inverses with the order reversed, so (C-1 = R-1 T-1, which is equivalent to translating by the negative vector and then rotating by the opposite angle (-theta) around the same axis.
The fourCubes.iv top left cube will tell you if implemented TRS in the right order.
All you need to do here is take the numbers for top, bottom, left, right, near and far, and plug them into the formula for Perspective Projection as t, b, l, r, n, and f respectively. The formula is given on the page about Homogeneous Coordinates and Transformation Matrices.
Make sure you use perspective projection and not orthographic. This maps from some frustum shape (truncated wedge shape 6-hedron) to a cube).
Let O = O1*...*On = object transform clauses
C = camera transform (that's applied to the camera)
P = perspective projection
Then for each point, x (which has been homogenized by adding a fourth w
corrdinate of 1.0) the transformed point x' = P*C-1*O*x
Take
these x', divide by w, and plug them into the rasterizer (ignoring z at this
point-- use just the new x and y coordinates) so that they draw polygons as
described by your indexed face set.
Your 3D wireframe program should be called wireframe and take the following arguments:
wireframe [xRes] [yRes]
where xRes, yRes are the dimensions of the image that will be drawn
The two integers xRes and yRes indicate the size of the image to be made.
Input should be read from stdin. The input format is just the OpenInventor format discussed at length above.
The output image should be written to stdout in the PPM file format, as discussed in assignment 1.
To view the result image, you can pipe the output of your program to display:
./wireframe 400 400 < cube.iv | display -
You can also save to a file using convert:
./wireframe 400 400 < cube.iv | convert - cube.png
Now that you've got the canvas and lines and transformations from the previous assignment, you can string them all together with your .iv parser:
You can use whatever colors you like, although the sample results are white on black.
A few test cases are provided. You can also develop your own test cases.