/src/org/mt4j/util/opengl/GLTexture.java
http://mt4j.googlecode.com/ · Java · 1120 lines · 392 code · 146 blank · 582 comment · 75 complexity · f8c5de85f85f3b1c244a01286d6a0ea3 MD5 · raw file
- /***********************************************************************
- * mt4j Copyright (c) 2008 - 2010 Christopher Ruff, Fraunhofer-Gesellschaft All rights reserved.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- ***********************************************************************/
- package org.mt4j.util.opengl;
-
- import java.nio.IntBuffer;
-
- import javax.media.opengl.GL;
- import javax.media.opengl.glu.GLU;
-
- import org.mt4j.MTApplication;
- import org.mt4j.util.math.Tools3D;
- import org.mt4j.util.math.ToolsMath;
-
- import processing.core.PApplet;
- import processing.core.PConstants;
- import processing.core.PImage;
- import processing.opengl.PGraphicsOpenGL;
-
- import com.sun.opengl.util.BufferUtil;
-
- /**
- * This class can only be used in combination with a OpenGL renderer.
- * It holds a texture which can be used by processing and OpenGL. It allows to load, configure and update the OpenGL texture object as well
- * as Processing's PImage superclass.
- * If the texture isnt neeeded anymore, the destroy() method has to be called.
- *
- * @author Christopher Ruff
- */
- public class GLTexture extends PImage {
-
- public enum WRAP_MODE{
- REPEAT(GL.GL_REPEAT),
- CLAMP(GL.GL_CLAMP),
- CLAMP_TO_EDGE(GL.GL_CLAMP_TO_EDGE),
- CLAMP_TO_BORDER(GL.GL_CLAMP_TO_BORDER);
-
- private int glConstant;
- private WRAP_MODE(int glConstant) {
- this.glConstant = glConstant;
- }
- public int getGLConstant(){
- return glConstant;
- }
- }
-
- public enum SHRINKAGE_FILTER{
- /**
- * Nearest neighbor interpolation is the fastest and crudest filtering
- * method - it simply uses the color of the texel closest to the pixel
- * center for the pixel color. While fast, this results in aliasing and
- * shimmering during minification. (GL equivalent: GL_NEAREST)
- */
- NearestNeighborNoMipMaps(GL.GL_NEAREST, false),
-
- /**
- * In this method the four nearest texels to the pixel center are
- * sampled (at texture level 0), and their colors are combined by
- * weighted averages. Though smoother, without mipmaps it suffers the
- * same aliasing and shimmering problems as nearest
- * NearestNeighborNoMipMaps. (GL equivalent: GL_LINEAR)
- */
- BilinearNoMipMaps(GL.GL_LINEAR, false),
-
- /**
- * Same as NearestNeighborNoMipMaps except that instead of using samples
- * from texture level 0, the closest mipmap level is chosen based on
- * distance. This reduces the aliasing and shimmering significantly, but
- * does not help with blockiness. (GL equivalent: GL_NEAREST_MIPMAP_NEAREST)
- */
- NearestNeighborNearestMipMap(GL.GL_NEAREST_MIPMAP_NEAREST, true),
-
-
- /**
- * Same as BilinearNoMipMaps except that instead of using samples from
- * texture level 0, the closest mipmap level is chosen based on
- * distance. By using mipmapping we avoid the aliasing and shimmering
- * problems of BilinearNoMipMaps. (GL equivalent: GL_LINEAR_MIPMAP_NEAREST)
- */
- BilinearNearestMipMap(GL.GL_LINEAR_MIPMAP_NEAREST, true),
-
- /**
- * Similar to NearestNeighborNoMipMaps except that instead of using
- * samples from texture level 0, a sample is chosen from each of the
- * closest (by distance) two mipmap levels. A weighted average of these
- * two samples is returned. (GL equivalent: GL_NEAREST_MIPMAP_LINEAR)
- */
- NearestNeighborLinearMipMap(GL.GL_NEAREST_MIPMAP_LINEAR, true),
-
- /**
- * Trilinear filtering is a remedy to a common artifact seen in
- * mipmapped bilinearly filtered images: an abrupt and very noticeable
- * change in quality at boundaries where the renderer switches from one
- * mipmap level to the next. Trilinear filtering solves this by doing a
- * texture lookup and bilinear filtering on the two closest mipmap
- * levels (one higher and one lower quality), and then linearly
- * interpolating the results. This results in a smooth degradation of
- * texture quality as distance from the viewer increases, rather than a
- * series of sudden drops. Of course, closer than Level 0 there is only
- * one mipmap level available, and the algorithm reverts to bilinear
- * filtering (GL equivalent: GL_LINEAR_MIPMAP_LINEAR)
- */
- Trilinear(GL.GL_LINEAR_MIPMAP_LINEAR, true);
-
- private boolean usesMipMapLevels;
-
- private int glConstant;
-
- private SHRINKAGE_FILTER(int glConstant, boolean usesMipMapLevels) {
- this.usesMipMapLevels = usesMipMapLevels;
- this.glConstant = glConstant;
- }
- public int getGLConstant(){
- return glConstant;
- }
- public boolean usesMipMapLevels() {
- return usesMipMapLevels;
- }
- }
-
- public enum EXPANSION_FILTER{
- /**
- * Nearest neighbor interpolation is the fastest and crudest filtering
- * mode - it simply uses the color of the texel closest to the pixel
- * center for the pixel color. While fast, this results in texture
- * 'blockiness' during magnification. (GL equivalent: GL_NEAREST)
- */
- NearestNeighbor(GL.GL_NEAREST),
-
- /**
- * In this mode the four nearest texels to the pixel center are sampled
- * (at the closest mipmap level), and their colors are combined by
- * weighted average according to distance. This removes the 'blockiness'
- * seen during magnification, as there is now a smooth gradient of color
- * change from one texel to the next, instead of an abrupt jump as the
- * pixel center crosses the texel boundary. (GL equivalent: GL_LINEAR)
- */
- Bilinear(GL.GL_LINEAR);
-
- private int glConstant;
- private EXPANSION_FILTER(int glConstant) {
- this.glConstant = glConstant;
- }
- public int getGLConstant(){
- return glConstant;
- }
- }
-
- public enum TEXTURE_TARGET{
- TEXTURE_1D(GL.GL_TEXTURE_1D),
-
- TEXTURE_2D(GL.GL_TEXTURE_2D),
-
- RECTANGULAR(GL.GL_TEXTURE_RECTANGLE_ARB);
-
- private int glConstant;
- private TEXTURE_TARGET(int glConstant) {
- this.glConstant = glConstant;
- }
- public int getGLConstant(){
- return glConstant;
- }
- }
-
- /*
- //FIXME obsolete? how to deal with the PImage.format and the glFormat??
- public enum INTERNAL_FORMAT{
- // RGB(GL.GL_RGB),
-
- RGBA(GL.GL_RGBA)
-
- // ,BGRA(GL.GL_BGRA)
-
- ;
-
- private int glConstant;
- private INTERNAL_FORMAT(int glConstant) {
- this.glConstant = glConstant;
- }
- public int getGLConstant(){
- return glConstant;
- }
- }
- */
- /*
- //FIXME use instead of hardcoded GL_UNSIGNED_BYTE?
- public enum GL_TYPE{
- INTEGER(GL.GL_INT),
-
- UNSIGNED_BYTE(GL.GL_UNSIGNED_BYTE);
-
- private int glConstant;
- private GL_TYPE(int glConstant) {
- this.glConstant = glConstant;
- }
- public int getGLConstant(){
- return glConstant;
- }
- }
- */
-
- private PApplet app;
-
- private PGraphicsOpenGL pgl;
-
- private GL gl;
-
- protected boolean fboSupported;
-
- private boolean glTextureInitialized;
-
- protected int[] glTextureID = { 0 } ;
-
- private GLTextureSettings glTextureSettings;
-
- private int internalFormat;
-
- private boolean forcedRectMipMaps = false;
-
- // private int width;
- // private int height;
-
- //FIXME too many isPowerOfTwo checks (see space3d example texture creation)
-
- //TODO implement PBO texture upload
- //TODO initialize so that only the GLTexture object is initialized or nothing
-
- //TODO if shape useOpenGL/useProcessing changes check if PImage or OpenGL texture object is initialized and do if it isnt - on demand!
-
- //TODO need constructor that doesent init super so that when we create fbos at runtime we dont have toallocate huge pixel array!
- //TODO or mannualy set the fbo texture's width/height settings before
-
- //TODO mark the constructors which may only be used in the OpenGL/MT4j/Processing Thread
-
- /**
- * Instantiates a new gL texture.
- *
- * @param parent the parent
- */
- public GLTexture(PApplet parent){
- // this(parent, 2, 2, new GLTextureSettings()); //ORG
- this(parent, new GLTextureSettings());
- }
-
- /**
- * Instantiates a new gL texture.
- *
- * @param parent the parent
- * @param settings the settings
- */
- public GLTexture(PApplet parent, GLTextureSettings settings){
- super(0, 0, ARGB);
-
- this.glTextureInitialized = false;
- // this.pImageUpToDate = false;
- this.glTextureSettings = settings;
-
- this.app = parent;
- this.parent = parent;
- pgl = (PGraphicsOpenGL)parent.g;
- gl = pgl.gl;
- }
-
- /**
- * Instantiates a empty texture of the specified dimensions, using default GLTextureSettings.
- * Image data can be uploaded by calling setTexture(), setGLTexture //TODO setPImageTexture
- *
- * @param parent the parent
- * @param width the width
- * @param height the height
- */
- public GLTexture(PApplet parent, int width, int height){
- this(parent, width, height, new GLTextureSettings());
- }
-
- //TODO maybe make same constructor but with boolean initialzeGLTexture = true/false!?
- //-> see swingtexrenderer -> need to init with dimensions but not init gltex
-
- /**
- * Instantiates a new gL texture.
- *
- * @param parent the parent
- * @param width the width
- * @param height the height
- * @param settings the settings
- */
- public GLTexture(PApplet parent, int width, int height, GLTextureSettings settings){
- // this(parent, width, height, new GLTextureSettings());
- super(width, height, ARGB); //FIXME original!
- // super(2,2,ARGB);
-
- this.glTextureInitialized = false;
- // this.pImageUpToDate = false;
- this.glTextureSettings = settings;
-
- //FIXME test - set target to RECTANGULAR_ARB if loaded image is NPOT
- if ( !(ToolsMath.isPowerOfTwo(width) && ToolsMath.isPowerOfTwo(height)) ){
- this.glTextureSettings.target = TEXTURE_TARGET.RECTANGULAR;
- }
-
- this.app = parent;
- this.parent = parent;
- pgl = (PGraphicsOpenGL)parent.g;
- gl = pgl.gl;
-
- // setTextureParams(params);
-
- // if (initGLTextureObject){
- // initTexture(width, height);
- // }
-
- // if (app.isRenderThreadCurrent()){ //FIXME really allow to delay this? what if other methods are invoked afterwards which depend on this being called?
- setupGLTexture(width, height);
- // }else{
- // app.invokeLater(new Runnable() {
- // public void run() {
- // setupGLTexture(MTTexture.this.width, MTTexture.this.height); //FIXME check for initTexture calls in mt4j and remove them!
- // }
- // });
- // }
- }
-
- /**
- * Instantiates a new gL texture.
- *
- * @param parent the parent
- * @param fileName the file name
- */
- public GLTexture(PApplet parent, String fileName){
- this(parent, fileName, new GLTextureSettings());
- }
-
- /**
- * Instantiates a new gL texture.
- *
- * @param parent the parent
- * @param fileName the file name
- * @param settings the settings
- */
- public GLTexture(PApplet parent, String fileName, GLTextureSettings settings){
- super(2, 2, ARGB); //will get correct dimensions later at setTexture(PImage img, GLTextureSettings settings) -> init(..) call
-
- this.glTextureInitialized = false;
- // this.pImageUpToDate = false;
-
- this.app = parent;
- this.parent = parent;
- pgl = (PGraphicsOpenGL)app.g;
- gl = pgl.gl;
- this.glTextureSettings = settings;
- this.loadTexture(fileName, this.glTextureSettings);
- }
-
-
- /**
- * Instantiates a new gL texture.
- *
- * @param parent the parent
- * @param pImage the image
- */
- public GLTexture(PApplet parent, PImage pImage){
- this(parent, pImage, new GLTextureSettings());
- }
-
-
- /**
- * Instantiates a new texture using the specified settings and image data.
- *
- * <br><b>NOTE: </b>This will make this texture share the specified PImage's pixel
- * array. So changes to the original PImage may change this texture.
- *
- * @param parent the parent
- * @param pImage the image
- * @param settings the settings
- */
- public GLTexture(PApplet parent, PImage pImage, GLTextureSettings settings){
- this(parent, pImage.width, pImage.height, settings);
-
- if (pImage.pixels == null || pImage.pixels.length == 0){
- pImage.loadPixels();
- }
- this.pixels = pImage.pixels; //Dont copy the pixels for performance
- this.width = pImage.width;
- this.height = pImage.height;
-
- //this.loadPixels(); //FIXME neccessary? if we assigned the pixel array it should be loaded already!
- updateGLTextureFromPImage(); //TODO invokelater if not gl thread
- updatePixels();
- }
-
-
- // private void init(int width, int height){
- // if (this.glTextureSettings == null){
- // this.init(width, height, new GLTextureSettings());
- // }else{
- // this.init(width, height, this.glTextureSettings);
- // }
- // }
-
-
- /**
- * Initializes the empty PImage AND the OpenGL texture with the given dimension.
- * This can be used to change the texture dimension and reload a different texture into it.
- *
- * @param width the width
- * @param height the height
- * @param texSettings the tex settings
- */
- private void init(int width, int height, GLTextureSettings texSettings){
- // super.init(1, 1, ARGB);
- super.init(width, height, ARGB);
-
- // pImageUpToDate = false;
-
- this.glTextureSettings = texSettings;
-
- boolean POT = (ToolsMath.isPowerOfTwo(width) && ToolsMath.isPowerOfTwo(height)) ;
- if (POT){
- this.glTextureSettings.target = TEXTURE_TARGET.TEXTURE_2D;
- }else{
- this.glTextureSettings.target = TEXTURE_TARGET.RECTANGULAR;
- }
-
- //FIXME TEST -> only init GL texture if in opengl thread!
- if (app instanceof MTApplication && ((MTApplication)app).isRenderThreadCurrent()) {
- setupGLTexture(width, height);
- }
- }
-
-
- // protected void apply(GLTextureSettings settings){ //TODO
- // if (this.glTextureSettings == null || !this.glTextureSettings.equals(settings)){
- //
- // }
- // }
-
- /**
- * Creates and sets up an empty OpenGL texture object with the specified dimensions and
- * this texture's GLTextureSettings.
- * <br><b>NOTE: </b>
- *
- * @param width the width
- * @param height the height
- */
- public void setupGLTexture(int width, int height){ //TODO make private/protected
- if (this.glTextureID[0] != 0)
- destroy();
-
- //FIXME if check done here, we can remove the check elsewhere?
- boolean POT = (ToolsMath.isPowerOfTwo(width) && ToolsMath.isPowerOfTwo(height));
- if (this.glTextureSettings.target != TEXTURE_TARGET.RECTANGULAR && !POT){
- this.glTextureSettings.target = TEXTURE_TARGET.RECTANGULAR;
- }
-
- //FIXME TEST gluBuild2DMimaps with NPOT TEXTURE -> stretches the images to POT -> we can use normal TEXTURE2D target then
- if (this.glTextureSettings.target == TEXTURE_TARGET.RECTANGULAR && this.glTextureSettings.shrinkFilter.usesMipMapLevels()){
- System.err.println("INFO: A non-power-of-two dimension texture should ideally not be used with Mip Map minification filter. -> Result can be blurred/streched." );
- this.glTextureSettings.target = TEXTURE_TARGET.TEXTURE_2D;
- this.forcedRectMipMaps = true;
- }
-
- //check if fbo and thus the glGenerateMipmapEXT(GL_TEXTURE_2D);
- this.fboSupported = GLFBO.isSupported(app);
-
- // Target (GL_TEXTURE1D, GL_TEXTURE2D, GL_RECTANGLE_ARB ..)
- int textureTarget = glTextureSettings.target.getGLConstant();
-
- //GL_REPEAT with a GL_RECTANGLE_ARB texture target are not supported! => use GL_CLAMP then.
- if (glTextureSettings.target == TEXTURE_TARGET.RECTANGULAR){
- //FIXME gluBuildmipmaps staucht NPOT texture auf pot zusammen?
- //BEi clamp komischer wasser fbo error
- if (glTextureSettings.wrappingHorizontal == WRAP_MODE.REPEAT){
- glTextureSettings.wrappingHorizontal = WRAP_MODE.CLAMP_TO_EDGE; // this.wrap_s = GL.GL_CLAMP;
- }
- if (glTextureSettings.wrappingVertical == WRAP_MODE.REPEAT){
- glTextureSettings.wrappingVertical = WRAP_MODE.CLAMP_TO_EDGE; // this.wrap_t = GL.GL_CLAMP;
- }
-
- //NPOT texture dont support mipmaps!
- if (glTextureSettings.shrinkFilter.usesMipMapLevels()){
- this.glTextureSettings.shrinkFilter = SHRINKAGE_FILTER.BilinearNoMipMaps;
- }
- }
- // Wrapping
- int wrap_s = glTextureSettings.wrappingHorizontal.getGLConstant();
- int wrap_t = glTextureSettings.wrappingVertical.getGLConstant();
-
- //Filtering
- int minFilter = glTextureSettings.shrinkFilter.getGLConstant();
- int magFilter = glTextureSettings.expansionFilter.getGLConstant();
-
- // Texture internal format
- switch (this.format) {
- case PConstants.RGB:
- this.internalFormat = GL.GL_RGB;
- break;
- case PConstants.ARGB:
- this.internalFormat = GL.GL_RGBA;
- break;
- default:
- this.internalFormat = GL.GL_RGBA;
- break;
- }
-
- //Create the texture object
- gl.glGenTextures(1, glTextureID, 0);
- //Bind the texture
- gl.glBindTexture(textureTarget, glTextureID[0]);
- //SET texture mag/min FILTER mode
- gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_MIN_FILTER, minFilter);
- gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_MAG_FILTER, magFilter);
- //Set texture wrapping mode
- gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_WRAP_S, wrap_s);
- gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_WRAP_T, wrap_t);
-
- switch (glTextureSettings.target) {
- case TEXTURE_1D:
- gl.glTexImage1D(textureTarget, 0, internalFormat, width, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, null);
- break;
- default:
- gl.glTexImage2D(textureTarget, 0, internalFormat, width, height, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, null); //FIXME always use GL_RGBA as glformat??
- // gl.glTexImage2D(textureTarget, 0, internalFormat, width, height, 0, GL.GL_BGRA, GL.GL_UNSIGNED_BYTE, null);//ORIGINAL
- break;
- }
- gl.glBindTexture(textureTarget, 0);
- this.glTextureInitialized = true;
- }
-
-
-
- /**
- * Loads the image from the specifed file and the specified settings.
- * Then this PImage AND an OpenGL texture object are set up with the image data.
- * Re-initializes this texture if the old dimensions dont match the new image.
- *
- * @param filename the filename
- * @param settings the settings
- */
- public void loadTexture(String filename, GLTextureSettings settings){
- PImage img = app.loadImage(filename);
- this.glTextureSettings = settings;
- if (!(ToolsMath.isPowerOfTwo(img.width) && ToolsMath.isPowerOfTwo(img.height)) ){
- this.glTextureSettings.target = TEXTURE_TARGET.RECTANGULAR;
- }
- this.loadTexture(img, this.glTextureSettings);
- }
-
-
-
- /**
- * Sets this texture to the data of the specified PImage.
- * Re-sets this texture's PImage data AND OpenGL texture object data.
- * <br><b>NOTE: </b>This will make this texture share the specified PImage's pixel
- * array. So changes to the original PImage may change this texture.
- *
- * @param img the img
- * @param settings the settings
- */
- public void loadTexture(PImage img, GLTextureSettings settings) {
- img.loadPixels();
-
- this.glTextureSettings = settings;
- if (!(ToolsMath.isPowerOfTwo(img.width) && ToolsMath.isPowerOfTwo(img.height)) ){
- this.glTextureSettings.target = TEXTURE_TARGET.RECTANGULAR;
- }
-
- if ((img.width != this.width) || (img.height != this.height) || glTextureID[0] == 0) {
- this.init(img.width, img.height, settings);
- }
- // PApplet.arrayCopy(img.pixels, pixels); //TODO use same pixel array, avoid copy?
- this.pixels = img.pixels;
-
- this.updateGLTextureFromPImage();
- this.updatePixels();
- }
-
-
- /**
- * Sets this texture to the data of the specified image.
- * Re-sets ONLY this texture's OpenGL texture object data!
- * <br><b>NOTE: </b> This texture object should then only be rendered directly by OpenGL (not Processing)
- * <br>To also update the PImage's pixel data used by Processing, use <code>loadPImageTexture(...)</code> or
- * <code>updatePImageFromGLTexture()</code>
- *
- * @param img the new gL texture
- */
- public void loadGLTexture(PImage img) {
- if (!Tools3D.isPowerOfTwoDimension(img)){
- this.glTextureSettings.target = TEXTURE_TARGET.RECTANGULAR;
- }
-
- if ((img.width != width) || (img.height != height) ) {
- init(img.width, img.height, this.glTextureSettings);
- }
- this.updateGLTexture(img.pixels);
- }
-
-
- /**
- * Sets this texture's PImage pixel data to the data of the specified PImage.
- * Re-sets only this texture's PImage data, not the OpenGL texture object!
- * <br><b>NOTE: </b> This texture object should then only be rendered by Processing and not directly by OpenGL!
- * <br>To also update the OpenGL texture object, use <code>loadGLTexture(...)</code>or
- * <code>updateGLTextureFromPImage()</code>
- *
- * @param img the new p image texture
- */
- public void loadPImageTexture(PImage img){
- img.loadPixels();
-
- this.format = img.format;
- if ((img.width != width) || (img.height != height)){
- // System.out.println("loadPImageTexture ..dimensions are different from former texture!");
- this.init(img.width, img.height, this.glTextureSettings); // original
- // super.init(img.width, img.height, img.format);
- }
- // PApplet.arrayCopy(img.pixels, pixels); //TODO use same pixel array, avoid copy?
- this.pixels = img.pixels;
- this.updatePixels();
- }
-
-
-
- /**
- * Updates only the OpenGL texture object with the data from the specified image data array.
- * <br><b>NOTE:</b> The data has to match this texture's width/height dimensions! If it doesen't -
- * call <code>texture.init(newWidth, newHeight)</code> first!
- *
- * @param intArray the int array
- */
- public void updateGLTexture(int[] intArray){
- this.updateGLTexture(IntBuffer.wrap(intArray));
- }
-
-
- /**
- * Updates only the OpenGL texture object with the data from the specified image data buffer.
- * <br><b>NOTE:</b> The data has to match this texture's width/height dimensions! If it doesen't -
- * call <code>texture.init(newWidth, newHeight)</code> first!
- *
- * @param buffer the buffer
- */
- public void updateGLTexture(IntBuffer buffer){
- if (this.glTextureID[0] == 0 || !this.glTextureInitialized){
- setupGLTexture(this.width, this.height);
- System.out.println("calling setupGLTexture()" + " in " + "updateGLTexture() since texture wasnt initialized!" );
- }
-
- // int glFormat = glTextureSettings.glType.getGLConstant();
-
- int glFormat = GL.GL_BGRA; //FIXME DONT HARDCODE!?
- int type = GL.GL_UNSIGNED_BYTE; //FIXME DONT HARDCODE!?
-
- int textureTarget = glTextureSettings.target.getGLConstant();
- // int internalFormat = glTextureSettings.textureInternalFormat.getGLConstant();
-
- //FIXME TEST gluBuild2DMimaps with NPOT TEXTURE -> stretches the images to POT -> we can use normal TEXTURE2D target then
- if (this.glTextureSettings.target == TEXTURE_TARGET.RECTANGULAR && this.glTextureSettings.shrinkFilter.usesMipMapLevels()){
- this.glTextureSettings.target = TEXTURE_TARGET.TEXTURE_2D;
- this.forcedRectMipMaps = true;
- }
-
- //NPOT texture targets dont support mipmaps!
- if (glTextureSettings.target == TEXTURE_TARGET.RECTANGULAR){
- if (glTextureSettings.shrinkFilter.usesMipMapLevels()){
- this.glTextureSettings.shrinkFilter = SHRINKAGE_FILTER.BilinearNoMipMaps;
- }
- }
-
- switch (this.format) {
- case PConstants.RGB:
- this.internalFormat = GL.GL_RGB;
- break;
- case PConstants.ARGB:
- this.internalFormat = GL.GL_RGBA;
- break;
- default:
- this.internalFormat = GL.GL_RGBA;
- break;
- }
-
- gl.glBindTexture(textureTarget, this.glTextureID[0]);
-
- switch (glTextureSettings.target) {
- case TEXTURE_1D:
- if (glFormat == GL.GL_BGRA){
- glFormat = GL.GL_RGBA;
- }
- gl.glTexSubImage1D(textureTarget, 0, 0, this.width, glFormat, type, buffer);
- break;
- case TEXTURE_2D:
- case RECTANGULAR:
- default:
- //MipMapping wont work with RECTANGLE_ARB TARGET !
- if (glTextureSettings.shrinkFilter.usesMipMapLevels()
- && this.glTextureSettings.target != TEXTURE_TARGET.RECTANGULAR
- ){
- //deprectated in opengl 3.0 -will always create mipmaps automatically if lvl 0 changes
- // gl.glTexParameteri( textureTarget, GL.GL_GENERATE_MIPMAP, GL.GL_TRUE );
- if (this.forcedRectMipMaps){
- //Resizes NPOT textures to POT
- GLU glu = ((PGraphicsOpenGL)this.parent.g).glu;
- glu.gluBuild2DMipmaps(textureTarget, internalFormat, this.width, this.height, glFormat, type, buffer);
- }else{
- if (this.fboSupported){ //Naive check if glGenerateMipmapEXT command is supported
- gl.glTexSubImage2D(textureTarget, 0, 0, 0, this.width, this.height, glFormat, type, buffer);
- gl.glGenerateMipmapEXT(textureTarget); //newer OpenGL 3.x method of creating mip maps //TODO problems on ATI? use gl.glEnable(textureTarget) first?
- }else{
- //Old school software method, will resize a NPOT texture to a POT texture
- GLU glu = ((PGraphicsOpenGL)this.parent.g).glu;
- glu.gluBuild2DMipmaps(textureTarget, internalFormat, this.width, this.height, glFormat, type, buffer);
- }
- }
- }
- else{
- gl.glTexSubImage2D(textureTarget, 0, 0, 0, width, height, glFormat, type, buffer); //ORG
- // gl.glTexSubImage2D(textureTarget, 0, 0, 0, width, height, GL.GL_RGB, type, buffer);
- }
- break;
- }
- gl.glBindTexture(textureTarget, 0);
- }
-
-
-
- /**
- * Updates the OpenGL texture object with the data from this PImage.pixels pixel
- * array. This method should be called if the pixel data has changed and the change
- * has to be reflected in the OpenGL texture object (probably because direct OpenGL texture rendering is used)
- * <b>NOTE:</b>The PImage pixel data dimensions have to match the OpenGL texture dimension! If not, use loadTexture()
- */
- public void updateGLTextureFromPImage(){
- updateGLTexture(this.pixels);
- }
-
-
- /**
- * Updates the PImage pixel data from the texture's OpenGL texture object.
- * This method should be called if the OpenGL texture was changed and the change
- * has to be reflected in the PImage used by Processing
- * (probably because Processings rendering pipeling is used instead of direct OpenGL)
- *
- */
- public void updatePImageFromGLTexture(){
- IntBuffer buff = BufferUtil.newIntBuffer(this.width * this.height);
- int textureTarget = this.glTextureSettings.target.getGLConstant();
- gl.glBindTexture(textureTarget, this.glTextureID[0]);
- gl.glGetTexImage(textureTarget, 0, GL.GL_BGRA, GL.GL_UNSIGNED_BYTE, buff);
- gl.glBindTexture(textureTarget, 0);
- buff.get(pixels);
- }
-
-
-
- /**
- * Deletes the opengl texture object.
- */
- public void destroy(){
- if (this.glTextureID[0] != 0){
- gl.glDeleteTextures(1, this.glTextureID, 0);
- this.glTextureID[0] = 0;
- }
-
- // releasePBO(); //FIXME implement
- }
-
-
- /**
- * Sets the texture wrap mode.
- *
- * @param wrappingHorizontal the wrapping horizontal
- * @param wrappingVertical the wrapping vertical
- */
- public void setWrapMode(WRAP_MODE wrappingHorizontal, WRAP_MODE wrappingVertical){
- this.glTextureSettings.wrappingHorizontal = wrappingHorizontal;
- this.glTextureSettings.wrappingVertical = wrappingVertical;
-
- if (this.isGLTexObjectInitialized()){
- gl.glBindTexture(this.getTextureTarget(), this.getTextureID());
- gl.glTexParameteri(this.getTextureTarget(), GL.GL_TEXTURE_WRAP_S, this.glTextureSettings.wrappingHorizontal.getGLConstant());
- gl.glTexParameteri(this.getTextureTarget(), GL.GL_TEXTURE_WRAP_T, this.glTextureSettings.wrappingVertical.getGLConstant());
- gl.glBindTexture(this.getTextureTarget(), 0);
- }
- }
-
- public WRAP_MODE getWrappingHorizontal(){
- return this.glTextureSettings.wrappingHorizontal;
- }
-
- public WRAP_MODE getWrappingVertical(){
- return this.glTextureSettings.wrappingVertical;
- }
-
- /**
- * Sets the texture filtes.
- *
- * @param minFilter the min filter
- * @param magFilter the mag filter
- */
- public void setFilter(SHRINKAGE_FILTER minFilter, EXPANSION_FILTER magFilter){
- if (this.forcedRectMipMaps){
- //Because current target is TEXTURE_2D although it was a NPOT texture which was rescaled using glubuild2dmipmaps
- //and if another filter is chosen that doesent use mip maps and an updating method is called it would choose a RECTANGULAR target = conflict
- System.err.println("INFO: Changing the texture filter for NPOT texture in combination with MipMapping isnt allowed atm.");
- }
- boolean usedMipMapPreviously = this.glTextureSettings.shrinkFilter.usesMipMapLevels();
-
- this.glTextureSettings.shrinkFilter = minFilter;
- this.glTextureSettings.expansionFilter = magFilter;
-
- if (this.isGLTexObjectInitialized()){
- gl.glBindTexture(this.getTextureTarget(), this.getTextureID());
- gl.glTexParameteri(this.getTextureTarget(), GL.GL_TEXTURE_MIN_FILTER, this.glTextureSettings.shrinkFilter.getGLConstant());
- gl.glTexParameteri(this.getTextureTarget(), GL.GL_TEXTURE_MAG_FILTER, this.glTextureSettings.expansionFilter.getGLConstant());
- gl.glBindTexture(this.getTextureTarget(), 0);
- }
-
- //FIXME pixels may be empty/not current - just create mipmaps with gl code ourselves!!
- if (!usedMipMapPreviously && this.glTextureSettings.shrinkFilter.usesMipMapLevels()){
- this.updateGLTexture(this.pixels);
- }
- }
-
- public SHRINKAGE_FILTER getShrinkageFilter(){
- return this.glTextureSettings.shrinkFilter;
- }
-
- public EXPANSION_FILTER getExpansionFilter(){
- return this.glTextureSettings.expansionFilter;
- }
-
- /**
- * Gets the OpenGL texture id.
- *
- * @return the texture id
- */
- public int getTextureID(){
- return this.glTextureID[0];
- }
-
- public int getTextureTarget(){
- return this.glTextureSettings.target.getGLConstant();
- }
-
- public TEXTURE_TARGET getTextureTargetEnum(){
- return this.glTextureSettings.target;
- }
-
- public boolean isGLTexObjectInitialized(){
- return this.glTextureInitialized;
- }
-
-
- /*
- private int[] toRGBA(int[] intArray, int arrayFormat) {
- int t = 0;
- int p = 0;
- int twidth = width;
-
- int[] tIntArray = new int[width * height];
- if (PGraphicsOpenGL.BIG_ENDIAN) {
- switch (arrayFormat) {
- case ALPHA:
-
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- tIntArray[t++] = 0xFFFFFF00 | intArray[p++];
- }
- t += twidth - width;
- }
- break;
- case RGB:
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- int pixel = intArray[p++];
- tIntArray[t++] = (pixel << 8) | 0xff;
- }
- t += twidth - width;
- }
- break;
- case ARGB:
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- int pixel = intArray[p++];
- tIntArray[t++] = (pixel << 8) | ((pixel >>4) & 0xff);
- }
- t += twidth - width;
- }
- break;
- }
- } else {
- // LITTLE_ENDIAN
- // ARGB native, and RGBA opengl means ABGR on windows
- // for the most part just need to swap two components here
- // the sun.cpu.endian here might be "false", oddly enough..
- // (that's why just using an "else", rather than check for "little")
- switch (arrayFormat) {
- case ALPHA:
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- tIntArray[t++] = (intArray[p++] <<4) | 0x00FFFFFF;
- }
- t += twidth - width;
- }
- break;
- case RGB:
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- int pixel = intArray[p++];
- // needs to be ABGR, stored in memory xRGB
- // so R and B must be swapped, and the x just made FF
- tIntArray[t++] = 0xff000000
- | // force opacity for good measure
- ((pixel & 0xFF) <<6) | ((pixel & 0xFF0000) >>6)
- | (pixel & 0x0000FF00);
- }
- t += twidth - width;
- }
- break;
- case ARGB:
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- int pixel = intArray[p++];
- // needs to be ABGR stored in memory ARGB
- // so R and B must be swapped, A and G just brought back in
- tIntArray[t++] = ((pixel & 0xFF) <<6)
- | ((pixel & 0xFF0000) >>6) | (pixel & 0xFF00FF00);
- }
- t += twidth - width;
- }
- break;
- }
- }
- return tIntArray;
- }
-
-
- private int[] toARGB(int[] intArray) {
- int t = 0;
- int p = 0;
- int twidth = width;
- int[] tIntArray = new int[width * height];
- if (PGraphicsOpenGL.BIG_ENDIAN) {
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- int pixel = intArray[p++];
- tIntArray[t++] = (pixel >> 8) | ((pixel <<4) & 0xff);
- }
- t += twidth - width;
- }
- } else {
- // LITTLE_ENDIAN
- // ARGB native, and RGBA opengl means ABGR on windows
- // for the most part just need to swap two components here
- // the sun.cpu.endian here might be "false", oddly enough..
- // (that's why just using an "else", rather than check for "little")
- for (int y = 0; y < height; y++) {
- for (int x = 0; x < width; x++) {
- int pixel = intArray[p++];
- // needs to be ARGB stored in memory ABGR (RGBA = ABGR -> ARGB)
- // so R and B must be swapped, A and G just brought back in
- tIntArray[t++] = ((pixel & 0xFF) <<6) | ((pixel & 0xFF0000) >>6)
- | (pixel & 0xFF00FF00);
- }
- t += twidth - width;
- }
- }
- return tIntArray;
- }
- */
-
-
- /*
-
- if (tpixels == null) {
- twidth = width2;
- theight = height2;
- tpixels = new int[twidth * theight];
- tbuffer = BufferUtil.newIntBuffer(twidth * theight);
- }
-
- // copy image data into the texture
- int p = 0;
- int t = 0;
-
- if (BIG_ENDIAN) {
- switch (source.format) {
- case ALPHA:
- for (int y = 0; y < source.height; y++) {
- for (int x = 0; x < source.width; x++) {
- tpixels[t++] = 0xFFFFFF00 | source.pixels[p++];
- }
- t += twidth - source.width;
- }
- break;
-
- case RGB:
- for (int y = 0; y < source.height; y++) {
- for (int x = 0; x < source.width; x++) {
- int pixel = source.pixels[p++];
- tpixels[t++] = (pixel << 8) | 0xff;
- }
- t += twidth - source.width;
- }
- break;
-
- case ARGB:
- for (int y = 0; y < source.height; y++) {
- for (int x = 0; x < source.width; x++) {
- int pixel = source.pixels[p++];
- tpixels[t++] = (pixel << 8) | ((pixel >> 24) & 0xff);
- }
- t += twidth - source.width;
- }
- break;
- }
-
- } else { // LITTLE_ENDIAN
- // ARGB native, and RGBA opengl means ABGR on windows
- // for the most part just need to swap two components here
- // the sun.cpu.endian here might be "false", oddly enough..
- // (that's why just using an "else", rather than check for "little")
-
- switch (source.format) {
- case ALPHA:
- for (int y = 0; y < source.height; y++) {
- for (int x = 0; x < source.width; x++) {
- tpixels[t++] = (source.pixels[p++] << 24) | 0x00FFFFFF;
- }
- t += twidth - source.width;
- }
- break;
-
- case RGB:
- for (int y = 0; y < source.height; y++) {
- for (int x = 0; x < source.width; x++) {
- int pixel = source.pixels[p++];
- // needs to be ABGR, stored in memory xRGB
- // so R and B must be swapped, and the x just made FF
- tpixels[t++] =
- 0xff000000 | // force opacity for good measure
- ((pixel & 0xFF) << 16) |
- ((pixel & 0xFF0000) >> 16) |
- (pixel & 0x0000FF00);
- }
- t += twidth - source.width;
- }
- break;
-
- case ARGB:
- for (int y = 0; y < source.height; y++) {
- for (int x = 0; x < source.width; x++) {
- int pixel = source.pixels[p++];
- // needs to be ABGR stored in memory ARGB
- // so R and B must be swapped, A and G just brought back in
- tpixels[t++] =
- ((pixel & 0xFF) << 16) |
- ((pixel & 0xFF0000) >> 16) |
- (pixel & 0xFF00FF00);
- }
- t += twidth - source.width;
- }
- break;
- }
- }
- tbuffer.put(tpixels);
- tbuffer.rewind();
- */
-
- public boolean isGLTextureInitialized(){
- return this.glTextureInitialized && this.glTextureID[0] != 0;
- }
-
- /*
- However, non-power-of-two sized textures have limitations that
- do not apply to power-of-two sized textures. NPOTS textures may
- not use mipmap filtering; POTS textures support both mipmapped
- and non-mipmapped filtering. NPOTS textures support only the
- GL_CLAMP, GL_CLAMP_TO_EDGE, and GL_CLAMP_TO_BORDER wrap modes;
- POTS textures support GL_CLAMP_TO_EDGE, GL_REPEAT, GL_CLAMP,
- GL_MIRRORED_REPEAT, and GL_CLAMP_TO_BORDER (and GL_MIRROR_CLAMP_ATI
- and GL_MIRROR_CLAMP_TO_EDGE_ATI if ATI_texture_mirror_once is
- supported) . NPOTS textures do not support an optional 1-texel
- border; POTS textures do support an optional 1-texel border.
-
- */
-
-
- @Override
- protected void finalize() throws Throwable {
- //System.out.println("Finalizing GLTEXTURE - " + this);
- if (this.app instanceof MTApplication) {
- MTApplication mtApp = (MTApplication) this.app;
- mtApp.invokeLater(new Runnable() {
- public void run() {
- destroy();
- }
- });
- }else{
- //TODO use registerPre()?
- //is the object even valid after finalize() is called??
- }
- super.finalize();
- }
-
- }