summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2013-09-03 16:28:32 +0200
committerSven Gothel <[email protected]>2013-09-03 16:28:32 +0200
commitc8b0516472dec8b76cc7c3a3f71683ffe1410a3a (patch)
treee18cc7f7b161cb33d25e62700d3b12b58eae2115
parente3a5868b189b4979a8a85746b1ae3b880ed8f8f0 (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
-rw-r--r--src/jogl/classes/com/jogamp/opengl/util/TileRenderer.java447
-rw-r--r--src/jogl/classes/com/jogamp/opengl/util/TileRenderer2.java129
-rw-r--r--src/jogl/classes/com/jogamp/opengl/util/gl2/TileRenderer.java601
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/TestTiledRendering1GL2.java172
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/demos/gl2/TestTiledRendering2GL2.java135
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?)");
+ }
+ }
+ }
+}