Our goal is to describe various shapes in terms of the two arrays
int faces[][]; double vertices[][6];As we said in class, we will assign a normal vector to each vertex, so each vertex contains not three numeric values but rather six. Eventually we will create the illusion of smooth curved surfaces by using this normal vector data - two adjoining polygonal faces of a shape can be made to appear smooth and rounded by having them share vertices, since they will then share the same normal vector at that vertex.
CUBE
As we said in class, this means that if we with to create the visual impression of an edge between two adjoining faces, then we must use two different vertices to describe those faces' common point - one vertex having the normal vector each face. So a unit cube, for example, no longer requires eight vertices, but rather twenty four - we cannot reuse the smame vertices where different faces meet at corners of the cube, because those vertices have different surface normals. For example, the four vertices of the back and front faces are, respectively, as follows:
... { -1, -1, -1, 0, 0, -1 }, // FOUR BACK FACE VERTICES { -1, 1, -1, 0, 0, -1 }, // IN COUNTERCLOCKWISE ORDER { 1, 1, -1, 0, 0, -1 }, { 1, -1, -1, 0, 0, -1 }, ... { -1, -1, 1, 0, 0, 1 }, // FOUR FRONT FACE VERTICES { 1, -1, 1, 0, 0, 1 }, // IN COUNTERCLOCKWISE ORDER { 1, 1, 1, 0, 0, 1 }, { -1, 1, 1, 0, 0, 1 }, ...We follow a similar principle for the left and right faces and for the bottom and top faces.
The face description for the cube now becomes easy:
{ { 0, 1, 2, 3}, { 4, 5, 6, 7}, { 8, 9, 10, 11}, { 12, 13, 14, 15}, { 16, 17, 18, 19}, { 20, 21, 22, 23}, }TUBE
We make curved shapes by having different faces
share vectors, so that they will have the same surface normal
at that point.
For example,
as we discussed in class,
to describe an n
sided
approximation to an open cylindrical tube
(where the opening is along the z axis)
we need a totwl of 2n
vertices.
There is a row of back vertices 0..n-1
:
vi = { cos θ , sin θ , -1 , cos θ , sin θ , 0 }and a row of front vertices
n..2n-1
:
vn+i = { cos θ , sin θ , 1 , cos θ , sin θ , 0 }where
θ = (2π i) / n
.
We also need n faces, each with four vertices:
int i1 = (i + 1) % n; facei = { i , i1 , n + i1 , n + i }Given a geometry object
Geometry g = new Geometry();you can implement the above logic by defining a method
tube(int n)
in the Geometry
class.
Implementing this method consists of filling in a geometry object's
faces[]
and
vertices[]
arrays
as described above.
GLOBE
To create a sphere as a longitude/latitude globe,
we again need to create an array of vertices,
and also an array of faces that index into the vertices array.
We can create a method in the Geometry class called
globe(int m, int n)
where m
is the number of longitude steps
around the equator of our polygon approximation to a sphere,
and n
is the number of steps from the south
pole to the north pole.
Internally we want m × n+1
vertices. We need the extra row of vertices in the n
direction because we want to have
vertices at both the south pole and the north pole.
There is a little trickiness here
because we are thinking of this as a two dimensional
array of vertices, but our
Geometry data structure for storing vertices
is indexed by a single number.
We are going to deal with this as follows:
First we will
act as though we really two have two indices (i,j),
and then we will come up with a single index I
to describe the index pair (i,j).
As we discussed in class,
the location of a single vertex on the globe vi,j
in is given by computing the polar coordinates:
θ = 2π i / m // LONGITUDE BETWEEN 0 AND 2π
φ = π j / n - π/2 // LATITUDE BETWEEN -π/2 AND π/2
x = cos θ cos φ
y = sin θ cos φ
z = sin φ
vi,j = { x , y , z , x , y , z }
Note that the normal vector for each vertex
is the same as its location.
This is only true in the special case
where the shape is a unit sphere.
As we said above,
we really need to this data into a singly-indexed
array of vertices. We do that by defining a single array
double vertices[ m * (n+1) ][6];
which is indexed by I = i + m * j
.
For convenience you mght want to define a function within the geometry object
that does this mapping:
double I(int i, int j) { return i + m * j; }
The faces will refer to this single index.
The globe will have m × n
faces, each with four vertices.
int faces[][] = new int[m * n][4];
Using our handy dandy index mapping function I(i,j)
,
each face is described as a counterclockwise loop of four vertex indices:
int i1 = (i + 1) % m;
faceI(i,j) = { I(i,j) , I(i1,j) , I(i1,j+1) , I(i,j+1) }
DISK
A flat circuar disk can be approximated by an n-sided polygon.
In this case it is useful to introduce one extra
vertex, in the center of the disk, so that you can
define the disk as a ring of triangles.
So there will be n+1
vertices:
θ = 2π i / n
vi = { cos θ , sin θ , 0 , 0 , 0 , 1 } where 0 ≤ i < n
vn = { 0 , 0 , 0 , 0 , 0 , 1 }
Rather than one giant polygon, it is better
to use n
triangular faces:
int i1 = (i + 1) % n;
facei = { i , i1 , n }
CYLINDER
You can make a cylinder by putting together
an open tube and two disks as a composite object,
using logic something like this:
Geometry cyl = new Geometry();
cyl.add().tube(n); // THE TUBE
cyl.add().disk(n).getMatrix().translate(0,0,-1).rotateX(Math.PI); // END CAP IN BACK
cyl.add().disk(n).getMatrix().translate(0,0,1); // END CAP IN FRONT
HOMEWORK
Your assignment,
due next Wednesday, February 27
(the date was written incorrectly on the first version I posted),
is to implement some of these shapes,
as many as you can, and do something cool and fun with them.
Also, see if you can make other sorts of shapes,
like generalized cylinders -
extruded shapes with non-circualar cross-section.
I guess that would be the Ghostbusters Fun Factory. :-)
If you are ambitious, you might also try a torus.
The location of one surface point on a torus is as follows:
x = cos θ (R + r cos φ)
y = sin θ (R + r cos φ)
z = r sin φ
where R
is the radius of the large ring,
and r
is the radius (ie half-thickness) of the tube.