|
This example shows how to load simple objects from a text file, and morph smoothly from one object into another.
This is the Java port of the one of the NeHe OpenGL tutorials.
You can get complete IntelliJ IDEA project structure (all source, resources, build script, …) by downloading the source distribution from here.
The original post of the programmer who ported the examples can be found here.
package demos.nehe.lesson25;
import demos.common.GLDisplay;
public class Lesson25 {
public static void main(String[] args) {
GLDisplay neheGLDisplay = GLDisplay.createGLDisplay("Lesson 25: Morphing points");
Renderer renderer = new Renderer();
InputHandler inputHandler = new InputHandler(renderer, neheGLDisplay);
neheGLDisplay.addGLEventListener(renderer);
neheGLDisplay.addKeyListener(inputHandler);
neheGLDisplay.start();
}
}
package demos.nehe.lesson25;
import demos.common.GLDisplay;
import javax.swing.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
class InputHandler extends KeyAdapter {
private Renderer renderer;
public InputHandler(Renderer renderer, GLDisplay display) {
this.renderer = renderer;
display.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_1, 0), "Morph to object 1");
display.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_2, 0), "Morph to object 2");
display.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_3, 0), "Morph to object 3");
display.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_4, 0), "Morph to object 4");
display.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), "Increase X speed");
display.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), "Decrease X speed");
display.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "Increase Y speed");
display.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "Decrease Y speed");
display.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0), "Increase Z speed");
display.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0), "Decrease Z speed");
display.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_Q, 0), "Object farther");
display.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_Z, 0), "Object closer");
display.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), "Object up");
display.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_S, 0), "Object down");
display.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_A, 0), "Object left");
display.registerKeyStrokeForHelp(
KeyStroke.getKeyStroke(KeyEvent.VK_D, 0), "Object right");
}
public void keyPressed(KeyEvent e) {
processKeyEvent(e, true);
}
public void keyReleased(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_1: // Is 1 Pressed, key Not Equal To 1 And Morph False?
renderer.morphTo(1); // Sets key To 1 (To Prevent Pressing 1 2x In A Row)
break;
case KeyEvent.VK_2: // Is 2 Pressed, key Not Equal To 2 And Morph False?
renderer.morphTo(2); // Sets key To 2 (To Prevent Pressing 2 2x In A Row)
break;
case KeyEvent.VK_3: // Is 3 Pressed, key Not Equal To 3 And Morph False?
renderer.morphTo(3); // Sets key To 3 (To Prevent Pressing 3 2x In A Row)
break;
case KeyEvent.VK_4: // Is 4 Pressed, key Not Equal To 4 And Morph False?
renderer.morphTo(4); // Sets key To 4 (To Prevent Pressing 4 2x In A Row)
break;
default:
processKeyEvent(e, false);
}
}
private void processKeyEvent(KeyEvent e, boolean pressed) {
switch (e.getKeyCode()) {
case KeyEvent.VK_PAGE_UP: // Is Page Up Being Pressed?
renderer.increaseZspeed(pressed); // Increase zspeed
break;
case KeyEvent.VK_PAGE_DOWN: // Is Page Down Being Pressed?
renderer.decreaseZspeed(pressed); // Decrease zspeed
break;
case KeyEvent.VK_DOWN: // Is Page Up Being Pressed?
renderer.increaseXspeed(pressed); // Increase xspeed
break;
case KeyEvent.VK_UP: // Is Page Up Being Pressed?
renderer.decreaseXspeed(pressed); // Decrease xspeed
break;
case KeyEvent.VK_RIGHT: // Is Page Up Being Pressed?
renderer.increaseYspeed(pressed); // Increase yspeed
break;
case KeyEvent.VK_LEFT: // Is Page Up Being Pressed?
renderer.decreaseYspeed(pressed); // Decrease yspeed
break;
case KeyEvent.VK_Q: // Is Q Key Being Pressed?
renderer.moveObjectFarther(pressed); // Move Object Away From Viewer
break;
case KeyEvent.VK_Z: // Is Z Key Being Pressed?
renderer.moveObjectCloser(pressed); // Move Object Towards Viewer
break;
case KeyEvent.VK_W: // Is W Key Being Pressed?
renderer.moveObjectUp(pressed); // Move Object Up
break;
case KeyEvent.VK_S: // Is S Key Being Pressed?
renderer.moveObjectDown(pressed); // Move Object Down
break;
case KeyEvent.VK_D: // Is D Key Being Pressed?
renderer.moveObjectRight(pressed); // Move Object Right
break;
case KeyEvent.VK_A: // Is A Key Being Pressed?
renderer.moveObjectLeft(pressed); // Move Object Left
break;
}
}
}
package demos.nehe.lesson25;
import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;
import java.io.*;
import java.util.StringTokenizer;
import demos.common.ResourceRetriever;
class Renderer implements GLEventListener {
private static final int STEPS = 200; // Maximum Number Of Steps
private float xrot,yrot,zrot; // X, Y & Z Rotation
private float xspeed,yspeed,zspeed; // X, Y & Z Spin Speed
private boolean increaseXspeed, decreaseXspeed;
private boolean increaseYspeed, decreaseYspeed;
private boolean increaseZspeed, decreaseZspeed;
private float cx,cy,cz = -15; // X, Y & Z Position
private boolean moveObjectFarther, moveObjectCloser;
private boolean moveObjectUp, moveObjectDown;
private boolean moveObjectLeft, moveObjectRight;
private int step = 0; // Step Counter
private boolean morph = false; // Default morph To False (Not Morphing)
private int maxver; // Will Eventually Hold The Maximum Number Of Vertices
// Our 4 Morphable Objects (morph1,2,3 & 4)
private Object3D objects[] = new Object3D[4];
private final Object morphLock = new Object();
// Helper Object, Source Object, Destination Object
private Object3D helper,sour,dest;
private GLU glu = new GLU();
public void morphTo(int morphTarget) {
if (morphTarget < 1 || morphTarget > 4) {
throw new IllegalArgumentException(
"Morph target must lie between 1 and " + objects.length);
}
synchronized (morphLock) {
if (morph)
return;
morph = true;
dest = objects[morphTarget - 1];
}
}
public void increaseXspeed(boolean increaseXspeed) {
this.increaseXspeed = increaseXspeed;
}
public void decreaseXspeed(boolean decreaseXspeed) {
this.decreaseXspeed = decreaseXspeed;
}
public void increaseYspeed(boolean increaseYspeed) {
this.increaseYspeed = increaseYspeed;
}
public void decreaseYspeed(boolean decreaseYspeed) {
this.decreaseYspeed = decreaseYspeed;
}
public void increaseZspeed(boolean increaseZspeed) {
this.increaseZspeed = increaseZspeed;
}
public void decreaseZspeed(boolean decreaseZspeed) {
this.decreaseZspeed = decreaseZspeed;
}
public void moveObjectUp(boolean moveObjectUp) {
this.moveObjectUp = moveObjectUp;
}
public void moveObjectDown(boolean moveObjectDown) {
this.moveObjectDown = moveObjectDown;
}
public void moveObjectLeft(boolean moveObjectLeft) {
this.moveObjectLeft = moveObjectLeft;
}
public void moveObjectRight(boolean moveObjectRight) {
this.moveObjectRight = moveObjectRight;
}
public void moveObjectFarther(boolean moveObjectFarther) {
this.moveObjectFarther = moveObjectFarther;
}
public void moveObjectCloser(boolean moveObjectCloser) {
this.moveObjectCloser = moveObjectCloser;
}
// Loads Object From File (name)
private Object3D loadObject(InputStream inputStream) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream));
String oneline = in.readLine();
StringTokenizer tokenizer = new StringTokenizer(oneline);
tokenizer.nextToken(); // Vertices:
// Scans Text For "Vertices: ". Number After Is Stored In ver
int nbVertices = Integer.parseInt(tokenizer.nextToken());
// Sets Objects verts Variable To Equal The Value Of ver
Object3D object = new Object3D(nbVertices);
for (int i = 0; i < nbVertices; i++) { // Loops Through The Vertices
oneline = in.readLine(); // Reads In The Next Line Of Text
// Searches For 3 Floating Point Numbers, Store In rx,ry & rz
tokenizer = new StringTokenizer(oneline);
float rx = Float.parseFloat(tokenizer.nextToken());
float ry = Float.parseFloat(tokenizer.nextToken());
float rz = Float.parseFloat(tokenizer.nextToken());
object.points[i] = new Vertex();
object.points[i].x = rx; // Sets Objects (k) points.x Value To rx
object.points[i].y = ry; // Sets Objects (k) points.y Value To ry
object.points[i].z = rz; // Sets Objects (k) points.z Value To rz
}
// If ver Is Greater Than maxver Set maxver Equal To ver
if (nbVertices > maxver) maxver = nbVertices;
return object;
}
// Calculates Movement Of Points During Morphing
void calculate(int i, Vertex a) {
// a.x Value Equals Source x - Destination x Divided By Steps
a.x = (sour.points[i].x - dest.points[i].x) / STEPS;
// a.y Value Equals Source y - Destination y Divided By Steps
a.y = (sour.points[i].y - dest.points[i].y) / STEPS;
// a.z Value Equals Source z - Destination z Divided By Steps
a.z = (sour.points[i].z - dest.points[i].z) / STEPS;
} // This Makes Points Move At A Speed So They All Get To Their
// Destination At The Same Time
public void init(GLAutoDrawable drawable) {
GL gl = drawable.getGL();
// Set The Blending Function For Translucency
gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE);
// This Will Clear The Background Color To Black
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
gl.glClearDepth(1.0); // Enables Clearing Of The Depth Buffer
gl.glDepthFunc(GL.GL_LESS); // The Type Of Depth Test To Do
gl.glEnable(GL.GL_DEPTH_TEST); // Enables Depth Testing
gl.glShadeModel(GL.GL_SMOOTH); // Enables Smooth Color Shading
// Really Nice Perspective Calculations
gl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);
maxver = 0; // Sets Max Vertices To 0 By Default
try {
// Load The First Object Into morph1 From File sphere.txt
objects[0] = loadObject(ResourceRetriever.getResourceAsStream(
"demos/data/models/Sphere.txt"));
// Load The Second Object Into morph2 From File torus.txt
objects[1] = loadObject(ResourceRetriever.getResourceAsStream(
"demos/data/models/Torus.txt"));
// Load The Third Object Into morph3 From File tube.txt
objects[2] = loadObject(ResourceRetriever.getResourceAsStream(
"demos/data/models/Tube.txt"));
// Load sphere.txt Object Into Helper (Used As Starting Point)
helper = loadObject(ResourceRetriever.getResourceAsStream(
"demos/data/models/Sphere.txt"));
} catch (IOException e) {
throw new RuntimeException(e);
}
// Manually Reserver Ram For A 4th 468 Vertice Object (morph4)
objects[3] = new Object3D(486);
for (int i = 0; i < 486; i++) // Loop Through All 468 Vertices
{
objects[3].points[i] = new Vertex();
// morph4 x Point Becomes A Random Float Value From -7 to 7
objects[3].points[i].x = (float) (Math.random() - 0.5) * 7;
// morph4 y Point Becomes A Random Float Value From -7 to 7
objects[3].points[i].y = (float) (Math.random() - 0.5) * 7;
// morph4 z Point Becomes A Random Float Value From -7 to 7
objects[3].points[i].z = (float) (Math.random() - 0.5) * 7;
}
// Source & Destination Are Set To Equal First Object (morph1)
sour = dest = objects[0];
}
public void display(GLAutoDrawable drawable) {
GL gl = drawable.getGL();
drawGLScene(gl);
update();
}
private void drawGLScene(GL gl) {
// Clear The Screen And The Depth Buffer
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity(); // Reset The View
// Translate The The Current Position To Start Drawing
gl.glTranslatef(cx, cy, cz);
gl.glRotatef(xrot, 1, 0, 0); // Rotate On The X Axis By xrot
gl.glRotatef(yrot, 0, 1, 0); // Rotate On The Y Axis By yrot
gl.glRotatef(zrot, 0, 0, 1); // Rotate On The Z Axis By zrot
xrot += xspeed;
yrot += yspeed;
zrot += zspeed; // Increase xrot,yrot & zrot by xspeed, yspeed & zspeed
float tx,ty,tz; // Temp X, Y & Z Variables
Vertex q = new Vertex(); // Holds Returned Calculated Values For One Vertex
gl.glBegin(GL.GL_POINTS); // Begin Drawing Points
for (int i = 0; i < maxver; i++) { // Loop Through All The Verts
if (morph)
calculate(i, q);
else
// If morph Is True Calculate Movement Otherwise Movement=0
q.x = q.y = q.z = 0;
// Subtract q.x Units From helper.points[i].x (Move On X Axis)
helper.points[i].x -= q.x;
// Subtract q.y Units From helper.points[i].y (Move On Y Axis)
helper.points[i].y -= q.y;
// Subtract q.z Units From helper.points[i].z (Move On Z Axis)
helper.points[i].z -= q.z;
// Make Temp X Variable Equal To Helper's X Variable
tx = helper.points[i].x;
// Make Temp Y Variable Equal To Helper's Y Variable
ty = helper.points[i].y;
// Make Temp Z Variable Equal To Helper's Z Variable
tz = helper.points[i].z;
gl.glColor3f(0, 1, 1); // Set Color To A Bright Shade Of Off Blue
// Draw A Point At The Current Temp Values (Vertex)
gl.glVertex3f(tx, ty, tz);
gl.glColor3f(0, 0.5f, 1); // Darken Color A Bit
tx -= 2 * q.x;
ty -= 2 * q.y;
ty -= 2 * q.y; // Calculate Two Positions Ahead
// Draw A Second Point At The Newly Calculate Position
gl.glVertex3f(tx, ty, tz);
gl.glColor3f(0, 0, 1); // Set Color To A Very Dark Blue
tx -= 2 * q.x;
ty -= 2 * q.y;
ty -= 2 * q.y; // Calculate Two More Positions Ahead
// Draw A Third Point At The Second New Position
gl.glVertex3f(tx, ty, tz);
} // This Creates A Ghostly Tail As Points Move
gl.glEnd(); // Done Drawing Points
// If We're Morphing And We Haven't Gone Through All 200 Steps
// Increase Our Step Counter
// Otherwise Set Morphing To False, Make Source=Destination And Set
// The Step Counter Back To Zero.
if (morph && step <= STEPS)
step++;
else {
synchronized (morphLock) {
morph = false;
sour = dest;
step = 0;
}
}
}
private void update() {
if (increaseZspeed) // Is Page Up Being Pressed?
zspeed += 0.001f; // Increase zspeed
if (decreaseZspeed) // Is Page Down Being Pressed?
zspeed -= 0.001f; // Decrease zspeed
if (increaseXspeed) // Is Page Up Being Pressed?
xspeed += 0.001f; // Increase xspeed
if (decreaseXspeed) // Is Page Up Being Pressed?
xspeed -= 0.001f; // Decrease xspeed
if (increaseYspeed) // Is Page Up Being Pressed?
yspeed += 0.001f; // Increase yspeed
if (decreaseYspeed) // Is Page Up Being Pressed?
yspeed -= 0.001f; // Decrease yspeed
if (moveObjectFarther) // Is Q Key Being Pressed?
cz -= 0.01f; // Move Object Away From Viewer
if (moveObjectCloser) // Is Z Key Being Pressed?
cz += 0.01f; // Move Object Towards Viewer
if (moveObjectUp) // Is W Key Being Pressed?
cy += 0.01f; // Move Object Up
if (moveObjectDown) // Is S Key Being Pressed?
cy -= 0.01f; // Move Object Down
if (moveObjectRight) // Is D Key Being Pressed?
cx += 0.01f; // Move Object Right
if (moveObjectLeft) // Is A Key Being Pressed?
cx -= 0.01f; // Move Object Left
}
public void reshape(GLAutoDrawable drawable,
int xstart,
int ystart,
int width,
int height) {
GL gl = drawable.getGL();
height = (height == 0) ? 1 : height;
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL.GL_PROJECTION);
gl.glLoadIdentity();
glu.gluPerspective(45, (float) width / height, 0.1, 100);
gl.glMatrixMode(GL.GL_MODELVIEW);
gl.glLoadIdentity();
}
public void displayChanged(GLAutoDrawable drawable,
boolean modeChanged,
boolean deviceChanged) {
}
private static class Vertex { // Structure For 3D Points
public float x, y, z; // X, Y & Z Points
}
private static class Object3D {
public Vertex[] points; // One Vertice (Vertex x,y & z)
public Object3D(int nbPoints) {
points = new Vertex[nbPoints];
&nbs | |