Saturday 30 August 2008

Adventures in 3D: Part III - Poly Filler

Right, you've done parts I and II, and you've got some rotating points. Remember that in Part I, we discussed that we were going to build our scenes out of triangles. Luckily for us, we also mentioned that a triangle is just 3 points. If you can rotate a point, you can rotate a triangle.

To start with, we'll throw together a Triangle class. It'll have arrays to store the coordinates for each point, and when we rotate it we just need to apply the rotation to each element of the array.

import java.awt.Graphics2D;

public class Triangle implements Drawable {

double[] x = new double[3];
double[] y = new double[3];
double[] z = new double[3];

public Triangle(double x1, double y1, double z1, double x2, double y2, double z2,
double x3, double y3, double z3){
x[0] = x1;
x[1] = x2;
x[2] = x3;
y[0] = y1;
y[1] = y2;
y[2] = y3;
z[0] = z1;
z[1] = z2;
z[2] = z3;

public void draw(Graphics2D graphics){

public void rotateZ(double radians){
double[] sx = x.clone();
for(int i = 0; i < 3 ;i++){
x[i] = (sx[i] * Math.cos(radians)) - (y[i] * Math.sin(radians));
y[i] = (y[i] * Math.cos(radians)) + (sx[i] * Math.sin(radians));

Don't forget that when it comes to storing the original points in the rotation, assignment of an array is by reference, so you need to use .clone() to get a copy of the array instead of a reference to the original. Again, a shrinking triangle will be a sign that you'd missed something.

We're also implementing Drawable, so we need to specify how to draw this triangle. Luckily that's pretty simple, it's just a case of drawing a polygon between the appropriate points. The only slight difficulty is that drawPolygon() expects arrays of integers, and we're storing the points as doubles, so we'll need to do a quick cast into new arrays before we can draw the object.

public void draw(Graphics2D graphics){

for(int i=0;i < 3;i++){
xPoints[i] = (int) x[i];
yPoints[i] = (int) y[i];

graphics.drawPolygon(xPoints, yPoints, 3);

Last thing to do is to actually create some triangles to draw. We could do something really simple, but let's push the boat out and create a sphere. I won't go into the details too much, but it just involves creating points by latitude and longitude, creating squares from neighbouring points, and then breaking that square down into 2 triangles, which are added to the scene. The STP variable determines how small those squares are, and so ultimately how smooth your object looks - at the cost of speed, natch. Play around with it and see what works.

    private ArrayList<Drawable> createScene(){
int STP = 60;
double stp = Math.PI * 2 / STP;
double radius = 100;
ArrayList<Drawable> scene = new ArrayList<Drawable>();

Point[][] points = new Point[STP][(STP/2)+1];
for(int phi = 0; phi < STP; phi++){
for(int theta = 0; theta < (STP/2)+1; theta++){
points[phi][theta] = new Point(
radius * Math.cos(stp*phi) * Math.sin(stp*theta),
radius * Math.sin(stp*phi) * Math.sin(stp*theta),
radius * Math.cos(stp*theta));

for(int x = 0; x < STP; x++){
for(int y = 0; y < (STP/2); y++){
int xx = (x+1)%STP;
int yy = (y+1);
Point p1 = points[x][y];
Point p2 = points[xx][y];
Point p3 = points[x][yy];
Point p4 = points[xx][yy];
Triangle t = new Triangle(p1.x, p1.y, p1.z,
p2.x, p2.y, p2.z,
p3.x, p3.y, p3.z);
Triangle t2 = new Triangle(p2.x, p2.y, p2.z,
p4.x, p4.y, p4.z,
p3.x, p3.y, p3.z);

return scene;

Some other slight adjustments to be made. In our laziness we assumed in the mouseDragged() method that we were dealing with Points, but our scene is now made up of Triangles, so that needs to be changed. You might also want to change the axes of rotation to whatever feels natural, now that you've only gone and created a freakin' wireframe sphere that rotates when you drag it! Download the source

One final thing - instead of wireframe, let's actually fill the polygons and create a solid object. Just replace graphics.drawPolygon() with graphics.fillPolygon().
Uhhhh. Wait. Now we've just got a big blob. That's not nearly as cool. Don't panic though, in Part IV - Let There Be Light, we'll look at how to give this thing some definition.

No comments: