I've provided a very simple Applet which conforms to the Java 1.0.2 spec, below, for the benefit of those of you who've never written a Java applet before. I tried to err on the side of simplicity, to make it easy for you to get started, but I implemented double-buffering for you, so you won't get annoying flicker in your applets. All my applet does is let you drag the mouse around to move a big 'X' shape. You can compile it with: javac MySimpleApplet.java and you can run it by loading file MySimpleApplet.html in your Web browser.
There is an on-line API for Java 1.0.2, which you should definitely make use of (I do, continuously).
Feel free to implement in Java 1.1 instead, which uses a newer event model, if you feel more comfortable with that. Either way is ok.
Make an applet which displays a cube as 12 vectors, and lets the user drag the mouse around to rotate the cube. Use the Matrix3D Java classes that I have posted off of my home page to do the 3D matrix and vector math.
Think of the mouse as a simulated track-ball, and try to make your program rotate the cube in a way which feels intuitive to the person dragging the mouse. In other words, rotating the mouse side to side should rotate about the y axis, and dragging the mouse up and down should rotate the cube about the x axis.
Hints: Keep the points of the cube in an array:
Vector3D vertices[8];and at every frame set all the x,y,z coords to be either -1.0 or +1.0:
vertex | x coord | y coord | z coord |
---|---|---|---|
0 | -1 | -1 | -1 |
1 | -1 | -1 | +1 |
2 | -1 | +1 | -1 |
3 | -1 | +1 | +1 |
4 | +1 | -1 | -1 |
5 | +1 | -1 | +1 |
6 | +1 | +1 | -1 |
7 | +1 | +1 | +1 |
Maintain two variables for the rotations about the y and x axes: theta and phi, to hold the current rotation about the y and x axes. To render the transformed cube, get the correct values in the transformation matrix by doing something like:
m.identity(); m.rotateY(theta); m.rotateX(phi);Then you can use the transformed matrix to move all the vertices (by using the transform method in the Vector3D class). When you're done transforming the vertices, just use their x and y coordinates to draw the picture. For any vertex, you'll need to move and scale the coordinates before you draw them, so that they will be visible. To do this you can use something like:
Vector3D v; // assume this is the already-transformed 3D point ... int pixelX = (int)( v.get(0) * 200 + 200 ); int pixelY = (int)( v.get(1) * 200 + 200 );
You should draw the cube by drawing lines between pairs of vertices, by using the g.drawLine method. You don't need to worry about perspective right now.
I leave it up to you to figure out the correct way to connect pairs of vertices in order to draw the edges of a cube. I will tell you that you are going to have to draw 12 lines, and each one is going to be defined by a particular pair of vertex indices. For example, two of them are (0,1) and (0,2). You'll need to figure out the other ten for yourself. Hint: In every case, exactly one of the three coordinates is different between the two vertices that the line connects.
MySimpleApplet.html
<html> <head> <title>MySimpleApplet</title> </head> <body> <applet code=MySimpleApplet.class width=400 height=400> </applet> </body> </html>
MySimpleApplet.java
import java.awt.*; public class MySimpleApplet extends GenericApplet { int x = 100, y = 100; public void render(Graphics g) { if (damage) { g.setColor(Color.white); g.fillRect(0, 0, bounds().width, bounds().height); g.setColor(Color.black); g.drawLine(x - 20, y + 20, x + 20, y - 20); g.drawLine(x - 20, y - 20, x + 20, y + 20); } } public boolean mouseDown(Event e, int x, int y) { moveXY(x, y); return true; } public boolean mouseDrag(Event e, int x, int y) { moveXY(x, y); return true; } void moveXY(int x, int y) { this.x = x; this.y = y; damage = true; } } class GenericApplet extends java.applet.Applet implements Runnable { public boolean damage = true; // you can force a render public void render(Graphics g) { } // you can define how to render private Image image = null; private Graphics buffer = null; private Thread t; private Rectangle r = new Rectangle(0, 0, 0, 0); public void start() { if (t == null) { t = new Thread(this); t.start(); } } public void stop() { if (t != null) { t.stop(); t = null; } } public void run() { try { while (true) { repaint(); t.sleep(30); } } catch(InterruptedException e){}; } public void update(Graphics g) { if (r.width != bounds().width || r.height != bounds().height) { image = createImage(bounds().width, bounds().height); buffer = image.getGraphics(); r = bounds(); damage = true; } render(buffer); damage = false; if (image != null) g.drawImage(image,0,0,this); } }