package classwork; import javax.media.opengl.*; import java.awt.event.KeyEvent; import jocode.*; /** * GLART_11_timing.java *
* Simulates a clock with second hand moving in real-time. Demonstrates * use of a timing loop to control program execution speed. *
* Hit SPACE to reset the timer. *
* The frequency at which screen images are drawn is called the * framerate and is usually measured in frames per second. The goal * in this demo is to animate a clock at the same speed (real time) * no matter how fast or slow the system is rendering. *
* Since cpu performance varies, program execution speed can vary from * one computer to another. If you move an animation a fixed amount * in each render(), then the animation will move at different rates on * different computers, as render() is called more often on the faster * cpu. *
* VSync also affects the framerate. If VSync is enabled then * OpenGL frame updates are synchronized with the monitor refresh rate. * Most monitors refresh the screen 60 or 75 per second, and VSync will * force updates to wait till the monitor is ready to draw. * This limits the refresh rate to something constant, but monitors can * have different refresh rates, and slower graphics cards will slow * down the refresh rate. *
* To insure that animations run at the same rate across various * computers, you need to move animations according to actual time * elapsed, not frame render rate. *
* Check how much time has elapsed since the last frame rendered, then * "step" the simulation forward in proportion to that amount of time. * The simulation keeps track of how much time has elapsed. *
* Java's time function, System.currentTimeMillis(), does not measure * time accurately below 10 milliseconds, and will not give accurate * results when measuring time elapsed between frames. Use * System.nanoTime() instead. *
* Java includes a hardware timer that measures nanoseconds (System.nanoTime()). * Use double datatype to hold nanosecond time values type to prevent loss of * precision. *
* The JOApp.getSecondsPerFrame() function returns the average seconds per frame * for the past 100 frames. This creates smoother timing than reacting to the * exact time elapsed between each frame (which can vary unpredictably * depending on cpu load). */ public class GLART_11_timing extends JOApp { float rotation = 0f; // texture handle (a number that refers to an allocated texture) int myTextureHandle = 0; // For time tracking double startTime; // starting time of simulation (in seconds) double simulationTime; // current time of simulation (in seconds) /** * Main function just creates and runs the application. */ public static void main(String args[]) { GLART_11_timing app = new GLART_11_timing(); windowTitle = "Timing Demo"; displayWidth = 800; displayHeight = 600; app.useCurrentDisplay = false; app.fullScreen = false; app.run(); } /** * Initialize the environment */ public void setup() { // Allocate and configure a texture based on the image myTextureHandle = makeTexture("images/grid_texture_marked.png"); //---------------------------------------------------- // Initialize values for tracking time: //---------------------------------------------------- resetTimeCounters(); // Select the Projection Matrix (controls perspective) gl.glMatrixMode(GL.GL_PROJECTION); gl.glLoadIdentity(); // Reset The Projection Matrix // Define perspective glu.gluPerspective( 45.0f, // Field Of View (float)getWidth() / (float)getHeight(), // aspect ratio 0.1f, // near Z clipping plane 100.0f); // far Z clipping plane // set the background color gl.glClearColor(.2f, .2f, .23f, 1); } /** * Reset the timer values. */ public void resetTimeCounters() { // reset simulation time startTime = simulationTime = getTimeInSeconds(); // rotation of clock hand in degrees rotation = 0; } /** * Render the scene. */ public void draw() { // how far should second hand rotate per second float degreesPerSecond = 6f; // seconds elapsed since we last rendered double secsSinceLastFrame = (double)getTimeInSeconds() - simulationTime; // update the simulation current time simulationTime += secsSinceLastFrame; /////////////////////////////////////////////////////// // Animate second hand of clock PICK ONE: // OLD WAY: rotate an arbitrary amount each frame //rotation += .2f; // BETTER: rotate in proportion to time elapsed rotation += degreesPerSecond * secsSinceLastFrame; // done /////////////////////////////////////////////////////// // Clear screen and depth buffer gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); // Select The Modelview Matrix (controls model orientation) gl.glMatrixMode(GL.GL_MODELVIEW); // Reset the Modelview matrix // this resets the coordinate system to center of screen gl.glLoadIdentity(); // Where is the 'eye' glu.gluLookAt( -1f, 0f, 7f, // eye position -1f, 0f, 0f, // target to look at 0f, 1f, 0f); // which way is up // draw the clock face gl.glDisable(GL.GL_TEXTURE_2D); gl.glColor4f(.5f, .5f, .5f, 1f); gl.glPushMatrix(); { // draw markers every 5 minutes (30 degrees) for (int i=0; i < 12; i++) { gl.glRotatef(30f, 0,0,1); gl.glBegin(GL.GL_LINES); { gl.glVertex3f(0f, 1.5f, -.1f); gl.glVertex3f(0f, 2f, -.1f); } gl.glEnd(); } } gl.glPopMatrix(); // draw the second hand gl.glColor4f(.9f, .8f, .8f, 1f); gl.glPushMatrix(); { // rotate coordinate system gl.glRotatef(-rotation, 0,0,1); // draw a triangular second hand gl.glBegin(GL.GL_TRIANGLES); { gl.glTexCoord2f(0, 0); gl.glVertex3f(-.1f, 0f, 0f); // Bottom Left gl.glTexCoord2f(1, 0); gl.glVertex3f( .1f, 0f, 0f); // Bottom Right gl.glTexCoord2f(.5f, .5f); gl.glVertex3f( 0, 2f, 0f); // Top } gl.glEnd(); } gl.glPopMatrix(); // text needs textures ON gl.glEnable(GL.GL_TEXTURE_2D); // Show the simulated time elapsed. // This is the time that we have processed within our simulation. printZ(-4.5f, -.25f, 0f, 0, .03f, formatTime(simulationTime-startTime) ); // Show the actual system time elapsed. // This is the computer clock time that has elapsed since we started the simulation. print(50, 40, "Actual time elapsed: " + formatTime(getTimeInSeconds()-startTime) ); } /** * Given time in seconds, returns minutes:seconds formatted string. */ public String formatTime(double seconds) { int secs = (int)(seconds % 60); String strSecs = (secs < 10)? "0" + secs : "" + secs; return "" + ((int)(seconds/60f)) + ":" + strSecs; } /** * SPACEBAR resets clock */ public void keyUp(int keycode) { if (keycode == KeyEvent.VK_SPACE) { resetTimeCounters(); } } }