3D Modeling with polygons lecture (Sep 26)

In order to do this week's homework (due Tuesday Oct 3), the following notes from class should be useful.

NOTES ON REPRESENTING 3D SHAPES WITH POLYGONS

A shape can be represented as an object that contains:

• A 4x4 transformation matrix
• an array of: `double vertices[NVERTICES][6]`
• an array of: `int faces[NFACES][]`
When you want a shape to have a visible edge with a crease, then you should use different vertices on the two sides of the edge, so you can create a discontinuity in surface normal along that edge. But if you want to create the illusion of a continuous curved surface, then you should make two adjacent polygonal faces share the same vertices.

### Cylinder example

For example, to create an approximation of a cylinder, where the circle is represented by N steps (eg: N=8 or N=20) you need to create the central tube and two end caps:

If we let angle theta take on each of the N values around a circle:

θ = 2π i / N   ,   where i = 0, 1, 2, .... N-1
then we can create the central tube with 2N vertices (each with x,y,z and normal nx,ny,nz), where the first N vertices are:
[cosθ, sinθ, -1, cosθ, sinθ, 0]
and the last N vertices are:
[cosθ, sinθ, +1, cosθ, sinθ, 0]
Each of the faces of this central tube will have four sides, so the `face[][]` array needs to consist of:
 0 1 N+1 N 1 2 N+2 N+1 ... ... ... ... N-2 N-1 2N-1 2N-2 N-1 0 N 2N-1
The front cap will contain N+1 vertices, including the added central point [0,0,1, 0,0,1], and its faces will consist of N triangles. The back cap will also contain N+1 vertices, including the added central point [0,0,-1, 0,0,-1], and its faces will also consist of N triangles. I leave it as an exercise for you to create these two end caps. When you've built the complete cylinder, you should have 4N+2 vertices and 3N faces.

### Cube example

As we discussed in class, a cube is rather simple. You would model the vertices as a unit cube (with extent -1 to +1 in each of the three dimensions), and then rely on the shape's matrix to transform these vertices in order to create all other box-like shapes.

Because we need to separately store vertices that have different normals, there will be 24 vertices; one for each corner of each face of the cube. For example, the cube's four vertices on its leftmost face (the face that points into negative x), are:

 -1 -1 -1 -1 0 0 -1 -1 +1 -1 0 0 -1 +1 +1 -1 0 0 -1 +1 -1 -1 0 0
Notice that I have ordered these four vertices in counterclockwise order, if you are looking at this face of the cube from the outside. I leave it to you to create the vertices for the other five faces of the cube.

The face data for the cube is rather simple, as we discussed in class:

 0 1 2 3 4 5 6 7 ... ... ... ... 20 21 22 23
TRANSFORMING AND RENDERING SHAPES

At every animation frame, for each shape in your scene you'll want to:

1. animate the shape by manipulating its matrix, as we discussed in class, then

2. transform each of the shape's vertices by its matrix, and finally

3. display the edges of each of the shape's faces, by drawing a line, using the `java.awt.Graphics` method `drawLine` between the projection onto the image of successive vertices in that face, as we discussed in class.

I recommend that in your Shape object you maintain an array `transformedVertices[NV][6]`, into which you can place your transformed vertex data every frame between steps (2) and (3) above.

WINDOW AND VIEWPORT

In order to properly project each transformed vertex onto your image, so that you can draw lines between them, you'll need to do a window transform followed by a viewport transform.

The window transformation places an object floating in 3D space into position in front of the virtual "camera". For now, you can just use a really trivial window transformation:

[x,y,z] → [x,y]
Later on we will do more interesting things, like perspective and animating cameras. The viewport transformation maps the coordinates (x,y) into some pixel coordinates (px, py). For now you can just use the simple viewport transform:
[x,y] → [ (int)(W/2 + x*W/2) , (int)(H/2 - y*W/2) ]
where W and H are the width and height of your applet, in pixels.