From 1452e67ee4c6dc1da54119c86e37225ac6b3b559 Mon Sep 17 00:00:00 2001 From: Kenneth Russel Date: Sat, 20 Jan 2007 19:33:47 +0000 Subject: Added 3D rendering methods to TextureRenderer and TextRenderer on request of several people on javagaming.org forums. Refactored existing 2D rendering support in these classes in terms of the new 3D methods. Wrote new TextCube demo illustrating how to render 2D text in 3D using the TextRenderer. Factored out FPS counter rendering into new FPSCounter utility class and updated TestTextRenderer and FlyingText demos. git-svn-id: file:///usr/local/projects/SUN/JOGL/git-svn/svn-server-sync/jogl/trunk@1092 232f8b59-042b-4e1e-8c03-345bb8c30851 --- .../com/sun/opengl/util/j2d/TextRenderer.java | 143 ++++++++++++----- .../com/sun/opengl/util/j2d/TextureRenderer.java | 177 +++++++++++++++------ 2 files changed, 230 insertions(+), 90 deletions(-) (limited to 'src/classes/com/sun/opengl/util/j2d') diff --git a/src/classes/com/sun/opengl/util/j2d/TextRenderer.java b/src/classes/com/sun/opengl/util/j2d/TextRenderer.java index 7f47c011a..0ad84c90c 100755 --- a/src/classes/com/sun/opengl/util/j2d/TextRenderer.java +++ b/src/classes/com/sun/opengl/util/j2d/TextRenderer.java @@ -262,26 +262,23 @@ public class TextRenderer { @throws GLException If an OpenGL context is not current when this method is called */ public void beginRendering(int width, int height) throws GLException { - if (DEBUG && !debugged) { - debug(); - } + beginRendering(true, width, height); + } - getBackingStore().beginOrthoRendering(width, height); - GL gl = GLU.getCurrentGL(); + /** Begins rendering of 2D text in 3D with this {@link TextRenderer + TextRenderer} into the current OpenGL drawable. Assumes the end + user is responsible for setting up the modelview and projection + matrices, and will render text using the {@link #draw3D draw3D} + method. This method pushes some OpenGL state bits, binds and + enables the internal OpenGL texture object, sets the texture + environment mode to GL_MODULATE, and changes the current color + to the last color set with this TextRenderer via {@link + #setColor setColor}. - if (!haveMaxSize) { - // Query OpenGL for the maximum texture size and set it in the - // RectanglePacker to keep it from expanding too large - int[] sz = new int[1]; - gl.glGetIntegerv(GL.GL_MAX_TEXTURE_SIZE, sz, 0); - packer.setMaxSize(sz[0], sz[0]); - haveMaxSize = true; - } - - // Change texture environment mode to MODULATE - gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE); - // Change text color to last saved - gl.glColor4f(r, g, b, a); + @throws GLException If an OpenGL context is not current when this method is called + */ + public void begin3DRendering() throws GLException { + beginRendering(false, 0, 0); } /** Changes the current color of this TextRenderer to the supplied @@ -320,6 +317,23 @@ public class TextRenderer { @throws GLException If an OpenGL context is not current when this method is called */ public void draw(String str, int x, int y) throws GLException { + draw3D(str, x, y, 0, 1); + } + + /** Draws the supplied String at the desired 3D location using the + renderer's current color. The baseline of the leftmost character + is placed at position (x, y, z) in the current coordinate system. + + @param str the string to draw + @param x the x coordinate at which to draw + @param y the y coordinate at which to draw + @param z the z coordinate at which to draw + @param scaleFactor a uniform scale factor applied to the width and height of the drawn rectangle + @throws GLException If an OpenGL context is not current when this method is called + */ + public void draw3D(String str, + float x, float y, float z, + float scaleFactor) { // Split up the string into space-separated pieces tokenize(str); int xOffset = 0; @@ -369,33 +383,39 @@ public class TextRenderer { TextData data = (TextData) rect.getUserData(); data.markUsed(); - // Align the leftmost point of the baseline to the (x, y) coordinate requested - renderer.drawOrthoRect(x - data.origin().x + xOffset, - y - (rect.h() - data.origin().y), - rect.x(), - renderer.getHeight() - rect.y() - rect.h(), - rect.w(), rect.h()); - xOffset += rect.w(); + // Align the leftmost point of the baseline to the (x, y, z) coordinate requested + renderer.draw3DRect(x - scaleFactor * (data.origin().x + xOffset), + y - scaleFactor * ((rect.h() - data.origin().y)), + z, + rect.x(), + renderer.getHeight() - rect.y() - rect.h(), + rect.w(), rect.h(), + scaleFactor); + xOffset += rect.w() * scaleFactor; } - xOffset += getSpaceWidth(); + xOffset += getSpaceWidth() * scaleFactor; } } /** Ends a render cycle with this {@link TextRenderer TextRenderer}. Restores the projection and modelview matrices as well as - several OpenGL state bits. + several OpenGL state bits. Should be paired with {@link + #beginRendering beginRendering}. @throws GLException If an OpenGL context is not current when this method is called */ public void endRendering() throws GLException { - getBackingStore().endOrthoRendering(); - if (++numRenderCycles >= CYCLES_PER_FLUSH) { - numRenderCycles = 0; - if (DEBUG) { - System.err.println("Clearing unused entries in endRendering()"); - } - clearUnusedEntries(); - } + endRendering(true); + } + + /** Ends a 3D render cycle with this {@link TextRenderer TextRenderer}. + Restores several OpenGL state bits. Should be paired with {@link + #begin3DRendering begin3DRendering}. + + @throws GLException If an OpenGL context is not current when this method is called + */ + public void end3DRendering() throws GLException { + endRendering(false); } /** Disposes of all resources this TextRenderer is using. It is not @@ -416,10 +436,13 @@ public class TextRenderer { // private static Rectangle2D normalize(Rectangle2D src) { - return new Rectangle2D.Double((int) Math.floor(src.getMinX()), - (int) Math.floor(src.getMinY()), - (int) Math.ceil(src.getWidth()), - (int) Math.ceil(src.getHeight())); + // Give ourselves a one-pixel boundary around each string in order + // to prevent bleeding of nearby Strings due to the fact that we + // use linear filtering + return new Rectangle2D.Double((int) Math.floor(src.getMinX() - 1), + (int) Math.floor(src.getMinY() - 1), + (int) Math.ceil(src.getWidth() + 2), + (int) Math.ceil(src.getHeight()) + 2); } private TextureRenderer getBackingStore() { @@ -454,6 +477,48 @@ public class TextRenderer { return cachedGraphics; } + private void beginRendering(boolean ortho, int width, int height) { + if (DEBUG && !debugged) { + debug(); + } + + if (ortho) { + getBackingStore().beginOrthoRendering(width, height); + } else { + getBackingStore().begin3DRendering(); + } + GL gl = GLU.getCurrentGL(); + + if (!haveMaxSize) { + // Query OpenGL for the maximum texture size and set it in the + // RectanglePacker to keep it from expanding too large + int[] sz = new int[1]; + gl.glGetIntegerv(GL.GL_MAX_TEXTURE_SIZE, sz, 0); + packer.setMaxSize(sz[0], sz[0]); + haveMaxSize = true; + } + + // Change texture environment mode to MODULATE + gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE); + // Change text color to last saved + gl.glColor4f(r, g, b, a); + } + + private void endRendering(boolean ortho) throws GLException { + if (ortho) { + getBackingStore().endOrthoRendering(); + } else { + getBackingStore().end3DRendering(); + } + if (++numRenderCycles >= CYCLES_PER_FLUSH) { + numRenderCycles = 0; + if (DEBUG) { + System.err.println("Clearing unused entries in endRendering()"); + } + clearUnusedEntries(); + } + } + private int getSpaceWidth() { if (spaceWidth < 0) { Graphics2D g = getGraphics2D(); diff --git a/src/classes/com/sun/opengl/util/j2d/TextureRenderer.java b/src/classes/com/sun/opengl/util/j2d/TextureRenderer.java index 976806db3..3670d3c51 100755 --- a/src/classes/com/sun/opengl/util/j2d/TextureRenderer.java +++ b/src/classes/com/sun/opengl/util/j2d/TextureRenderer.java @@ -274,13 +274,14 @@ public class TextureRenderer { /** Convenience method which assists in rendering portions of the OpenGL texture to the screen, if the application intends to draw - them as a flat overlay on to the screen. Sets up the viewing - matrices, for orthographic rendering where the coordinates go + them as a flat overlay on to the screen. Pushes OpenGL state + bits (GL_ENABLE_BIT, GL_DEPTH_BUFFER_BIT and GL_TRANSFORM_BIT); + disables the depth test, back-face culling, and lighting; + enables the texture in this renderer; and sets up the viewing + matrices for orthographic rendering where the coordinates go from (0, 0) at the lower left to (width, height) at the upper - right; disables the depth test and lighting; and enables the - texture in this renderer. {@link #endOrthoRendering} must be - used in conjunction with this method to restore all OpenGL - states. + right. {@link #endOrthoRendering} must be used in conjunction + with this method to restore all OpenGL states. @param width the width of the current on-screen OpenGL drawable @param height the height of the current on-screen OpenGL drawable @@ -288,37 +289,24 @@ public class TextureRenderer { @throws GLException If an OpenGL context is not current when this method is called */ public void beginOrthoRendering(int width, int height) throws GLException { - GL gl = GLU.getCurrentGL(); - gl.glPushAttrib(GL.GL_ENABLE_BIT | GL.GL_DEPTH_BUFFER_BIT | GL.GL_TRANSFORM_BIT); - gl.glDisable(GL.GL_DEPTH_TEST); - gl.glDisable(GL.GL_CULL_FACE); - gl.glDisable(GL.GL_LIGHTING); - gl.glMatrixMode(GL.GL_PROJECTION); - gl.glPushMatrix(); - gl.glLoadIdentity(); - glu.gluOrtho2D(0, width, 0, height); - gl.glMatrixMode(GL.GL_MODELVIEW); - gl.glPushMatrix(); - gl.glLoadIdentity(); - gl.glMatrixMode(GL.GL_TEXTURE); - gl.glPushMatrix(); - gl.glLoadIdentity(); - gl.glEnable(GL.GL_BLEND); - gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA); - Texture texture = getTexture(); - texture.enable(); - texture.bind(); - gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_REPLACE); - if (smoothingChanged) { - smoothingChanged = false; - if (smoothing) { - texture.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); - texture.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); - } else { - texture.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST); - texture.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST); - } - } + beginRendering(true, width, height); + } + + /** Convenience method which assists in rendering portions of the + OpenGL texture to the screen as 2D quads in 3D space. Pushes + OpenGL state (GL_ENABLE_BIT); disables lighting; and enables the + texture in this renderer. Unlike {@link #beginOrthoRendering + beginOrthoRendering}, does not modify the depth test, back-face + culling, lighting, or the modelview or projection matrices. The + user is responsible for setting up the view matrices for correct + results of {@link #draw3DRect draw3DRect}. {@link + #end3DRendering} must be used in conjunction with this method to + restore all OpenGL states. + + @throws GLException If an OpenGL context is not current when this method is called + */ + public void begin3DRendering() throws GLException { + beginRendering(false, 0, 0); } /** Draws an orthographically projected rectangle containing all of @@ -360,6 +348,38 @@ public class TextureRenderer { public void drawOrthoRect(int screenx, int screeny, int texturex, int texturey, int width, int height) throws GLException { + draw3DRect(screenx, screeny, 0, texturex, texturey, width, height, 1); + } + + /** Draws a rectangle of the underlying texture to the specified 3D + location. In the current coordinate system, the lower left + corner of the rectangle is placed at (x, y, z), and the upper + right corner is placed at (x + width * scaleFactor, y + height * + scaleFactor, z). The lower left corner of the sub-rectangle of + the texture is (texturex, texturey) and the upper right corner + is (texturex + width, texturey + height). For back-face culling + purposes, the rectangle is drawn with counterclockwise + orientation of the vertices when viewed from the front. + + @param x the x coordinate at which to draw the rectangle + @param y the y coordinate at which to draw the rectangle + @param z the z coordinate at which to draw the rectangle + @param texturex the x coordinate of the pixel in the texture of + the lower left portion of the rectangle to draw + @param texturey the y coordinate of the pixel in the texture + (relative to lower left) of the lower left portion of the + rectangle to draw + @param width the width in texels of the rectangle to draw + @param height the height in texels of the rectangle to draw + @param scaleFactor the scale factor to apply (multiplicatively) + to the size of the drawn rectangle + + @throws GLException If an OpenGL context is not current when this method is called + */ + public void draw3DRect(float x, float y, float z, + int texturex, int texturey, + int width, int height, + float scaleFactor) throws GLException { GL gl = GLU.getCurrentGL(); Texture texture = getTexture(); TextureCoords coords = texture.getSubImageTexCoords(texturex, texturey, @@ -367,13 +387,13 @@ public class TextureRenderer { texturey + height); gl.glBegin(GL.GL_QUADS); gl.glTexCoord2f(coords.left(), coords.bottom()); - gl.glVertex3f(screenx, screeny, 0); + gl.glVertex3f(x, y, z); gl.glTexCoord2f(coords.right(), coords.bottom()); - gl.glVertex3f(screenx + width, screeny, 0); + gl.glVertex3f(x + width * scaleFactor, y, z); gl.glTexCoord2f(coords.right(), coords.top()); - gl.glVertex3f(screenx + width, screeny + height, 0); + gl.glVertex3f(x + width * scaleFactor, y + height * scaleFactor, z); gl.glTexCoord2f(coords.left(), coords.top()); - gl.glVertex3f(screenx, screeny + height, 0); + gl.glVertex3f(x, y + height * scaleFactor, z); gl.glEnd(); } @@ -386,22 +406,77 @@ public class TextureRenderer { @throws GLException If an OpenGL context is not current when this method is called */ public void endOrthoRendering() throws GLException { - GL gl = GLU.getCurrentGL(); - Texture texture = getTexture(); - texture.disable(); - gl.glMatrixMode(GL.GL_PROJECTION); - gl.glPopMatrix(); - gl.glMatrixMode(GL.GL_MODELVIEW); - gl.glPopMatrix(); - gl.glMatrixMode(GL.GL_TEXTURE); - gl.glPopMatrix(); - gl.glPopAttrib(); + endRendering(true); + } + + /** Convenience method which assists in rendering portions of the + OpenGL texture to the screen as 2D quads in 3D space. Must be + used if {@link #begin3DRendering} is used to set up the + rendering stage for this overlay. + + @throws GLException If an OpenGL context is not current when this method is called + */ + public void end3DRendering() throws GLException { + endRendering(false); } //---------------------------------------------------------------------- // Internals only below this point // + private void beginRendering(boolean ortho, int width, int height) { + GL gl = GLU.getCurrentGL(); + int attribBits = + GL.GL_ENABLE_BIT | (ortho ? (GL.GL_DEPTH_BUFFER_BIT | GL.GL_TRANSFORM_BIT) : 0); + gl.glPushAttrib(attribBits); + gl.glDisable(GL.GL_LIGHTING); + if (ortho) { + gl.glDisable(GL.GL_DEPTH_TEST); + gl.glDisable(GL.GL_CULL_FACE); + gl.glMatrixMode(GL.GL_PROJECTION); + gl.glPushMatrix(); + gl.glLoadIdentity(); + glu.gluOrtho2D(0, width, 0, height); + gl.glMatrixMode(GL.GL_MODELVIEW); + gl.glPushMatrix(); + gl.glLoadIdentity(); + gl.glMatrixMode(GL.GL_TEXTURE); + gl.glPushMatrix(); + gl.glLoadIdentity(); + } + gl.glEnable(GL.GL_BLEND); + gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA); + Texture texture = getTexture(); + texture.enable(); + texture.bind(); + gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_REPLACE); + if (smoothingChanged) { + smoothingChanged = false; + if (smoothing) { + texture.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); + texture.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); + } else { + texture.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST); + texture.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST); + } + } + } + + private void endRendering(boolean ortho) { + GL gl = GLU.getCurrentGL(); + Texture texture = getTexture(); + texture.disable(); + if (ortho) { + gl.glMatrixMode(GL.GL_PROJECTION); + gl.glPopMatrix(); + gl.glMatrixMode(GL.GL_MODELVIEW); + gl.glPopMatrix(); + gl.glMatrixMode(GL.GL_TEXTURE); + gl.glPopMatrix(); + } + gl.glPopAttrib(); + } + private void init(int width, int height) { // Discard previous BufferedImage if any if (image != null) { -- cgit v1.2.3