diff options
author | Sven Gothel <[email protected]> | 2013-09-03 16:28:32 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2013-09-03 16:28:32 +0200 |
commit | c8b0516472dec8b76cc7c3a3f71683ffe1410a3a (patch) | |
tree | e18cc7f7b161cb33d25e62700d3b12b58eae2115 | |
parent | e3a5868b189b4979a8a85746b1ae3b880ed8f8f0 (diff) |
Cleaned up TiledRenderer capable to be used w/ GL2ES3 and TiledRenderer2 to be used w/ GLAutoDrawable.
- Remove GL2 dependencies
- Only requires PixelStorage ROW_LENGTH -> GL2ES3
- Position target buffer position according to skip [pixels, rows]
- Use an interface PMVMatrixCallback, allowing user to reshape
the custom 'PMV Matrix' according to the current rendered tile
- Properly adjust tile/image buffer to written position and flip for read operation
5 files changed, 883 insertions, 601 deletions
diff --git a/src/jogl/classes/com/jogamp/opengl/util/TileRenderer.java b/src/jogl/classes/com/jogamp/opengl/util/TileRenderer.java new file mode 100644 index 000000000..afd023224 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/TileRenderer.java @@ -0,0 +1,447 @@ +package com.jogamp.opengl.util; + +import java.nio.ByteBuffer; + +import javax.media.nativewindow.util.Dimension; +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES3; + +/** + * A fairly direct port of Brian Paul's tile rendering library, found + * at <a href = "http://www.mesa3d.org/brianp/TR.html"> + * http://www.mesa3d.org/brianp/TR.html </a> . I've java-fied it, but + * the functionality is the same. + * <p> + * Original code Copyright (C) 1997-2005 Brian Paul. Licensed under + * BSD-compatible terms with permission of the author. See LICENSE.txt + * for license information. + * </p> + * <p> + * Enhanced for {@link GL2ES3}. + * </p> + * + * @author ryanm, sgothel + */ +public class TileRenderer { + + protected static final boolean DEBUG = true; + protected static final int DEFAULT_TILE_WIDTH = 256; + protected static final int DEFAULT_TILE_HEIGHT = 256; + protected static final int DEFAULT_TILE_BORDER = 0; + + /** + * The width of a tile + */ + public static final int TR_TILE_WIDTH = 0; + /** + * The height of a tile + */ + public static final int TR_TILE_HEIGHT = 1; + /** + * The width of the border around the tiles + */ + public static final int TR_TILE_BORDER = 2; + /** + * The width of the final image + */ + public static final int TR_IMAGE_WIDTH = 3; + /** + * The height of the final image + */ + public static final int TR_IMAGE_HEIGHT = 4; + /** + * The number of rows of tiles + */ + public static final int TR_ROWS = 5; + /** + * The number of columns of tiles + */ + public static final int TR_COLUMNS = 6; + /** + * The current row number + */ + public static final int TR_CURRENT_ROW = 7; + /** + * The current column number + */ + public static final int TR_CURRENT_COLUMN = 8; + /** + * The width of the current tile + */ + public static final int TR_CURRENT_TILE_WIDTH = 9; + /** + * The height of the current tile + */ + public static final int TR_CURRENT_TILE_HEIGHT = 10; + /** + * The order that the rows are traversed + */ + public static final int TR_ROW_ORDER = 11; + /** + * Indicates we are traversing rows from the top to the bottom + */ + public static final int TR_TOP_TO_BOTTOM = 1; + /** + * Indicates we are traversing rows from the bottom to the top + */ + public static final int TR_BOTTOM_TO_TOP = 2; + + protected final Dimension imageSize = new Dimension(0, 0); + protected final Dimension tileSize = new Dimension(DEFAULT_TILE_WIDTH, DEFAULT_TILE_HEIGHT); + protected final Dimension tileSizeNB = new Dimension(DEFAULT_TILE_WIDTH - 2 * DEFAULT_TILE_BORDER, DEFAULT_TILE_HEIGHT - 2 * DEFAULT_TILE_BORDER); + protected final int[] userViewport = new int[ 4 ]; + protected final GLPixelStorageModes psm = new GLPixelStorageModes(); + + protected int tileBorder = DEFAULT_TILE_BORDER; + protected int imageFormat; + protected int imageType; + protected ByteBuffer imageBuffer; + protected int tileFormat; + protected int tileType; + protected ByteBuffer tileBuffer; + protected int rowOrder = TR_BOTTOM_TO_TOP; + protected int rows; + protected int columns; + protected int currentTile = -1; + protected int currentTileWidth; + protected int currentTileHeight; + protected int currentRow; + protected int currentColumn; + protected PMVMatrixCallback pmvMatrixCB = null; + + public static interface PMVMatrixCallback { + void reshapePMVMatrix(GL gl, int tileNum, int tileColumn, int tileRow, int tileX, int tileY, int tileWidth, int tileHeight, int imageWidth, int imageHeight); + } + + /** + * Creates a new TileRenderer object + */ + public TileRenderer() { + } + + /** + * Sets the size of the tiles to use in rendering. The actual + * effective size of the tile depends on the border size, ie ( + * width - 2*border ) * ( height - 2 * border ) + * + * @param width + * The width of the tiles. Must not be larger than the GL + * context + * @param height + * The height of the tiles. Must not be larger than the + * GL context + * @param border + * The width of the borders on each tile. This is needed + * to avoid artifacts when rendering lines or points with + * thickness > 1. + */ + public final void setTileSize(int width, int height, int border) { + assert ( border >= 0 ); + assert ( width >= 1 ); + assert ( height >= 1 ); + assert ( width >= 2 * border ); + assert ( height >= 2 * border ); + + tileBorder = border; + tileSize.setWidth( width ); + tileSize.setHeight( height ); + tileSizeNB.setWidth( width - 2 * border ); + tileSizeNB.setHeight( height - 2 * border ); + setup(); + } + + public final void setPMVMatrixCallback(PMVMatrixCallback pmvMatrixCB) { + assert ( null != pmvMatrixCB ); + this.pmvMatrixCB = pmvMatrixCB; + } + + /** + * Sets up the number of rows and columns needed + */ + protected final void setup() throws IllegalStateException { + columns = ( imageSize.getWidth() + tileSizeNB.getWidth() - 1 ) / tileSizeNB.getWidth(); + rows = ( imageSize.getHeight() + tileSizeNB.getHeight() - 1 ) / tileSizeNB.getHeight(); + currentTile = 0; + currentTileWidth = 0; + currentTileHeight = 0; + currentRow = 0; + currentColumn = 0; + + assert columns >= 0; + assert rows >= 0; + } + + /** + * Returns <code>true</code> if all tiles have been rendered or {@link #setup()} + * has not been called, otherwise <code>false</code>. + */ + public final boolean eot() { return 0 > currentTile; } + + /** + * Specify a buffer the tiles to be copied to. This is not + * necessary for the creation of the final image, but useful if you + * want to inspect each tile in turn. + * + * @param format + * Interpreted as in glReadPixels + * @param type + * Interpreted as in glReadPixels + * @param buffer + * The buffer itself. Must be large enough to contain a + * tile, minus any borders + */ + public final void setTileBuffer(int format, int type, ByteBuffer buffer) { + tileFormat = format; + tileType = type; + tileBuffer = buffer; + } + + /** + * Sets the desired size of the final image + * + * @param width + * The width of the final image + * @param height + * The height of the final image + */ + public final void setImageSize(int width, int height) { + imageSize.setWidth(width); + imageSize.setHeight(height); + setup(); + } + + /** + * Sets the buffer in which to store the final image + * + * @param format + * Interpreted as in glReadPixels + * @param type + * Interpreted as in glReadPixels + * @param image + * the buffer itself, must be large enough to hold the + * final image + */ + public final void setImageBuffer(int format, int type, ByteBuffer buffer) { + imageFormat = format; + imageType = type; + imageBuffer = buffer; + } + + /** + * Gets the parameters of this TileRenderer object + * + * @param param + * The parameter that is to be retrieved + * @return the value of the parameter + */ + public final int getParam(int param) { + switch (param) { + case TR_TILE_WIDTH: + return tileSize.getWidth(); + case TR_TILE_HEIGHT: + return tileSize.getHeight(); + case TR_TILE_BORDER: + return tileBorder; + case TR_IMAGE_WIDTH: + return imageSize.getWidth(); + case TR_IMAGE_HEIGHT: + return imageSize.getHeight(); + case TR_ROWS: + return rows; + case TR_COLUMNS: + return columns; + case TR_CURRENT_ROW: + if( currentTile < 0 ) + return -1; + else + return currentRow; + case TR_CURRENT_COLUMN: + if( currentTile < 0 ) + return -1; + else + return currentColumn; + case TR_CURRENT_TILE_WIDTH: + return currentTileWidth; + case TR_CURRENT_TILE_HEIGHT: + return currentTileHeight; + case TR_ROW_ORDER: + return rowOrder; + default: + throw new IllegalArgumentException("Invalid enumerant as argument"); + } + } + + /** + * Sets the order of row traversal + * + * @param order + * The row traversal order, must be + * eitherTR_TOP_TO_BOTTOM or TR_BOTTOM_TO_TOP + */ + public final void setRowOrder(int order) { + if (order == TR_TOP_TO_BOTTOM || order == TR_BOTTOM_TO_TOP) { + rowOrder = order; + } else { + throw new IllegalArgumentException("Must pass TR_TOP_TO_BOTTOM or TR_BOTTOM_TO_TOP"); + } + } + + /** + * Begins rendering a tile. + * <p> + * The projection matrix stack should be + * left alone after calling this method! + * </p> + * + * @param gl The gl context + */ + public final void beginTile( GL2ES3 gl ) { + if( 0 >= imageSize.getWidth() || 0 >= imageSize.getHeight() ) { + throw new IllegalStateException("Image size has not been set"); + } + if( null == this.pmvMatrixCB ) { + throw new IllegalStateException("pmvMatrixCB has not been set"); + } + if (currentTile <= 0) { + setup(); + /* + * Save user's viewport, will be restored after last tile + * rendered + */ + gl.glGetIntegerv( GL.GL_VIEWPORT, userViewport, 0 ); + } + + final int preRow = currentRow; + final int preColumn = currentColumn; + + /* which tile (by row and column) we're about to render */ + if (rowOrder == TR_BOTTOM_TO_TOP) { + currentRow = currentTile / columns; + currentColumn = currentTile % columns; + } else { + currentRow = rows - ( currentTile / columns ) - 1; + currentColumn = currentTile % columns; + } + assert ( currentRow < rows ); + assert ( currentColumn < columns ); + + int border = tileBorder; + + int tH, tW; + + /* Compute actual size of this tile with border */ + if (currentRow < rows - 1) { + tH = tileSize.getHeight(); + } else { + tH = imageSize.getHeight() - ( rows - 1 ) * ( tileSizeNB.getHeight() ) + 2 * border; + } + + if (currentColumn < columns - 1) { + tW = tileSize.getWidth(); + } else { + tW = imageSize.getWidth() - ( columns - 1 ) * ( tileSizeNB.getWidth() ) + 2 * border; + } + + final int tX = currentColumn * tileSizeNB.getWidth() - border; + final int tY = currentRow * tileSizeNB.getHeight() - border; + + final int preTileWidth = currentTileWidth; + final int preTileHeight = currentTileHeight; + + /* Save tile size, with border */ + currentTileWidth = tW; + currentTileHeight = tH; + + if( DEBUG ) { + System.err.println("Tile["+currentTile+"]: ["+preColumn+"]["+preRow+"] "+preTileWidth+"x"+preTileHeight+ + " -> ["+currentColumn+"]["+currentRow+"] "+tX+"/"+tY+", "+tW+"x"+tH+", image "+imageSize.getWidth()+"x"+imageSize.getHeight()); + } + + gl.glViewport( 0, 0, tW, tH ); + pmvMatrixCB.reshapePMVMatrix(gl, currentTile, currentColumn, currentRow, tX, tY, tW, tH, imageSize.getWidth(), imageSize.getHeight()); + } + + /** + * Must be called after rendering the scene + * + * @param gl + * the gl context + * @return true if there are more tiles to be rendered, false if + * the final image is complete + */ + public boolean endTile( GL2ES3 gl ) { + assert ( currentTile >= 0 ); + + // be sure OpenGL rendering is finished + gl.glFlush(); + + // save current glPixelStore values + psm.save(gl); + + final int tmp[] = new int[1]; + + if( tileBuffer != null ) { + int srcX = tileBorder; + int srcY = tileBorder; + int srcWidth = tileSizeNB.getWidth(); + int srcHeight = tileSizeNB.getHeight(); + final int bytesPerPixel = GLBuffers.bytesPerPixel(tileFormat, tileType); + final int readPixelSize = GLBuffers.sizeof(gl, tmp, bytesPerPixel, srcWidth, srcHeight, 1, true); + tileBuffer.clear(); + if( tileBuffer.limit() < readPixelSize ) { + throw new IndexOutOfBoundsException("Required " + readPixelSize + " bytes of buffer, only had " + tileBuffer.limit()); + } + gl.glReadPixels( srcX, srcY, srcWidth, srcHeight, tileFormat, tileType, tileBuffer ); + // be sure OpenGL rendering is finished + gl.glFlush(); + tileBuffer.position( readPixelSize ); + tileBuffer.flip(); + } + + if( imageBuffer != null ) { + int srcX = tileBorder; + int srcY = tileBorder; + int srcWidth = currentTileWidth - 2 * tileBorder; + int srcHeight = currentTileHeight - 2 * tileBorder; + + /* setup pixel store for glReadPixels */ + final int rowLength = imageSize.getWidth(); + psm.setPackRowLength(gl, rowLength); + psm.setPackAlignment(gl, 1); + + /* read the tile into the final image */ + final int bytesPerPixel = GLBuffers.bytesPerPixel(imageFormat, imageType); + final int readPixelSize = GLBuffers.sizeof(gl, tmp, bytesPerPixel, srcWidth, srcHeight, 1, true); + + final int skipPixels = tileSizeNB.getWidth() * currentColumn; + final int skipRows = tileSizeNB.getHeight() * currentRow; + final int ibPos = ( skipPixels + ( skipRows * rowLength ) ) * bytesPerPixel; + final int ibLim = ibPos + readPixelSize; + imageBuffer.clear(); + if( imageBuffer.limit() < ibLim ) { + throw new IndexOutOfBoundsException("Required " + ibLim + " bytes of buffer, only had " + imageBuffer.limit()); + } + imageBuffer.position(ibPos); + + gl.glReadPixels( srcX, srcY, srcWidth, srcHeight, imageFormat, imageType, imageBuffer); + // be sure OpenGL rendering is finished + gl.glFlush(); + imageBuffer.position( ibLim ); + imageBuffer.flip(); + } + + /* restore previous glPixelStore values */ + psm.restore(gl); + + /* increment tile counter, return 1 if more tiles left to render */ + currentTile++; + if( currentTile >= rows * columns ) { + /* restore user's viewport */ + gl.glViewport( userViewport[ 0 ], userViewport[ 1 ], userViewport[ 2 ], userViewport[ 3 ] ); + currentTile = -1; /* all done */ + return false; + } else { + return true; + } + } +}
\ No newline at end of file diff --git a/src/jogl/classes/com/jogamp/opengl/util/TileRenderer2.java b/src/jogl/classes/com/jogamp/opengl/util/TileRenderer2.java new file mode 100644 index 000000000..a77456889 --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/TileRenderer2.java @@ -0,0 +1,129 @@ +package com.jogamp.opengl.util; + +import javax.media.opengl.GL2ES3; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLEventListener; + +/** + * See {@link TileRenderer}. + * <p> + * Enhanced for {@link GLAutoDrawable} usage. + * </p> + */ +public class TileRenderer2 extends TileRenderer { + private GLAutoDrawable glad; + private GLEventListener[] listeners; + private boolean[] listenersInit; + + /** + * Creates a new TileRenderer object + */ + public TileRenderer2() { + glad = null; + listeners = null; + listenersInit = null; + } + + /** + * + * <p> + * Sets the size of the tiles to use in rendering. The actual + * effective size of the tile depends on the border size, ie ( + * width - 2*border ) * ( height - 2 * border ) + * </p> + * @param glad + * @param border + * The width of the borders on each tile. This is needed + * to avoid artifacts when rendering lines or points with + * thickness > 1. + * @throws IllegalStateException if an {@link GLAutoDrawable} is already attached + */ + public void attachAutoDrawable(GLAutoDrawable glad, int border, PMVMatrixCallback pmvMatrixCB) throws IllegalStateException { + if( null != this.glad ) { + throw new IllegalStateException("GLAutoDrawable already attached"); + } + this.glad = glad; + setTileSize(glad.getWidth(), glad.getHeight(), border); + setPMVMatrixCallback(pmvMatrixCB); + + final int aSz = glad.getGLEventListenerCount(); + listeners = new GLEventListener[aSz]; + listenersInit = new boolean[aSz]; + for(int i=0; i<aSz; i++) { + final GLEventListener l = glad.getGLEventListener(0); + listenersInit[i] = glad.getGLEventListenerInitState(l); + listeners[i] = glad.removeGLEventListener( l ); + } + glad.addGLEventListener(tiledGLEL); + } + + public void detachAutoDrawable() { + if( null != glad ) { + glad.removeGLEventListener(tiledGLEL); + final int aSz = listenersInit.length; + for(int i=0; i<aSz; i++) { + final GLEventListener l = listeners[i]; + glad.addGLEventListener(l); + glad.setGLEventListenerInitState(l, listenersInit[i]); + } + listeners = null; + listenersInit = null; + glad = null; + pmvMatrixCB = null; + } + } + + /** + * Rendering one tile, by simply calling {@link GLAutoDrawable#display()}. + * + * @return true if there are more tiles to be rendered, false if the final image is complete + * @throws IllegalStateException if no {@link GLAutoDrawable} is {@link #attachAutoDrawable(GLAutoDrawable, int) attached} + * or imageSize is not set + */ + public boolean display() throws IllegalStateException { + if( null == glad ) { + throw new IllegalStateException("No GLAutoDrawable attached"); + } + glad.display(); + return !eot(); + } + + private final GLEventListener tiledGLEL = new GLEventListener() { + @Override + public void init(GLAutoDrawable drawable) { + final int aSz = listenersInit.length; + for(int i=0; i<aSz; i++) { + final GLEventListener l = listeners[i]; + l.init(drawable); + listenersInit[i] = true; + } + } + @Override + public void dispose(GLAutoDrawable drawable) { + final int aSz = listenersInit.length; + for(int i=0; i<aSz; i++) { + listeners[i].dispose(drawable); + } + } + @Override + public void display(GLAutoDrawable drawable) { + final GL2ES3 gl = drawable.getGL().getGL2ES3(); + + beginTile(gl); + + final int aSz = listenersInit.length; + for(int i=0; i<aSz; i++) { + listeners[i].display(drawable); + } + + endTile(gl); + } + @Override + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { + final int aSz = listenersInit.length; + for(int i=0; i<aSz; i++) { + listeners[i].reshape(drawable, x, y, width, height); + } + } + }; +} diff --git a/src/jogl/classes/com/jogamp/opengl/util/gl2/TileRenderer.java b/src/jogl/classes/com/jogamp/opengl/util/gl2/TileRenderer.java deleted file mode 100644 index 714c134d4..000000000 --- a/src/jogl/classes/com/jogamp/opengl/util/gl2/TileRenderer.java +++ /dev/null @@ -1,601 +0,0 @@ -package com.jogamp.opengl.util.gl2; - -import java.awt.Dimension; -import java.nio.Buffer; - -import javax.media.opengl.*; -import javax.media.opengl.glu.*; -import javax.media.opengl.glu.gl2.*; - -/** - * A fairly direct port of Brian Paul's tile rendering library, found - * at <a href = "http://www.mesa3d.org/brianp/TR.html"> - * http://www.mesa3d.org/brianp/TR.html </a> . I've java-fied it, but - * the functionality is the same. - * - * Original code Copyright (C) 1997-2005 Brian Paul. Licensed under - * BSD-compatible terms with permission of the author. See LICENSE.txt - * for license information. - * - * @author ryanm - */ -public class TileRenderer -{ - private static final int DEFAULT_TILE_WIDTH = 256; - - private static final int DEFAULT_TILE_HEIGHT = 256; - - private static final int DEFAULT_TILE_BORDER = 0; - - // - // Enumeration flags for accessing variables - // - // @author ryanm - // - - /** - * The width of a tile - */ - public static final int TR_TILE_WIDTH = 0; - /** - * The height of a tile - */ - public static final int TR_TILE_HEIGHT = 1; - /** - * The width of the border around the tiles - */ - public static final int TR_TILE_BORDER = 2; - /** - * The width of the final image - */ - public static final int TR_IMAGE_WIDTH = 3; - /** - * The height of the final image - */ - public static final int TR_IMAGE_HEIGHT = 4; - /** - * The number of rows of tiles - */ - public static final int TR_ROWS = 5; - /** - * The number of columns of tiles - */ - public static final int TR_COLUMNS = 6; - /** - * The current row number - */ - public static final int TR_CURRENT_ROW = 7; - /** - * The current column number - */ - public static final int TR_CURRENT_COLUMN = 8; - /** - * The width of the current tile - */ - public static final int TR_CURRENT_TILE_WIDTH = 9; - /** - * The height of the current tile - */ - public static final int TR_CURRENT_TILE_HEIGHT = 10; - /** - * The order that the rows are traversed - */ - public static final int TR_ROW_ORDER = 11; - - - /** - * Indicates we are traversing rows from the top to the bottom - */ - public static final int TR_TOP_TO_BOTTOM = 1; - - /** - * Indicates we are traversing rows from the bottom to the top - */ - public static final int TR_BOTTOM_TO_TOP = 2; - - /* Final image parameters */ - private Dimension imageSize = new Dimension(); - - private int imageFormat, imageType; - - private Buffer imageBuffer; - - /* Tile parameters */ - private Dimension tileSize = new Dimension(); - - private Dimension tileSizeNB = new Dimension(); - - private int tileBorder; - - private int tileFormat, tileType; - - private Buffer tileBuffer; - - /* Projection parameters */ - private boolean perspective; - - private double left; - - private double right; - - private double bottom; - - private double top; - - private double near; - - private double far; - - /* Misc */ - private int rowOrder; - - private int rows, columns; - - private int currentTile; - - private int currentTileWidth, currentTileHeight; - - private int currentRow, currentColumn; - - private int[] viewportSave = new int[ 4 ]; - - /** - * Creates a new TileRenderer object - */ - public TileRenderer() - { - tileSize.width = DEFAULT_TILE_WIDTH; - tileSize.height = DEFAULT_TILE_HEIGHT; - tileBorder = DEFAULT_TILE_BORDER; - rowOrder = TR_BOTTOM_TO_TOP; - currentTile = -1; - } - - /** - * Sets up the number of rows and columns needed - */ - private void setup() - { - columns = ( imageSize.width + tileSizeNB.width - 1 ) / tileSizeNB.width; - rows = ( imageSize.height + tileSizeNB.height - 1 ) / tileSizeNB.height; - currentTile = 0; - - assert columns >= 0; - assert rows >= 0; - } - - /** - * Sets the size of the tiles to use in rendering. The actual - * effective size of the tile depends on the border size, ie ( - * width - 2*border ) * ( height - 2 * border ) - * - * @param width - * The width of the tiles. Must not be larger than the GL - * context - * @param height - * The height of the tiles. Must not be larger than the - * GL context - * @param border - * The width of the borders on each tile. This is needed - * to avoid artifacts when rendering lines or points with - * thickness > 1. - */ - public void setTileSize( int width, int height, int border ) - { - assert ( border >= 0 ); - assert ( width >= 1 ); - assert ( height >= 1 ); - assert ( width >= 2 * border ); - assert ( height >= 2 * border ); - - tileBorder = border; - tileSize.width = width; - tileSize.height = height; - tileSizeNB.width = width - 2 * border; - tileSizeNB.height = height - 2 * border; - setup(); - } - - /** - * Specify a buffer the tiles to be copied to. This is not - * necessary for the creation of the final image, but useful if you - * want to inspect each tile in turn. - * - * @param format - * Interpreted as in glReadPixels - * @param type - * Interpreted as in glReadPixels - * @param image - * The buffer itself. Must be large enough to contain a - * tile, minus any borders - */ - public void setTileBuffer( int format, int type, Buffer image ) - { - tileFormat = format; - tileType = type; - tileBuffer = image; - } - - /** - * Sets the desired size of the final image - * - * @param width - * The width of the final image - * @param height - * The height of the final image - */ - public void setImageSize( int width, int height ) - { - imageSize.width = width; - imageSize.height = height; - setup(); - } - - /** - * Sets the buffer in which to store the final image - * - * @param format - * Interpreted as in glReadPixels - * @param type - * Interpreted as in glReadPixels - * @param image - * the buffer itself, must be large enough to hold the - * final image - */ - public void setImageBuffer( int format, int type, Buffer image ) - { - imageFormat = format; - imageType = type; - imageBuffer = image; - } - - /** - * Gets the parameters of this TileRenderer object - * - * @param param - * The parameter that is to be retrieved - * @return the value of the parameter - */ - public int getParam( int param ) - { - switch (param) { - case TR_TILE_WIDTH: - return tileSize.width; - case TR_TILE_HEIGHT: - return tileSize.height; - case TR_TILE_BORDER: - return tileBorder; - case TR_IMAGE_WIDTH: - return imageSize.width; - case TR_IMAGE_HEIGHT: - return imageSize.height; - case TR_ROWS: - return rows; - case TR_COLUMNS: - return columns; - case TR_CURRENT_ROW: - if( currentTile < 0 ) - return -1; - else - return currentRow; - case TR_CURRENT_COLUMN: - if( currentTile < 0 ) - return -1; - else - return currentColumn; - case TR_CURRENT_TILE_WIDTH: - return currentTileWidth; - case TR_CURRENT_TILE_HEIGHT: - return currentTileHeight; - case TR_ROW_ORDER: - return rowOrder; - default: - throw new IllegalArgumentException("Invalid enumerant as argument"); - } - } - - /** - * Sets the order of row traversal - * - * @param order - * The row traversal order, must be - * eitherTR_TOP_TO_BOTTOM or TR_BOTTOM_TO_TOP - */ - public void setRowOrder( int order ) - { - if (order == TR_TOP_TO_BOTTOM || order == TR_BOTTOM_TO_TOP) { - rowOrder = order; - } else { - throw new IllegalArgumentException("Must pass TR_TOP_TO_BOTTOM or TR_BOTTOM_TO_TOP"); - } - } - - /** - * Sets the context to use an orthographic projection. Must be - * called before rendering the first tile - * - * @param left - * As in glOrtho - * @param right - * As in glOrtho - * @param bottom - * As in glOrtho - * @param top - * As in glOrtho - * @param zNear - * As in glOrtho - * @param zFar - * As in glOrtho - */ - public void trOrtho( double left, double right, double bottom, double top, double zNear, - double zFar ) - { - this.perspective = false; - this.left = left; - this.right = right; - this.bottom = bottom; - this.top = top; - this.near = zNear; - this.far = zFar; - } - - /** - * Sets the perspective projection frustrum. Must be called before - * rendering the first tile - * - * @param left - * As in glFrustrum - * @param right - * As in glFrustrum - * @param bottom - * As in glFrustrum - * @param top - * As in glFrustrum - * @param zNear - * As in glFrustrum - * @param zFar - * As in glFrustrum - */ - public void trFrustum( double left, double right, double bottom, double top, double zNear, - double zFar ) - { - this.perspective = true; - this.left = left; - this.right = right; - this.bottom = bottom; - this.top = top; - this.near = zNear; - this.far = zFar; - } - - /** - * Convenient way to specify a perspective projection - * - * @param fovy - * As in gluPerspective - * @param aspect - * As in gluPerspective - * @param zNear - * As in gluPerspective - * @param zFar - * As in gluPerspective - */ - public void trPerspective( double fovy, double aspect, double zNear, double zFar ) - { - double xmin, xmax, ymin, ymax; - ymax = zNear * Math.tan( fovy * 3.14159265 / 360.0 ); - ymin = -ymax; - xmin = ymin * aspect; - xmax = ymax * aspect; - trFrustum( xmin, xmax, ymin, ymax, zNear, zFar ); - } - - /** - * Begins rendering a tile. The projection matrix stack should be - * left alone after calling this - * - * @param gl - * The gl context - */ - public void beginTile( GL2 gl ) - { - if (currentTile <= 0) { - setup(); - /* - * Save user's viewport, will be restored after last tile - * rendered - */ - gl.glGetIntegerv( GL2.GL_VIEWPORT, viewportSave, 0 ); - } - - /* which tile (by row and column) we're about to render */ - if (rowOrder == TR_BOTTOM_TO_TOP) { - currentRow = currentTile / columns; - currentColumn = currentTile % columns; - } else { - currentRow = rows - ( currentTile / columns ) - 1; - currentColumn = currentTile % columns; - } - assert ( currentRow < rows ); - assert ( currentColumn < columns ); - - int border = tileBorder; - - int th, tw; - - /* Compute actual size of this tile with border */ - if (currentRow < rows - 1) { - th = tileSize.height; - } else { - th = imageSize.height - ( rows - 1 ) * ( tileSizeNB.height ) + 2 * border; - } - - if (currentColumn < columns - 1) { - tw = tileSize.width; - } else { - tw = imageSize.width - ( columns - 1 ) * ( tileSizeNB.width ) + 2 * border; - } - - /* Save tile size, with border */ - currentTileWidth = tw; - currentTileHeight = th; - - gl.glViewport( 0, 0, tw, th ); - - /* save current matrix mode */ - int[] matrixMode = new int[ 1 ]; - gl.glGetIntegerv( GL2.GL_MATRIX_MODE, matrixMode, 0 ); - gl.glMatrixMode( GL2.GL_PROJECTION ); - gl.glLoadIdentity(); - - /* compute projection parameters */ - double l = - left + ( right - left ) * ( currentColumn * tileSizeNB.width - border ) - / imageSize.width; - double r = l + ( right - left ) * tw / imageSize.width; - double b = - bottom + ( top - bottom ) * ( currentRow * tileSizeNB.height - border ) - / imageSize.height; - double t = b + ( top - bottom ) * th / imageSize.height; - - if( perspective ) { - gl.glFrustum( l, r, b, t, near, far ); - } else { - gl.glOrtho( l, r, b, t, near, far ); - } - - /* restore user's matrix mode */ - gl.glMatrixMode( matrixMode[ 0 ] ); - } - - /** - * Must be called after rendering the scene - * - * @param gl - * the gl context - * @return true if there are more tiles to be rendered, false if - * the final image is complete - */ - public boolean endTile( GL2 gl ) - { - int[] prevRowLength = new int[ 1 ], prevSkipRows = new int[ 1 ], prevSkipPixels = new int[ 1 ], prevAlignment = - new int[ 1 ]; - - assert ( currentTile >= 0 ); - - // be sure OpenGL rendering is finished - gl.glFlush(); - - // save current glPixelStore values - gl.glGetIntegerv( GL2.GL_PACK_ROW_LENGTH, prevRowLength, 0 ); - gl.glGetIntegerv( GL2.GL_PACK_SKIP_ROWS, prevSkipRows, 0 ); - gl.glGetIntegerv( GL2.GL_PACK_SKIP_PIXELS, prevSkipPixels, 0 ); - gl.glGetIntegerv( GL2.GL_PACK_ALIGNMENT, prevAlignment, 0 ); - - if( tileBuffer != null ) { - int srcX = tileBorder; - int srcY = tileBorder; - int srcWidth = tileSizeNB.width; - int srcHeight = tileSizeNB.height; - gl.glReadPixels( srcX, srcY, srcWidth, srcHeight, tileFormat, tileType, tileBuffer ); - } - - if( imageBuffer != null ) { - int srcX = tileBorder; - int srcY = tileBorder; - int srcWidth = currentTileWidth - 2 * tileBorder; - int srcHeight = currentTileHeight - 2 * tileBorder; - int destX = tileSizeNB.width * currentColumn; - int destY = tileSizeNB.height * currentRow; - - /* setup pixel store for glReadPixels */ - gl.glPixelStorei( GL2.GL_PACK_ROW_LENGTH, imageSize.width ); - gl.glPixelStorei( GL2.GL_PACK_SKIP_ROWS, destY ); - gl.glPixelStorei( GL2.GL_PACK_SKIP_PIXELS, destX ); - gl.glPixelStorei( GL2.GL_PACK_ALIGNMENT, 1 ); - - /* read the tile into the final image */ - gl.glReadPixels( srcX, srcY, srcWidth, srcHeight, imageFormat, imageType, imageBuffer ); - } - - /* restore previous glPixelStore values */ - gl.glPixelStorei( GL2.GL_PACK_ROW_LENGTH, prevRowLength[ 0 ] ); - gl.glPixelStorei( GL2.GL_PACK_SKIP_ROWS, prevSkipRows[ 0 ] ); - gl.glPixelStorei( GL2.GL_PACK_SKIP_PIXELS, prevSkipPixels[ 0 ] ); - gl.glPixelStorei( GL2.GL_PACK_ALIGNMENT, prevAlignment[ 0 ] ); - - /* increment tile counter, return 1 if more tiles left to render */ - currentTile++; - if( currentTile >= rows * columns ) { - /* restore user's viewport */ - gl.glViewport( viewportSave[ 0 ], viewportSave[ 1 ], viewportSave[ 2 ], viewportSave[ 3 ] ); - currentTile = -1; /* all done */ - return false; - } else { - return true; - } - } - - /** - * Tile rendering causes problems with using glRasterPos3f, so you - * should use this replacement instead - * - * @param x - * As in glRasterPos3f - * @param y - * As in glRasterPos3f - * @param z - * As in glRasterPos3f - * @param gl - * The gl context - * @param glu - * A GLUgl2 object - */ - public void trRasterPos3f( float x, float y, float z, GL2 gl, GLUgl2 glu ) - { - if (currentTile < 0) { - /* not doing tile rendering right now. Let OpenGL do this. */ - gl.glRasterPos3f( x, y, z ); - } else { - double[] modelview = new double[ 16 ], proj = new double[ 16 ]; - int[] viewport = new int[ 4 ]; - double[] win = new double[3]; - - /* Get modelview, projection and viewport */ - gl.glGetDoublev( GL2.GL_MODELVIEW_MATRIX, modelview, 0 ); - gl.glGetDoublev( GL2.GL_PROJECTION_MATRIX, proj, 0 ); - viewport[ 0 ] = 0; - viewport[ 1 ] = 0; - viewport[ 2 ] = currentTileWidth; - viewport[ 3 ] = currentTileHeight; - - /* Project object coord to window coordinate */ - if( glu.gluProject( x, y, z, modelview, 0, proj, 0, viewport, 0, win, 0 ) ) { - - /* set raster pos to window coord (0,0) */ - gl.glMatrixMode( GL2.GL_MODELVIEW ); - gl.glPushMatrix(); - gl.glLoadIdentity(); - gl.glMatrixMode( GL2.GL_PROJECTION ); - gl.glPushMatrix(); - gl.glLoadIdentity(); - gl.glOrtho( 0.0, currentTileWidth, 0.0, currentTileHeight, 0.0, 1.0 ); - gl.glRasterPos3d( 0.0, 0.0, -win[ 2 ] ); - - /* - * Now use empty bitmap to adjust raster position to - * (winX,winY) - */ - { - byte[] bitmap = { 0 }; - gl.glBitmap( 1, 1, 0.0f, 0.0f, ( float ) win[ 0 ], ( float ) win[ 1 ], bitmap , 0 ); - } - - /* restore original matrices */ - gl.glPopMatrix(); /* proj */ - gl.glMatrixMode( GL2.GL_MODELVIEW ); - gl.glPopMatrix(); - } - } - } -} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/TestTiledRendering1GL2.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/TestTiledRendering1GL2.java new file mode 100644 index 000000000..05da3d71b --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/TestTiledRendering1GL2.java @@ -0,0 +1,172 @@ +package com.jogamp.opengl.test.junit.jogl.demos.gl2; + +import com.jogamp.common.util.IOUtil; +import com.jogamp.opengl.util.TGAWriter; +import com.jogamp.opengl.util.TileRenderer; +import com.jogamp.opengl.util.awt.ImageUtil; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferByte; +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; +import javax.imageio.ImageIO; +import javax.media.opengl.GL; +import javax.media.opengl.GL2; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLDrawableFactory; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLOffscreenAutoDrawable; + +/** Demonstrates the TileRenderer class by rendering a large version + of the Gears demo to the specified file. */ + +public class TestTiledRendering1GL2 { + + public static void main(String[] args) throws IOException { + + if (args.length != 1) { + System.out.println("Usage: java TiledRendering [output file name]"); + System.out.println("Writes output (a large version of the Gears demo) to"); + System.out.println("the specified file, using either ImageIO or the fast TGA writer"); + System.out.println("depending on the file extension."); + System.exit(1); + } + + String filename = args[0]; + File file = new File(filename); + + GLCapabilities caps = new GLCapabilities(null); + caps.setDoubleBuffered(false); + + if (!GLDrawableFactory.getFactory(caps.getGLProfile()).canCreateGLPbuffer(null, caps.getGLProfile())) { + System.out.println("Demo requires pbuffer support"); + System.exit(1); + } + + // Use a pbuffer for rendering + final GLDrawableFactory factory = GLDrawableFactory.getFactory(caps.getGLProfile()); + final GLOffscreenAutoDrawable glad = factory.createOffscreenAutoDrawable(null, caps, null, 256, 256, null); + + // Fix the image size for now + // final int imageWidth = glad.getWidth() * 16; + // final int imageHeight = glad.getHeight() * 12; + final int imageWidth = glad.getWidth() * 2; + final int imageHeight = glad.getHeight() * 2; + + System.err.println("XXX1: "+imageWidth+"x"+imageHeight); + System.err.println("XXX1: "+glad); + + // Figure out the file format + TGAWriter tga = null; + BufferedImage img = null; + ByteBuffer buf = null; + + if (filename.endsWith(".tga")) { + tga = new TGAWriter(); + tga.open(file, + imageWidth, + imageHeight, + false); + buf = tga.getImageData(); + } else { + img = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_3BYTE_BGR); + buf = ByteBuffer.wrap(((DataBufferByte) img.getRaster().getDataBuffer()).getData()); + } + + // Initialize the tile rendering library + final TileRenderer renderer = new com.jogamp.opengl.util.TileRenderer(); + final TileRenderer.PMVMatrixCallback pmvMatrixCallback = new TileRenderer.PMVMatrixCallback() { + public void reshapePMVMatrix(GL _gl, int tileNum, int tileColumn, int tileRow, int tileX, int tileY, int tileWidth, int tileHeight, int imageWidth, int imageHeight) { + final GL2 gl = _gl.getGL2(); + gl.glMatrixMode( GL2.GL_PROJECTION ); + gl.glLoadIdentity(); + + /* compute projection parameters */ + float left, right, bottom, top; + if( imageHeight > imageWidth ) { + float a = (float)imageHeight / (float)imageWidth; + left = -1.0f; + right = 1.0f; + bottom = -a; + top = a; + } else { + float a = (float)imageWidth / (float)imageHeight; + left = -a; + right = a; + bottom = -1.0f; + top = 1.0f; + } + final float w = right - left; + final float h = top - bottom; + final float l = left + w * tileX / imageWidth; + final float r = l + w * tileWidth / imageWidth; + final float b = bottom + h * tileY / imageHeight; + final float t = b + h * tileHeight / imageHeight; + + final float _w = r - l; + final float _h = t - b; + System.err.println(">> [l "+left+", r "+right+", b "+bottom+", t "+top+"] "+w+"x"+h+" -> [l "+l+", r "+r+", b "+b+", t "+t+"] "+_w+"x"+_h); + gl.glFrustum(l, r, b, t, 5.0f, 60.0f); + + gl.glMatrixMode(GL2.GL_MODELVIEW); + } + }; + + renderer.setTileSize(glad.getWidth(), glad.getHeight(), 0); + renderer.setPMVMatrixCallback(pmvMatrixCallback); + renderer.setImageSize(imageWidth, imageHeight); + renderer.setImageBuffer(GL2.GL_BGR, GL.GL_UNSIGNED_BYTE, buf); + + glad.addGLEventListener( new GLEventListener() { + @Override + public void init(GLAutoDrawable drawable) { + System.err.println("XXX2: "+drawable); + } + @Override + public void dispose(GLAutoDrawable drawable) { + } + @Override + public void display(GLAutoDrawable drawable) { + renderer.beginTile(drawable.getGL().getGL2()); + } + @Override + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { + } + }); + final Gears gears = new Gears(); + gears.setDoRotation(false); + glad.addGLEventListener( gears ); + glad.addGLEventListener( new GLEventListener() { + @Override + public void init(GLAutoDrawable drawable) { + } + @Override + public void dispose(GLAutoDrawable drawable) { + } + @Override + public void display(GLAutoDrawable drawable) { + renderer.endTile(drawable.getGL().getGL2()); + } + @Override + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { + } + }); + + do { + glad.display(); + } while ( !renderer.eot() ); + + glad.destroy(); + + // Close things up and/or write image using ImageIO + if (tga != null) { + tga.close(); + } else { + ImageUtil.flipImageVertically(img); + if (!ImageIO.write(img, IOUtil.getFileSuffix(file), file)) { + System.err.println("Error writing file using ImageIO (unsupported file format?)"); + } + } + } +} diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/TestTiledRendering2GL2.java b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/TestTiledRendering2GL2.java new file mode 100644 index 000000000..2f04f5a0c --- /dev/null +++ b/src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/TestTiledRendering2GL2.java @@ -0,0 +1,135 @@ +package com.jogamp.opengl.test.junit.jogl.demos.gl2; + +import com.jogamp.common.util.IOUtil; +import com.jogamp.opengl.util.TGAWriter; +import com.jogamp.opengl.util.TileRenderer; +import com.jogamp.opengl.util.TileRenderer2; + +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferByte; +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; +import javax.imageio.ImageIO; +import javax.media.opengl.GL; +import javax.media.opengl.GL2; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLDrawableFactory; +import javax.media.opengl.GLOffscreenAutoDrawable; + +/** Demonstrates the TileRenderer class by rendering a large version + of the Gears demo to the specified file. */ + +public class TestTiledRendering2GL2 { + + public static void main(String[] args) throws IOException { + + if (args.length != 1) { + System.out.println("Usage: java TiledRendering [output file name]"); + System.out.println("Writes output (a large version of the Gears demo) to"); + System.out.println("the specified file, using either ImageIO or the fast TGA writer"); + System.out.println("depending on the file extension."); + System.exit(1); + } + + String filename = args[0]; + File file = new File(filename); + + GLCapabilities caps = new GLCapabilities(null); + caps.setDoubleBuffered(false); + + if (!GLDrawableFactory.getFactory(caps.getGLProfile()).canCreateGLPbuffer(null, caps.getGLProfile())) { + System.out.println("Demo requires pbuffer support"); + System.exit(1); + } + + // Use a pbuffer for rendering + final GLDrawableFactory factory = GLDrawableFactory.getFactory(caps.getGLProfile()); + final GLOffscreenAutoDrawable glad = factory.createOffscreenAutoDrawable(null, caps, null, 256, 256, null); + + final Gears gears = new Gears(); + gears.setDoRotation(false); + glad.addGLEventListener( gears ); + + // Fix the image size for now + // final int imageWidth = glad.getWidth() * 16; + // final int imageHeight = glad.getHeight() * 12; + final int imageWidth = glad.getWidth() * 2; + final int imageHeight = glad.getHeight() * 2; + + // Figure out the file format + TGAWriter tga = null; + BufferedImage img = null; + ByteBuffer buf = null; + + if (filename.endsWith(".tga")) { + tga = new TGAWriter(); + tga.open(file, + imageWidth, + imageHeight, + false); + buf = tga.getImageData(); + } else { + img = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_3BYTE_BGR); + buf = ByteBuffer.wrap(((DataBufferByte) img.getRaster().getDataBuffer()).getData()); + } + + // Initialize the tile rendering library + final TileRenderer2 renderer = new TileRenderer2(); + final TileRenderer.PMVMatrixCallback pmvMatrixCallback = new TileRenderer.PMVMatrixCallback() { + public void reshapePMVMatrix(GL _gl, int tileNum, int tileColumn, int tileRow, int tileX, int tileY, int tileWidth, int tileHeight, int imageWidth, int imageHeight) { + final GL2 gl = _gl.getGL2(); + gl.glMatrixMode( GL2.GL_PROJECTION ); + gl.glLoadIdentity(); + + /* compute projection parameters */ + float left, right, bottom, top; + if( imageHeight > imageWidth ) { + float a = (float)imageHeight / (float)imageWidth; + left = -1.0f; + right = 1.0f; + bottom = -a; + top = a; + } else { + float a = (float)imageWidth / (float)imageHeight; + left = -a; + right = a; + bottom = -1.0f; + top = 1.0f; + } + final float w = right - left; + final float h = top - bottom; + final float l = left + w * tileX / imageWidth; + final float r = l + w * tileWidth / imageWidth; + final float b = bottom + h * tileY / imageHeight; + final float t = b + h * tileHeight / imageHeight; + + final float _w = r - l; + final float _h = t - b; + System.err.println(">> [l "+left+", r "+right+", b "+bottom+", t "+top+"] "+w+"x"+h+" -> [l "+l+", r "+r+", b "+b+", t "+t+"] "+_w+"x"+_h); + gl.glFrustum(l, r, b, t, 5.0f, 60.0f); + + gl.glMatrixMode(GL2.GL_MODELVIEW); + } + }; + + renderer.attachAutoDrawable(glad, 0, pmvMatrixCallback); + renderer.setImageSize(imageWidth, imageHeight); + renderer.setImageBuffer(GL2.GL_BGR, GL.GL_UNSIGNED_BYTE, buf); + + while ( renderer.display() ); + + renderer.detachAutoDrawable(); + + glad.destroy(); + + // Close things up and/or write image using ImageIO + if (tga != null) { + tga.close(); + } else { + if (!ImageIO.write(img, IOUtil.getFileSuffix(file), file)) { + System.err.println("Error writing file using ImageIO (unsupported file format?)"); + } + } + } +} |