hello everybody, im new to jogl and i intend to port a C opengl app to jogl.
here comes my first question:
ive seen jogl examples in the opengl-pack quite simple and they all seem to apply the simulation function y the rendering func as this is called everytime by the Animator.
What i want is to know the right way to implement de main loop and call the simulation func at a given rate as i do in C.
In another java app i did, i used a thread to implement this and the sim and rendering func were called inside the while loop in the thread.
¿how do you guys do this? ¿could you post something like a template? ¿were would you call the whole simulation func in your game and how can you control the FPS?
If you want your transition from OpenGL in C to OpenGL in Java to be as smooth as possible, pick the ‘other’ OpenGL binding: LWJGL. (www.lwjgl.org) Browse through the examples.
I bet the next reply here is how I’d ever dare to point you on that direction, but trust me, LWJGL is much more C style. JOGL is more OO, raising the abstraction level a bit, making it harder to port C to Java.
Additionally:
LWJGL features OpenGL 3.0 and 3.1, is actively supported, and JOGL is currently, seemingly, dead in the water.
instead of using the Animator you can simple call display() on a drawable like a GLCanvas in a custom thread.
package org.yourorghere;
import com.sun.opengl.util.Animator;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;
/**
* SimpleJOGL.java
* author: Brian Paul (converted to Java by Ron Cemer and Sven Goethel) <P>
*
* This version is equal to Brian Paul's version 1.2 1999/10/21
*/
public class SimpleJOGL implements GLEventListener {
public static void main(String[] args) {
Frame frame = new Frame("Simple JOGL Application");
final GLCanvas canvas = new GLCanvas();
canvas.addGLEventListener(new SimpleJOGL());
frame.add(canvas);
frame.setSize(640, 480);
// final Animator animator = new Animator(canvas);
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
// Run this on another thread than the AWT event queue to
// make sure the call to Animator.stop() completes before
// exiting
new Thread(new Runnable() {
public void run() {
// animator.stop();
System.exit(0);
}
}).start();
}
});
// Center frame
frame.setLocationRelativeTo(null);
frame.setVisible(true);
// animator.start();
while(true) {
canvas.display();
}
}
public void init(GLAutoDrawable drawable) {
// Use debug pipeline
// drawable.setGL(new DebugGL(drawable.getGL()));
GL gl = drawable.getGL();
System.err.println("INIT GL IS: " + gl.getClass().getName());
// Enable VSync
gl.setSwapInterval(1);
// Setup the drawing area and shading mode
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
gl.glShadeModel(GL.GL_SMOOTH); // try setting this to GL_FLAT and see what happens.
}
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
GL gl = drawable.getGL();
GLU glu = new GLU();
if (height <= 0) { // avoid a divide by zero error!
height = 1;
}
final float h = (float) width / (float) height;
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL.GL_PROJECTION);
gl.glLoadIdentity();
glu.gluPerspective(45.0f, h, 1.0, 20.0);
gl.glMatrixMode(GL.GL_MODELVIEW);
gl.glLoadIdentity();
}
public void display(GLAutoDrawable drawable) {
GL gl = drawable.getGL();
// Clear the drawing area
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
// Reset the current matrix to the "identity"
gl.glLoadIdentity();
// Move the "drawing cursor" around
gl.glTranslatef(-1.5f, 0.0f, -6.0f);
// Drawing Using Triangles
gl.glBegin(GL.GL_TRIANGLES);
gl.glColor3f(1.0f, 0.0f, 0.0f); // Set the current drawing color to red
gl.glVertex3f(0.0f, 1.0f, 0.0f); // Top
gl.glColor3f(0.0f, 1.0f, 0.0f); // Set the current drawing color to green
gl.glVertex3f(-1.0f, -1.0f, 0.0f); // Bottom Left
gl.glColor3f(0.0f, 0.0f, 1.0f); // Set the current drawing color to blue
gl.glVertex3f(1.0f, -1.0f, 0.0f); // Bottom Right
// Finished Drawing The Triangle
gl.glEnd();
// Move the "drawing cursor" to another position
gl.glTranslatef(3.0f, 0.0f, 0.0f);
// Draw A Quad
gl.glBegin(GL.GL_QUADS);
gl.glColor3f(0.5f, 0.5f, 1.0f); // Set the current drawing color to light blue
gl.glVertex3f(-1.0f, 1.0f, 0.0f); // Top Left
gl.glVertex3f(1.0f, 1.0f, 0.0f); // Top Right
gl.glVertex3f(1.0f, -1.0f, 0.0f); // Bottom Right
gl.glVertex3f(-1.0f, -1.0f, 0.0f); // Bottom Left
// Done Drawing The Quad
gl.glEnd();
// Flush all drawing operations to the graphics card
gl.glFlush();
}
public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {
}
}
JOGL has only some object-oriented classes but the most part is in the GL interface which is very close of what you can do with C. Therefore, I recommend you to use JOGL. On the other hand, Riven has forgotten to tell you that in most cases, you are not forced to use the higher abstraction level if you prefer doing exactly the same things than in C. For example, to use the textures, you can use the class Texture to take the OO way or use the methods of the interface GL to take the C-like way.
Kenneth is very busy, JOGL is not dead, please stop this propaganda now. You disappoint me because I thought you would have a bit more respect for his work. He wrote me recently that he has only to modify some things in GlueGen but the biggest part of the refactoring has been done.
flip, let me know if you need some more examples or if you have any problem with your game, I will do my best.
Yes, another guy who worked on GL4Java works now on JOGL too.
flip, you can watch the following example and the one provided by bienator:
//depends on jogl.jar and gluegen-rt.jar
import javax.media.opengl.GL;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.glu.GLU;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.media.opengl.GLCanvas;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import com.sun.opengl.util.Animator;
public class JOGLTetrahedron implements GLEventListener, KeyListener {
float rotateT = 0.0f;
static GLU glu = new GLU();
static GLCanvas canvas = new GLCanvas();
static Frame frame = new Frame("Jogl 3D Shape/Rotation");
static Animator animator = new Animator(canvas);
public void display(GLAutoDrawable gLDrawable) {
final GL gl = gLDrawable.getGL();
gl.glClear(GL.GL_COLOR_BUFFER_BIT);
gl.glClear(GL.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();
gl.glTranslatef(0.0f, 0.0f, -5.0f);
gl.glRotatef(rotateT, 1.0f, 0.0f, 0.0f);
gl.glRotatef(rotateT, 0.0f, 1.0f, 0.0f);
gl.glRotatef(rotateT, 0.0f, 0.0f, 1.0f);
gl.glRotatef(rotateT, 0.0f, 1.0f, 0.0f);
gl.glBegin(GL.GL_TRIANGLES);
// Front
gl.glColor3f(0.0f, 1.0f, 1.0f);
gl.glVertex3f(0.0f, 1.0f, 0.0f);
gl.glColor3f(0.0f, 0.0f, 1.0f);
gl.glVertex3f(-1.0f, -1.0f, 1.0f);
gl.glColor3f(0.0f, 0.0f, 0.0f);
gl.glVertex3f(1.0f, -1.0f, 1.0f);
// Right Side Facing Front
gl.glColor3f(0.0f, 1.0f, 1.0f);
gl.glVertex3f(0.0f, 1.0f, 0.0f);
gl.glColor3f(0.0f, 0.0f, 1.0f);
gl.glVertex3f(1.0f, -1.0f, 1.0f);
gl.glColor3f(0.0f, 0.0f, 0.0f);
gl.glVertex3f(0.0f, -1.0f, -1.0f);
// Left Side Facing Front
gl.glColor3f(0.0f, 1.0f, 1.0f);
gl.glVertex3f(0.0f, 1.0f, 0.0f);
gl.glColor3f(0.0f, 0.0f, 1.0f);
gl.glVertex3f(0.0f, -1.0f, -1.0f);
gl.glColor3f(0.0f, 0.0f, 0.0f);
gl.glVertex3f(-1.0f, -1.0f, 1.0f);
// Bottom
gl.glColor3f(0.0f, 0.0f, 0.0f);
gl.glVertex3f(-1.0f, -1.0f, 1.0f);
gl.glColor3f(0.1f, 0.1f, 0.1f);
gl.glVertex3f(1.0f, -1.0f, 1.0f);
gl.glColor3f(0.2f, 0.2f, 0.2f);
gl.glVertex3f(0.0f, -1.0f, -1.0f);
gl.glEnd();
rotateT += 0.2f;
}
public void displayChanged(GLAutoDrawable gLDrawable,
boolean modeChanged, boolean deviceChanged) {
}
public void init(GLAutoDrawable gLDrawable) {
GL gl = gLDrawable.getGL();
gl.glShadeModel(GL.GL_SMOOTH);
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
gl.glClearDepth(1.0f);
gl.glEnable(GL.GL_DEPTH_TEST);
gl.glDepthFunc(GL.GL_LEQUAL);
gl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT,
GL.GL_NICEST);
gLDrawable.addKeyListener(this);
}
public void reshape(GLAutoDrawable gLDrawable, int x,
int y, int width, int height) {
GL gl = gLDrawable.getGL();
if(height <= 0) {
height = 1;
}
float h = (float)width / (float)height;
gl.glMatrixMode(GL.GL_PROJECTION);
gl.glLoadIdentity();
glu.gluPerspective(50.0f, h, 1.0, 1000.0);
gl.glMatrixMode(GL.GL_MODELVIEW);
gl.glLoadIdentity();
}
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_ESCAPE) {
exit();
}
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
public static void exit(){
animator.stop();
frame.dispose();
System.exit(0);
}
public static void main(String[] args) {
canvas.addGLEventListener(new JOGLTetrahedron());
frame.add(canvas);
frame.setSize(640, 480);
frame.setUndecorated(true);
frame.setExtendedState(Frame.MAXIMIZED_BOTH);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
exit();
}
});
frame.setVisible(true);
animator.start();
canvas.requestFocus();
}
}
As we use an Animator, you don’t need to call repaint or anything like this except in active rendering:
while (!gameover){
//simulation stuff
calculate();
//rendering stuff
canvas.display();
//NO SLEEP!!! It breaks the synchronization and decreases the frame rate noticeably!!!
}
Then, you need a GLEventListener to perform all GL tasks, watch the example whose I have given you the link. Your GLEventListener has a method called public void display(GLAutoDrawable gLDrawable) that performs the GL calls except for the initialization, calling display() on the canvas (GLCanvas) does the same thing than glutPostRedisplay when you use GLUT in C. The method public void init(GLAutoDrawable gLDrawable) performs the GL calls at the initialization of the context (glFrustum and gluLookAt for example). Another method handles the case of reshaping and change of display.
Ok, ive already seen those examples, but where do you really make the call to the simulation function (collision detection, etc) where is the right spot to place it in the code? i believe it’s not in the rendering function which is called everytime by the animator.
i’ve pointed out a possible solution with a thread, but there may be others like for example with a timertask perhaps?
i mean where do you guys put all the code related to the game in the example above of the wikipedia? in the example above, the
rotateT+= 0.2f
in the display function shouldnt be there because as i see it, it belongs to the simulation function, that’s what im specifically asking. We cant place all the gameupdate/logic/etc into the display function just because it’s called everytime by the animator. It must be separated.
And finally,
how can you control the timing/FPS of your app then?
public final void runEngine(){
lastBotShotTime=currentTime();
initialize(); // load stuff, raw setup
gameRunning=true;
innerLoop=false;
boolean hasBeenKilled=false;
Rectangle2D.Float rWallVoxel = new Rectangle2D.Float();
Rectangle2D.Float rPlayerVoxel = new Rectangle2D.Float();
final float playerSize=1.0f/4.0f;
final float wallSize=1.0f;
rPlayerVoxel.width=playerSize;
rPlayerVoxel.height=playerSize;
rWallVoxel.width=wallSize;
rWallVoxel.height=wallSize;
boolean playMenuMusicOnce=true;
//loop until the player tries to exit the game
while(gameRunning)
{reinit(hasBeenKilled); // re-set all positions
while(gameController.getCycle()!=GameCycle.GAME && gameRunning)
{if(playMenuMusicOnce)
{gameController.playSound(15);
playMenuMusicOnce=false;
}
//TODO: update a model only used in the menu
gameController.display();
}
if(gameRunning)
{if(hasBeenKilled)
{player.respawn();
hasBeenKilled=false;
}
innerLoop=true;
gameController.playSound(14);
}
boolean bmoved;
double playerXnew,playerZnew;
float framerateCompensatedSpeed;
this.internalClock.start();
this.lastBotShotTime=currentTime();
this.lastShot=currentTime();
this.isFalling=false;
long cycleDuration;
boolean walkSoundPlayedOnce=false;
//loop until the end of a party (even when game paused)
while(innerLoop)
{gameController.display();
//update the clock if required
cycleDuration=internalClock.getElapsedTime();
if(bpause)
{if(!internalClock.isPaused())
internalClock.pause();
continue;
}
else
if(internalClock.isPaused())
internalClock.unpause();
else
internalClock.sync();
cycleDuration=internalClock.getElapsedTime()-cycleDuration;
framerateCompensationFactor = 16*cycleDuration*10;
//uses the time spent between 2 frames to adapt the step to the speed of the machine
framerateCompensatedSpeed=framerateCompensationFactor*(runningFast?3:1)/65536.0f;
In the source code above, you see gameController.display() in the inner loop, some synchronization (internalClock). If you look at the whole method, the collisions are tested a bit further.