/** * @(#) GLAnimCanvas.java */ package gl4java.awt; import gl4java.GLContext; import java.awt.*; import java.awt.event.*; import java.lang.Math; /** * This is meant as an base class writing * Animations. A clean usage of multi-threading compatible * with JAVA2 is implemented here ! * *

* * If you are interessting in further Documentation and/or * the history of GL4Java follow the following link. * *

        The GL4Java Documentation
 * 
*

* * This code uses repaint() to fire a sDisplay call by the AWT-Event thread ! * and sleep to suspend for a given Frames per secounds value as default !! * * To switch this behavior for a better performance, and responsiveness * so that sDisplay is called by the animation thread itself * call: * *

        setUseRepaint(false)
 * 
*

* * This code sleep's for a given Frames per secounds after each frame * as default !! * * To switch this behavior for a better performance, * so that much frames are rendered as the machine can do ! * call: * *

        setUseFpsSleep(false)
 * 
*

* But be sure, that the other threads may not have enough time or i * may not get the cpu power ... * * The following settings for setUseRepaint and setUseFpsSleep looks fine: * *

 	

A JVM with operating system threads has: native-threads

A JVM where all JVM threads runs in one operating-system-thread has: green-threads
green-threadsnative-threads
setUseRepaint true true & false
setUseFpsSleep true true & false
*

* * If you play with setUseRepaint or setUseFpsSleep, * be shure to have a Java VM with native-thread support, * because a GL-Context can be shared by many threads, * but one thread can have just one GL-Context ! * * (comments welcome) * *

* To use real fps settings, the following functions provides you to do so: *

        setAnimateFps
        getMaxFps
 * 
* Like the first animation run, this class renders a view frames (default 10) * to subtract the render time from the sleep time ! *

* You should overwrite the following methods for your needs: *

        init - 1st initialisation
        display - render one frame
        reshape - to reshape (window resize)
        ReInit - ReInitialisation after stop for setSuspended(false)
 * 
* * @see GLCanvas * @version 2.0, 21. April 1999 * @author Sven Goethel * */ public class GLAnimCanvas extends GLCanvas implements Runnable { /** * To support frames per scounds, * instead of killing the machine :-) * * A little GUI is supported ! * * @see GLAnimCanvas#run */ protected double FramesPerSec=20; protected long mSecPerFrame=0; /** * the delays .. */ protected long dFpsMilli = 0; /** * The thread for referencing Thread (Animation) * * @see GLAnimCanvas#stop * @see GLAnimCanvas#start * @see GLAnimCanvas#run */ protected Thread killme = null; /** * Instead of using suspend (JAVA2) * * @see GLAnimCanvas#run */ protected boolean threadSuspended = false; static { if(GLContext.loadNativeLibraries(null, null, null)==false) System.out.println("GLAnimCanvas could not load def. native libs."); } /** * * Constructor * * @see GLCanvas#GLCanvas * */ public GLAnimCanvas( int width, int height, String gl_Name, String glu_Name ) { super( width, height, gl_Name, glu_Name ); setAnimateFps(FramesPerSec); } /** * * Constructor * * Uses the default GLFunc and GLUFunc implementation ! * * @see GLCanvas#GLCanvas * */ public GLAnimCanvas( int width, int height ) { super( width, height); setAnimateFps(FramesPerSec); } /** * init should be overwritten by you, * to enter your initialisation code * */ public void init() { /* here we should add and initialize our JAVA components */ /* ... and furthet OpenGL init's - like you want to */ glj.gljCheckGL(); ReInit(); /* and start our working thread ... */ start(); } /** * * This is the rendering-method called by sDisplay * (and sDisplay is called by paint, or by the thread directly !). * The derived-class (Your Subclass) will redefine this, * to draw it's own animation ! * *

* * You should set shallWeRender here, * to signalize the animation-loop 'run' to supsend *

* To restart the thread, just call setSuspended(false) * * @see GLAnimCanvas#shallWeRender * @see GLAnimCanvas#run * @see GLAnimCanvas#setSuspended * @see GLCanvas#sDisplay * @see GLCanvas#paint */ public void display() { int i; /* Standard GL4Java Init */ if( cvsIsInit()==false ) { if(glj.gljClassDebug) System.out.println("GLAnimCanvas not initialized yet ..."); return; } if( glj.gljMakeCurrent(true) == false ) { if(glj.gljClassDebug) System.out.println("GLAnimCanvas problem in gljMakeCurrent() method"); return; } // ... just render it /* For your animation dutys ;-) */ glj.gljSwap(); glj.gljCheckGL(); glj.gljFree(); } /** * ReInit should be overwritten by you, * to enter your re-initialisation within setSuspended(false) * * @see GLAnimCanvas#setSuspended */ public void ReInit() { } protected boolean useRepaint = true; protected boolean useFpsSleep = true; /** * The normal behavior is to use 'repaint' * within the AWT-Event Thread to render. *

* If you have serious reasons, e.g. measuring performance, * you can change it while invoke this function with 'false'. * In this case, the thread itself calls the sDisplay method ! * * On fast good multi-threading machines (native-thread-JVM), * this should increase the performance and the responsiveness ! *

* * @param b if true, uses repaint (default), otherwise directly sDisplay * @see GLCanvas#sDisplay * @see GLAnimCanvas#setUseFpsSleep */ public void setUseRepaint(boolean b) { useRepaint = b; } /** * The normal behavior is to use FpsSleep * * But you can overwrite this behavior and * drop the Frame Per Secound sleeps - * so that much frames are rendered as the machine can do ! *

* * @param b if true, uses Fps sleeping, else not ! * @see GLCanvas#sDisplay * @see GLAnimCanvas#setUseRepaint */ public void setUseFpsSleep(boolean b) { useFpsSleep = b; } public boolean getUseRepaint() { return useRepaint; } public boolean getUseFpsSleep() { return useFpsSleep; } /** * HERE WE DO HAVE OUR RUNNING THREAD ! * WE NEED STUFF LIKE THAT FOR ANIMATION ;-) */ public void start() { if(killme == null) { killme = new Thread(this); killme.start(); resetFpsCounter(); } } public synchronized void stop() { killme = null; threadSuspended=false; notify(); } /** * Should be set in display, * whether to render or not while the animation loop *

* If shallWeRender is false, * this thread will suspend ! * * @see GLAnimCanvas#display * @see GLAnimCanvas#run */ protected boolean shallWeRender = true; private long _fDelay = 0; private long _fDelay_Frames = 10; private boolean _fDelaySync=true; private boolean _fDelayRun=false; /** * The running loop for animations * which initiates the call of display * * @see GLAnimCanvas#shallWeRender * @see GLAnimCanvas#display * @see GLAnimCanvas#diplay */ public void run() { Thread thisThread = Thread.currentThread(); while (killme==thisThread) { if(cvsIsInit()) { /* DRAW THE TINGS .. */ if (shallWeRender) { if(useRepaint) repaint(); else sDisplay(); } else { // lets sleep ... synchronized (this) { threadSuspended=true; } } if(fps_isCounting) fps_frames++; } try { if(useFpsSleep) { if(useRepaint) { if(mSecPerFrame<_f_dur) dFpsMilli=_f_dur; else dFpsMilli=mSecPerFrame; } else { dFpsMilli= mSecPerFrame - _f_dur; if (dFpsMilli<=0) dFpsMilli= 1; } Thread.currentThread().sleep(dFpsMilli, 0 ); } if (threadSuspended) { stopFpsCounter(); synchronized (this) { while (threadSuspended) wait(); } } } catch (InterruptedException e) {} } } /** * Here we can (re)start or suspend animation ... * * If the thread should be (re)started and is not alive -> killed, * or never be started, it will be started ! * * @param suspend if true the thread will be suspended, * if false, the thread will be (re)started * * @see GLAnimCanvas#isAlive * @see GLAnimCanvas#start */ public void setSuspended(boolean suspend) { setSuspended(suspend, false); } /** * Here we can (re)start or suspend animation ... * * If the thread should be (re)started and is not alive -> killed, * or never be started, it will be started ! * * @param suspend if true the thread will be suspended, * if false, the thread will be (re)started * * @param reInit if true the ReInit will be called additionally, * where the user can set additional initialisations * * @see GLAnimCanvas#isAlive * @see GLAnimCanvas#start */ public synchronized void setSuspended(boolean suspend, boolean reInit) { if(suspend) { shallWeRender=false; } else if(isAlive()==false) { start(); } else { // the thread is alive, but suspended and should be // re-started shallWeRender=true; resetFpsCounter(); if(reInit) ReInit(); threadSuspended=false; notify(); } } /** * is the thread alive, means is started and not died ? * * @see GLAnimCanvas#run * @see GLAnimCanvas#setSuspended * @see GLAnimCanvas#start * @see GLAnimCanvas#stop */ public boolean isAlive() { if(killme==null) return false; return killme.isAlive(); } /** * is the thread suspended, means is started but waiting, * or not alive (ok :-| - but it is practical) * * @see GLAnimCanvas#run * @see GLAnimCanvas#setSuspended * @see GLAnimCanvas#start * @see GLAnimCanvas#stop */ public boolean isSuspended() { if(killme==null) return true; return threadSuspended; } private double fps=0; // frame-per-sec private long fps_duration =0; // milli-secs private long fps_start=0; // milli-secs private long fps_frames =0; // number of frames private boolean fps_isCounting =true; // shall i count private boolean verboseFps =false; // shall i be verbose /** * resets the Fps Counter *

* this function is called automatically by * start and setSuspended * * @see GLAnimCanvas#start * @see GLAnimCanvas#setSuspended * @see GLAnimCanvas#resetFpsCounter * @see GLAnimCanvas#stopFpsCounter * @see GLAnimCanvas#getFps * @see GLAnimCanvas#getFpsDuration * @see GLAnimCanvas#getFpsFrames * @see GLAnimCanvas#setVerboseFps */ public void resetFpsCounter() { fps=0; // frame-per-sec fps_duration =0; // milli-secs fps_frames =0; // number of frames fps_isCounting =true; // shall i count fps_start=System.currentTimeMillis(); } /** * stops the Fps Counter and sets all values * fot the getFps* methods *

* this function is called automatically by * run, if the thread is suspended via shallWeRender *

* All data's are print out on System.out * if verboseFps is set ! * * @see GLAnimCanvas#run * @see GLAnimCanvas#shallWeRender * @see GLAnimCanvas#resetFpsCounter * @see GLAnimCanvas#stopFpsCounter * @see GLAnimCanvas#getFps * @see GLAnimCanvas#getFpsDuration * @see GLAnimCanvas#getFpsFrames * @see GLAnimCanvas#setVerboseFps */ public void stopFpsCounter() { if(fps_isCounting==true) { long fps_end=System.currentTimeMillis(); fps_duration = fps_end-fps_start; double timed= ((double)fps_duration)/1000.0; if(timed==0) timed=1.0; fps = ((double)fps_frames)/timed ; fps_isCounting=false; } if(verboseFps) { System.out.println("\nfps = "+String.valueOf(fps)); System.out.println("time = "+String.valueOf(fps_duration)+" ms"); System.out.println("frames = "+String.valueOf(fps_frames)); if(fps_frames==0) fps_frames=1; System.out.println("time/f = "+String.valueOf(fps_duration/fps_frames)+" ms"); } } /** * sets if the Fps data shall be printed to System.out * while stopFpsCounter is called ! *

* verboseFps is set to true by default ! * * @see GLAnimCanvas#run * @see GLAnimCanvas#shallWeRender * @see GLAnimCanvas#resetFpsCounter * @see GLAnimCanvas#stopFpsCounter * @see GLAnimCanvas#getFps * @see GLAnimCanvas#getFpsDuration * @see GLAnimCanvas#getFpsFrames * @see GLAnimCanvas#setVerboseFps */ public void setVerboseFps(boolean v) { verboseFps=v; } /** * returns the calculated frames per secounds *

* this data is avaiable after calling stopFpsCounter * * @see GLAnimCanvas#resetFpsCounter * @see GLAnimCanvas#stopFpsCounter * @see GLAnimCanvas#getFps * @see GLAnimCanvas#getFpsDuration * @see GLAnimCanvas#getFpsFrames * @see GLAnimCanvas#setVerboseFps */ public double getFps() { return fps; } /** * returns the calculated duration in millisecs *

* this data is avaiable after calling stopFpsCounter * * @see GLAnimCanvas#resetFpsCounter * @see GLAnimCanvas#stopFpsCounter * @see GLAnimCanvas#getFps * @see GLAnimCanvas#getFpsDuration * @see GLAnimCanvas#getFpsFrames * @see GLAnimCanvas#setVerboseFps */ public long getFpsDuration() { return fps_duration; } /** * returns the calculated frames number *

* this data is avaiable after calling stopFpsCounter * * @see GLAnimCanvas#resetFpsCounter * @see GLAnimCanvas#stopFpsCounter * @see GLAnimCanvas#getFps * @see GLAnimCanvas#getFpsDuration * @see GLAnimCanvas#getFpsFrames * @see GLAnimCanvas#setVerboseFps */ public long getFpsFrames() { return fps_frames; } /** * Just set the FramePerSecounds for Animation * * @deprecated Now the frames per seconds are allways * calculated, no pre-sync needed. * @see #setAnimateFps(double) */ public void setAnimateFps(double fps, int synFrames) { setAnimateFps(fps); } /** * Just set the FramePerSecounds for Animation * * @see GLAnimCanvas#getMaxFps */ public void setAnimateFps(double fps) { FramesPerSec=fps; mSecPerFrame = (long) ( (1.0/FramesPerSec) * 1000.0 ) ; if(verboseFps) { System.out.println("\nset fps := "+ String.valueOf(fps)+ " -> "+String.valueOf(mSecPerFrame)+ " [ms/frame]" ); } resetFpsCounter(); } /** * Just get the maximum number of Frames per secounds, * which is calculated with the time, one frame needs to render ! * * this value is avaiable after the thread is started * and the first frames are rendered ! * * @see GLAnimCanvas#setAnimateFps */ public double getMaxFps() { return (1.0/(double)_f_dur)*1000.0; } }