Efficient JOGL machining cycle

I'm new to JOGL and I'm trying to figure out how to make a render loop with it ... I know there are animator and FPS classes that I can use, but they seem to be pretty restrictive. I've written a few render loops in the past that I like much better than I prefer to use, but I can't seem to implement it correctly with the JOGL GLEventListener class.

Here is my render loop (and the class it comes in)

package com.richardkase.game;

public class Game implements Runnable {

public final String TITLE = "Test Game";

private boolean running = false;
private Thread thread;

private FPSCounter fps;


////// Constructor //////

public Game() {

    fps = new FPSCounter(150, 60);
}


////// Game Loop //////

@Override
public void run() {
    while (running) {
        fps.findDeltas();

        // this executes at "the second argument" of fps times a second
        if (fps.checkTickDelta())
            tick();

        // this executes at "the first argument" of fps times a second
        if (fps.checkFrameDelta())
            render();

        // this code executes once a second
        fps.checkPassingSecond();
    }
}


////// Tick Methods //////

private void tick() {
    long before = System.nanoTime();
    // code goes here

    fps.tick(before);
}


////// Render Methods //////

private void render() {
    long before = System.nanoTime();
    // code goes here

    fps.render(before);
}



////// Thread Methods //////

private synchronized void start() {
    if (running)
        return;
    running = true;
    thread = new Thread(this);
    thread.start();
}

private synchronized void stop() {
    if (!running)
        return;
    running = false;
    try {
        thread.join();
    } catch (InterruptedException e) {
        System.exit(1);
        e.printStackTrace();
    }
}


///////// Main Method //////////

public static void main(String[] args) {
    Game game = new Game();

    JFrame frame = new JFrame(game.TITLE);
    frame.setSize(500, 500);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setResizable(true);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);

    game.start();
}
}

      

here is the FPSCounter class just for reference, all it does is keep track of the time to make sure everything runs when it is supposed to

public class FPSCounter {

private long lastTime = System.nanoTime();

private double tickRate;
private double tickCheck;
private double tickDelta;

private double frameRate;
private double frameCheck;
private double frameDelta;

private int updates;
private int frames;
private long timer;

private long nanosPerFrame;
private long nanosPerUpdate;


////// Constructor ///////

public FPSCounter(double frameRate, double tickRate) {

    this.frameRate = frameRate;
    frameCheck = 1_000_000_000 / this.frameRate;
    frameDelta = 0;

    this.tickRate = tickRate;
    tickCheck = 1_000_000_000 / this.tickRate;
    tickDelta = 0;

    updates = 0;
    frames = 0;
    timer = System.currentTimeMillis();

}


////// find delta //////

public void findDeltas() {
    long now = System.nanoTime();
    tickDelta += now - lastTime;
    frameDelta += now - lastTime;
    lastTime = now;
}


////// Delta Check //////

public boolean checkTickDelta() {
    if (tickDelta >= tickCheck)  {
        tickDelta = 0;
        return true;
    }
    return false;
}

public boolean checkFrameDelta() {
    if (frameDelta >= frameCheck)  {
        frameDelta = 0;
        return true;
    }
    return false;
}


////// Second Check //////

public void checkPassingSecond() {
    if (System.currentTimeMillis() - timer > 1000) {
        System.out.println(updates + " updates, fps is " + frames);
        timer += 1000;
        frames = 0;
        updates = 0;
    }
}


////// Game Loop Methods ///////

public void render(long before) {
    long after = System.nanoTime();
    nanosPerFrame = after - before;
    frames++;
}

public void tick(long before) {
    long after = System.nanoTime();
    nanosPerUpdate = after - before;
    updates++;
}
}

      

How do I add actual content to this game loop? should I have this class also extend the GLEventListener or have a reference to a class that extends it? Or is there a more efficient way to do this with an animator class that gives me the kind of control I'm overlooking?

any help would be greatly appreciated !! Thanks to

EDIT:

I must also add that I am very new to JOGL, found out about this a few days ago because I was trying to render things in 3d, so I hardly know more than the profile system in JOGL ... the explanations are GREAT appreciated!

I think I need to expand what I'm looking for here. I need a render loop that renders in a similar way above, where the game updates its state at one rate and redraws all graphics at a different rate.

+3


source to share


1 answer


This is how I got started with JOGL ... It's pretty simple and straightforward, but if required I can explain the code in detail;)

import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLProfile;
import javax.media.opengl.awt.GLCanvas;
import javax.swing.JFrame;

import com.jogamp.opengl.util.FPSAnimator;

public class OpenGLMain implements GLEventListener { 

    private static FPSAnimator animator;
    private static int width;
    private static int height;
    private static GL2 gl;
    public static Rectangle screenSize;
    public static JFrame frame;

    public static void main(String[] args) {
        GLProfile glprofile = GLProfile.getMaximum(true);
        GLCapabilities capabilities = new GLCapabilities(glprofile);
        GLCanvas canvas = new GLCanvas(capabilities);

        screenSize = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
        width = (int) screenSize.getWidth();
        height = (int) screenSize.getHeight();

        frame = new JFrame("Frame name");
        frame.setAlwaysOnTop(false);
        frame.setSize(width, height);
        frame.add(canvas);
        frame.setUndecorated(true);
        frame.setVisible(true);

        animator = new FPSAnimator(25);
        animator.add(canvas);
        animator.start();

        canvas.addGLEventListener(new OpenGLMain());
        canvas.requestFocus();

        Listeners.keyClicks(canvas);
        Listeners.mouseMovement(canvas);
        Listeners.mouseClicks(canvas);
        Listeners.mouseScrolled(canvas);

        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
    }

    public void display(GLAutoDrawable drawable) {
        update();
        render(drawable);
    }

    public void dispose(GLAutoDrawable drawable) {
    }

    public void init(GLAutoDrawable drawable) {
        gl = drawable.getGL().getGL2();
        Scenes.init(drawable, gl);
    }

    public void reshape(GLAutoDrawable drawable, int arg1, int arg2, int arg3, int arg4) {
    }

    private void update() {
        Scenes.update();
    }

    private void render(GLAutoDrawable drawable) {
        Scenes.render(drawable);
    }
}

      


public class OpenGLMain implements GLEventListener {

      

This line allows you to implement the GLEvents that come with JOGL


GLProfile glprofile = GLProfile.getMaximum(true);
GLCapabilities capabilities = new GLCapabilities(glprofile);
GLCanvas canvas = new GLCanvas(capabilities);

      

This will get the largest possible openGL context to use in the canvas


screenSize = GraphicsEnvironment
    .getLocalGraphicsEnvironment()
    .getMaximumWindowBounds();
width = (int) screenSize.getWidth();
height = (int) screenSize.getHeight();

      

The maximum window borders will give you a comfortable place on your desktop, but you can simply set the width and height to whatever size you want ...


frame = new JFrame("Frame name");
frame.setAlwaysOnTop(false);
frame.setSize(width, height);
frame.add(canvas);
frame.setUndecorated(true);
frame.setVisible(true);

      

This is the JFrame that will hold the OpenGL canvas. You can customize it the way you like :)


animator = new FPSAnimator(25);
animator.add(canvas);
animator.start();

      



This creates the animator at a frame rate of 25 frames per second, connects it to the canvas, and starts the animator thread


canvas.addGLEventListener(new OpenGLMain());
canvas.requestFocus();

      

This will add a GLEvent listener to the newly instantiated instance of your class, but I guess this is the point where you inject a separate GLEventListener class


Listeners.keyClicks(canvas);
Listeners.mouseMovement(canvas);
Listeners.mouseClicks(canvas);
Listeners.mouseScrolled(canvas);

      

This is my way of triggering listeners for keys, mouse movement, clicks and scrolls ... it's in a separate static class


frame.addWindowListener(new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
            System.exit(0);
        }
    });

      

It's just a listener for the window close event ...


public void display(GLAutoDrawable drawable) {
    update();
    render(drawable);
}

      

This is where the main loop comes in ... I split it into methods update();

and render();

so that everything is updated before it happens.


public void init(GLAutoDrawable drawable) {
    gl = drawable.getGL().getGL2();
    Scenes.init(drawable, gl);
}

      

An init method that happens before rendering ... I have a separate static Scenes class where I create layers and a logical structure of scenes to paint on the canvas.

0


source







All Articles