diff options
-rwxr-xr-x | src/demos/j2d/FlyingText.java | 32 | ||||
-rwxr-xr-x | src/demos/j2d/TestTextRenderer.java | 32 | ||||
-rwxr-xr-x | src/demos/j2d/TextCube.java | 209 | ||||
-rwxr-xr-x | src/demos/util/FPSCounter.java | 225 |
4 files changed, 443 insertions, 55 deletions
diff --git a/src/demos/j2d/FlyingText.java b/src/demos/j2d/FlyingText.java index 49e723b..83bc77d 100755 --- a/src/demos/j2d/FlyingText.java +++ b/src/demos/j2d/FlyingText.java @@ -136,13 +136,7 @@ public class FlyingText extends Demo { private int maxTextWidth; - // FPS computation and rendering - private TextRenderer fpsRenderer; - private int fpsWidth; - private int frameCount; - private long startTime; - private DecimalFormat format = new DecimalFormat("####.00"); - private String fpsText; + private FPSCounter fps; public Container buildGUI() { // Create gui @@ -230,9 +224,8 @@ public class FlyingText extends Demo { // Create the text renderer renderer = new TextRenderer(new Font("Serif", Font.PLAIN, 72), true, true); - // Use a different font for the FPS - fpsRenderer = new TextRenderer(new Font("SansSerif", Font.BOLD, 36), true, true); - fpsWidth = (int) fpsRenderer.getBounds("FPS: 1000.00").getWidth(); + // Create the FPS counter + fps = new FPSCounter(drawable, 36); width = drawable.getWidth(); height = drawable.getWidth(); @@ -259,19 +252,6 @@ public class FlyingText extends Demo { } public void display(GLAutoDrawable drawable) { - if (startTime == 0) { - startTime = System.currentTimeMillis(); - } - - if (++frameCount == 100) { - long endTime = System.currentTimeMillis(); - float fps = 100.0f / (float) (endTime - startTime) * 1000; - frameCount = 0; - startTime = System.currentTimeMillis(); - - fpsText = "FPS: " + format.format(fps); - } - time.update(); // Update velocities and positions of all text @@ -389,11 +369,7 @@ public class FlyingText extends Demo { renderer.endRendering(); // Use the FPS renderer last to render the FPS - if (fpsText != null) { - fpsRenderer.beginRendering(drawable.getWidth(), drawable.getHeight()); - fpsRenderer.draw(fpsText, drawable.getWidth() - fpsWidth - 20, 20); - fpsRenderer.endRendering(); - } + fps.draw(); } public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { diff --git a/src/demos/j2d/TestTextRenderer.java b/src/demos/j2d/TestTextRenderer.java index 0356606..281476f 100755 --- a/src/demos/j2d/TestTextRenderer.java +++ b/src/demos/j2d/TestTextRenderer.java @@ -94,11 +94,7 @@ public class TestTextRenderer implements GLEventListener { private String TEST_STRING = "Java 2D Text"; private int textWidth; private int textHeight; - private String fpsText; - private int fpsWidth; - private long startTime; - private int frameCount; - private DecimalFormat format = new DecimalFormat("####.00"); + private FPSCounter fps; public void init(GLAutoDrawable drawable) { GL gl = drawable.getGL(); @@ -118,26 +114,11 @@ public class TestTextRenderer implements GLEventListener { Rectangle2D textBounds = renderer.getBounds(TEST_STRING); textWidth = (int) textBounds.getWidth(); textHeight = (int) textBounds.getHeight(); + + fps = new FPSCounter(drawable, 36); } public void display(GLAutoDrawable drawable) { - if (startTime == 0) { - startTime = System.currentTimeMillis(); - } - - if (++frameCount == 100) { - long endTime = System.currentTimeMillis(); - float fps = 100.0f / (float) (endTime - startTime) * 1000; - frameCount = 0; - startTime = System.currentTimeMillis(); - - fpsText = "FPS: " + format.format(fps); - if (fpsWidth == 0) { - // Place it at a fixed offset wrt the lower right corner - fpsWidth = (int) renderer.getBounds("FPS: 10000.00").getWidth(); - } - } - time.update(); // Compute the next position of the text @@ -162,11 +143,8 @@ public class TestTextRenderer implements GLEventListener { // Draw text renderer.draw(TEST_STRING, (int) position.x(), (int) position.y()); - if (fpsWidth != 0) { - renderer.draw(fpsText, - drawable.getWidth() - fpsWidth - 10, - 20); - } + // Draw FPS + fps.draw(); // Clean up rendering renderer.endRendering(); diff --git a/src/demos/j2d/TextCube.java b/src/demos/j2d/TextCube.java new file mode 100755 index 0000000..10d97b7 --- /dev/null +++ b/src/demos/j2d/TextCube.java @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package demos.j2d; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Font; +import java.awt.Frame; +import java.awt.event.*; +import java.awt.geom.*; + +import javax.media.opengl.*; +import javax.media.opengl.glu.*; +import com.sun.opengl.util.*; +import com.sun.opengl.util.j2d.*; + +import demos.common.*; +import demos.util.*; + +/** Shows how to place 2D text in 3D using the TextRenderer. */ + +public class TextCube extends Demo { + private float xAng; + private float yAng; + private GLU glu = new GLU(); + private Time time; + private TextRenderer renderer; + private FPSCounter fps; + private float textScaleFactor; + + public static void main(String[] args) { + Frame frame = new Frame("Text Cube"); + frame.setLayout(new BorderLayout()); + + GLCanvas canvas = new GLCanvas(); + final TextCube demo = new TextCube(); + + canvas.addGLEventListener(demo); + frame.add(canvas, BorderLayout.CENTER); + + frame.setSize(512, 512); + final Animator animator = new Animator(canvas); + frame.addWindowListener(new WindowAdapter() { + 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(); + } + }); + frame.show(); + animator.start(); + } + + public void init(GLAutoDrawable drawable) { + renderer = new TextRenderer(new Font("SansSerif", Font.PLAIN, 72)); + GL gl = drawable.getGL(); + gl.glEnable(GL.GL_DEPTH_TEST); + + // Compute the scale factor of the largest string which will make + // them all fit on the faces of the cube + Rectangle2D bounds = renderer.getBounds("Bottom"); + float w = (float) bounds.getWidth(); + float h = (float) bounds.getHeight(); + textScaleFactor = 1.0f / (w * 1.1f); + fps = new FPSCounter(drawable, 36); + + time = new SystemTime(); + ((SystemTime) time).rebase(); + gl.setSwapInterval(0); + } + + public void display(GLAutoDrawable drawable) { + GL gl = drawable.getGL(); + gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); + + gl.glMatrixMode(GL.GL_MODELVIEW); + gl.glLoadIdentity(); + glu.gluLookAt(0, 0, 10, + 0, 0, 0, + 0, 1, 0); + + // Base rotation of cube + gl.glRotatef(xAng, 1, 0, 0); + gl.glRotatef(yAng, 0, 1, 0); + + // Six faces of cube + // Top face + gl.glPushMatrix(); + gl.glRotatef(-90, 1, 0, 0); + drawFace(gl, 1.0f, 0.2f, 0.2f, 0.8f, "Top"); + gl.glPopMatrix(); + // Front face + drawFace(gl, 1.0f, 0.8f, 0.2f, 0.2f, "Front"); + // Right face + gl.glPushMatrix(); + gl.glRotatef(90, 0, 1, 0); + drawFace(gl, 1.0f, 0.2f, 0.8f, 0.2f, "Right"); + // Back face + gl.glRotatef(90, 0, 1, 0); + drawFace(gl, 1.0f, 0.8f, 0.8f, 0.2f, "Back"); + // Left face + gl.glRotatef(90, 0, 1, 0); + drawFace(gl, 1.0f, 0.2f, 0.8f, 0.8f, "Left"); + gl.glPopMatrix(); + // Bottom face + gl.glPushMatrix(); + gl.glRotatef(90, 1, 0, 0); + drawFace(gl, 1.0f, 0.8f, 0.2f, 0.8f, "Bottom"); + gl.glPopMatrix(); + + fps.draw(); + + time.update(); + xAng += 200 * (float) time.deltaT(); + yAng += 150 * (float) time.deltaT(); + } + + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { + GL gl = drawable.getGL(); + gl.glMatrixMode(GL.GL_PROJECTION); + gl.glLoadIdentity(); + glu.gluPerspective(15, (float) width / (float) height, 5, 15); + } + + public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {} + + private void drawFace(GL gl, + float faceSize, + float r, float g, float b, + String text) { + float halfFaceSize = faceSize / 2; + // Face is centered around the local coordinate system's z axis, + // at a z depth of faceSize / 2 + gl.glColor3f(r, g, b); + gl.glBegin(GL.GL_QUADS); + gl.glVertex3f(-halfFaceSize, -halfFaceSize, halfFaceSize); + gl.glVertex3f( halfFaceSize, -halfFaceSize, halfFaceSize); + gl.glVertex3f( halfFaceSize, halfFaceSize, halfFaceSize); + gl.glVertex3f(-halfFaceSize, halfFaceSize, halfFaceSize); + gl.glEnd(); + + // Now draw the overlaid text. In this setting, we don't want the + // text on the backward-facing faces to be visible, so we enable + // back-face culling; and since we're drawing the text over other + // geometry, to avoid z-fighting we disable the depth test. We + // could plausibly also use glPolygonOffset but this is simpler. + // Note that because the TextRenderer pushes the enable state + // internally we don't have to reset the depth test or cull face + // bits after we're done. + renderer.begin3DRendering(); + gl.glDisable(GL.GL_DEPTH_TEST); + gl.glEnable(GL.GL_CULL_FACE); + // Note that the defaults for glCullFace and glFrontFace are + // GL_BACK and GL_CCW, which match the TextRenderer's definition + // of front-facing text. + Rectangle2D bounds = renderer.getBounds(text); + float w = (float) bounds.getWidth(); + float h = (float) bounds.getHeight(); + renderer.draw3D(text, + w / -2.0f * textScaleFactor, + h / -2.0f * textScaleFactor, + halfFaceSize, + textScaleFactor); + renderer.end3DRendering(); + } +} diff --git a/src/demos/util/FPSCounter.java b/src/demos/util/FPSCounter.java new file mode 100755 index 0000000..b0ca200 --- /dev/null +++ b/src/demos/util/FPSCounter.java @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package demos.util; + +import java.awt.Font; +import java.awt.geom.*; +import java.text.*; + +import javax.media.opengl.*; +import com.sun.opengl.util.j2d.*; + +/** A simple class which uses the TextRenderer to provide an FPS + counter overlaid on top of the scene. */ + +public class FPSCounter { + // Placement constants + public static final int UPPER_LEFT = 1; + public static final int UPPER_RIGHT = 2; + public static final int LOWER_LEFT = 3; + public static final int LOWER_RIGHT = 4; + + private int textLocation = LOWER_RIGHT; + private GLDrawable drawable; + private TextRenderer renderer; + private DecimalFormat format = new DecimalFormat("####.00"); + private int frameCount; + private long startTime; + private String fpsText; + private int fpsMagnitude; + private int fpsWidth; + private int fpsHeight; + private int fpsOffset; + + /** Creates a new FPSCounter with the given font size. An OpenGL + context must be current at the time the constructor is called. + + @param drawable the drawable to render the text to + @param textSize the point size of the font to use + @throws GLException if an OpenGL context is not current when the constructor is called + */ + public FPSCounter(GLDrawable drawable, int textSize) throws GLException { + this(drawable, new Font("SansSerif", Font.BOLD, textSize)); + } + + /** Creates a new FPSCounter with the given font. An OpenGL context + must be current at the time the constructor is called. + + @param drawable the drawable to render the text to + @param font the font to use + @throws GLException if an OpenGL context is not current when the constructor is called + */ + public FPSCounter(GLDrawable drawable, Font font) throws GLException { + this(drawable, font, true, true); + } + + /** Creates a new FPSCounter with the given font and rendering + attributes. An OpenGL context must be current at the time the + constructor is called. + + @param drawable the drawable to render the text to + @param font the font to use + @param antialiased whether to use antialiased fonts + @param useFractionalMetrics whether to use fractional font + @throws GLException if an OpenGL context is not current when the constructor is called + */ + public FPSCounter(GLDrawable drawable, + Font font, + boolean antialiased, + boolean useFractionalMetrics) throws GLException { + this.drawable = drawable; + renderer = new TextRenderer(font, antialiased, useFractionalMetrics); + } + + /** Gets the relative location where the text of this FPSCounter + will be drawn: one of UPPER_LEFT, UPPER_RIGHT, LOWER_LEFT, or + LOWER_RIGHT. Defaults to LOWER_RIGHT. */ + public int getTextLocation() { + return textLocation; + } + + /** Sets the relative location where the text of this FPSCounter + will be drawn: one of UPPER_LEFT, UPPER_RIGHT, LOWER_LEFT, or + LOWER_RIGHT. Defaults to LOWER_RIGHT. */ + public void setTextLocation(int textLocation) { + if (textLocation < UPPER_LEFT || textLocation > LOWER_RIGHT) { + throw new IllegalArgumentException("textLocation"); + } + this.textLocation = textLocation; + } + + /** Changes the current color of this TextRenderer to the supplied + one, where each component ranges from 0.0f - 1.0f. The alpha + component, if used, does not need to be premultiplied into the + color channels as described in the documentation for {@link + Texture Texture}, although premultiplied colors are used + internally. The default color is opaque white. + + @param r the red component of the new color + @param g the green component of the new color + @param b the blue component of the new color + @param alpha the alpha component of the new color, 0.0f = + completely transparent, 1.0f = completely opaque + @throws GLException If an OpenGL context is not current when this method is called + */ + public void setColor(float r, float g, float b, float a) throws GLException { + renderer.setColor(r, g, b, a); + } + + /** Updates the FPSCounter's internal timer and counter and draws + the computed FPS. It is assumed this method will be called only + once per frame. + */ + public void draw() { + if (startTime == 0) { + startTime = System.currentTimeMillis(); + } + + if (++frameCount >= 100) { + long endTime = System.currentTimeMillis(); + float fps = 100.0f / (float) (endTime - startTime) * 1000; + recomputeFPSSize(fps); + frameCount = 0; + startTime = System.currentTimeMillis(); + + fpsText = "FPS: " + format.format(fps); + } + + if (fpsText != null) { + renderer.beginRendering(drawable.getWidth(), drawable.getHeight()); + // Figure out the location at which to draw the text + int x = 0; + int y = 0; + switch (textLocation) { + case UPPER_LEFT: + x = fpsOffset; + y = drawable.getHeight() - fpsHeight - fpsOffset; + break; + + case UPPER_RIGHT: + x = drawable.getWidth() - fpsWidth - fpsOffset; + y = drawable.getHeight() - fpsHeight - fpsOffset; + break; + + case LOWER_LEFT: + x = fpsOffset; + y = fpsOffset; + break; + + case LOWER_RIGHT: + x = drawable.getWidth() - fpsWidth - fpsOffset; + y = fpsOffset; + break; + } + + renderer.draw(fpsText, x, y); + renderer.endRendering(); + } + } + + private void recomputeFPSSize(float fps) { + String fpsText; + int fpsMagnitude; + if (fps >= 10000) { + fpsText = "10000.00"; + fpsMagnitude = 5; + } else if (fps >= 1000) { + fpsText = "1000.00"; + fpsMagnitude = 4; + } else if (fps >= 100) { + fpsText = "100.00"; + fpsMagnitude = 3; + } else if (fps >= 10) { + fpsText = "10.00"; + fpsMagnitude = 2; + } else { + fpsText = "9.00"; + fpsMagnitude = 1; + } + + if (fpsMagnitude > this.fpsMagnitude) { + Rectangle2D bounds = renderer.getBounds("FPS: " + fpsText); + fpsWidth = (int) bounds.getWidth(); + fpsHeight = (int) bounds.getHeight(); + fpsOffset = (int) (fpsHeight * 0.5f); + this.fpsMagnitude = fpsMagnitude; + } + } +} |