PageRenderTime 42ms CodeModel.GetById 8ms app.highlight 26ms RepoModel.GetById 1ms app.codeStats 0ms

/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
   1/***********************************************************************
   2 * mt4j Copyright (c) 2008 - 2010 Christopher Ruff, Fraunhofer-Gesellschaft All rights reserved.
   3 *  
   4 *   This program is free software: you can redistribute it and/or modify
   5 *   it under the terms of the GNU General Public License as published by
   6 *   the Free Software Foundation, either version 3 of the License, or
   7 *   (at your option) any later version.
   8 *
   9 *   This program is distributed in the hope that it will be useful,
  10 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 *   GNU General Public License for more details.
  13 *
  14 *   You should have received a copy of the GNU General Public License
  15 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
  16 *
  17 ***********************************************************************/
  18package org.mt4j.util.opengl;
  19
  20import java.nio.IntBuffer;
  21
  22import javax.media.opengl.GL;
  23import javax.media.opengl.glu.GLU;
  24
  25import org.mt4j.MTApplication;
  26import org.mt4j.util.math.Tools3D;
  27import org.mt4j.util.math.ToolsMath;
  28
  29import processing.core.PApplet;
  30import processing.core.PConstants;
  31import processing.core.PImage;
  32import processing.opengl.PGraphicsOpenGL;
  33
  34import com.sun.opengl.util.BufferUtil;
  35
  36/**
  37 * This class can only be used in combination with a OpenGL renderer.
  38 * 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
  39 * as Processing's PImage superclass.
  40 * If the texture isnt neeeded anymore, the destroy() method has to be called.
  41 *
  42 * @author Christopher Ruff
  43 */
  44public class GLTexture extends PImage {
  45	
  46	public enum WRAP_MODE{
  47		REPEAT(GL.GL_REPEAT),
  48		CLAMP(GL.GL_CLAMP),
  49		CLAMP_TO_EDGE(GL.GL_CLAMP_TO_EDGE),
  50		CLAMP_TO_BORDER(GL.GL_CLAMP_TO_BORDER);
  51		
  52		private int glConstant;
  53        private WRAP_MODE(int glConstant) {
  54            this.glConstant = glConstant;
  55        }
  56        public int getGLConstant(){
  57            return glConstant;
  58        }
  59	}
  60	
  61	public enum SHRINKAGE_FILTER{
  62		/**
  63         * Nearest neighbor interpolation is the fastest and crudest filtering
  64         * method - it simply uses the color of the texel closest to the pixel
  65         * center for the pixel color. While fast, this results in aliasing and
  66         * shimmering during minification. (GL equivalent: GL_NEAREST)
  67         */
  68        NearestNeighborNoMipMaps(GL.GL_NEAREST, false),
  69
  70        /**
  71         * In this method the four nearest texels to the pixel center are
  72         * sampled (at texture level 0), and their colors are combined by
  73         * weighted averages. Though smoother, without mipmaps it suffers the
  74         * same aliasing and shimmering problems as nearest
  75         * NearestNeighborNoMipMaps. (GL equivalent: GL_LINEAR)
  76         */
  77        BilinearNoMipMaps(GL.GL_LINEAR, false),
  78
  79        /**
  80         * Same as NearestNeighborNoMipMaps except that instead of using samples
  81         * from texture level 0, the closest mipmap level is chosen based on
  82         * distance. This reduces the aliasing and shimmering significantly, but
  83         * does not help with blockiness. (GL equivalent: GL_NEAREST_MIPMAP_NEAREST)
  84         */
  85        NearestNeighborNearestMipMap(GL.GL_NEAREST_MIPMAP_NEAREST, true),
  86
  87        
  88        /**
  89         * Same as BilinearNoMipMaps except that instead of using samples from
  90         * texture level 0, the closest mipmap level is chosen based on
  91         * distance. By using mipmapping we avoid the aliasing and shimmering
  92         * problems of BilinearNoMipMaps. (GL equivalent: GL_LINEAR_MIPMAP_NEAREST)
  93         */
  94        BilinearNearestMipMap(GL.GL_LINEAR_MIPMAP_NEAREST, true),
  95
  96        /**
  97         * Similar to NearestNeighborNoMipMaps except that instead of using
  98         * samples from texture level 0, a sample is chosen from each of the
  99         * closest (by distance) two mipmap levels. A weighted average of these
 100         * two samples is returned. (GL equivalent: GL_NEAREST_MIPMAP_LINEAR)
 101         */
 102        NearestNeighborLinearMipMap(GL.GL_NEAREST_MIPMAP_LINEAR, true),
 103
 104        /**
 105         * Trilinear filtering is a remedy to a common artifact seen in
 106         * mipmapped bilinearly filtered images: an abrupt and very noticeable
 107         * change in quality at boundaries where the renderer switches from one
 108         * mipmap level to the next. Trilinear filtering solves this by doing a
 109         * texture lookup and bilinear filtering on the two closest mipmap
 110         * levels (one higher and one lower quality), and then linearly
 111         * interpolating the results. This results in a smooth degradation of
 112         * texture quality as distance from the viewer increases, rather than a
 113         * series of sudden drops. Of course, closer than Level 0 there is only
 114         * one mipmap level available, and the algorithm reverts to bilinear
 115         * filtering (GL equivalent: GL_LINEAR_MIPMAP_LINEAR)
 116         */
 117        Trilinear(GL.GL_LINEAR_MIPMAP_LINEAR, true);
 118
 119        private boolean usesMipMapLevels;
 120
 121        private int glConstant;
 122        
 123        private SHRINKAGE_FILTER(int glConstant, boolean usesMipMapLevels) {
 124            this.usesMipMapLevels = usesMipMapLevels;
 125            this.glConstant = glConstant;
 126        }
 127        public int getGLConstant(){
 128            return glConstant;
 129        }
 130        public boolean usesMipMapLevels() {
 131            return usesMipMapLevels;
 132        }
 133	}
 134	
 135	public enum EXPANSION_FILTER{
 136		 /**
 137         * Nearest neighbor interpolation is the fastest and crudest filtering
 138         * mode - it simply uses the color of the texel closest to the pixel
 139         * center for the pixel color. While fast, this results in texture
 140         * 'blockiness' during magnification. (GL equivalent: GL_NEAREST)
 141         */
 142        NearestNeighbor(GL.GL_NEAREST),
 143
 144        /**
 145         * In this mode the four nearest texels to the pixel center are sampled
 146         * (at the closest mipmap level), and their colors are combined by
 147         * weighted average according to distance. This removes the 'blockiness'
 148         * seen during magnification, as there is now a smooth gradient of color
 149         * change from one texel to the next, instead of an abrupt jump as the
 150         * pixel center crosses the texel boundary. (GL equivalent: GL_LINEAR)
 151         */
 152        Bilinear(GL.GL_LINEAR);
 153        
 154        private int glConstant;
 155        private EXPANSION_FILTER(int glConstant) {
 156            this.glConstant = glConstant;
 157        }
 158        public int getGLConstant(){
 159            return glConstant;
 160        }
 161	}
 162	
 163	public enum TEXTURE_TARGET{
 164		TEXTURE_1D(GL.GL_TEXTURE_1D),
 165		
 166		TEXTURE_2D(GL.GL_TEXTURE_2D),
 167		
 168		RECTANGULAR(GL.GL_TEXTURE_RECTANGLE_ARB);
 169		
 170		private int glConstant;
 171        private TEXTURE_TARGET(int glConstant) {
 172            this.glConstant = glConstant;
 173        }
 174        public int getGLConstant(){
 175            return glConstant;
 176        }
 177	}
 178	
 179	/*
 180	//FIXME obsolete?  how to deal with the PImage.format and the glFormat??
 181	public enum INTERNAL_FORMAT{
 182//		RGB(GL.GL_RGB),
 183		
 184		RGBA(GL.GL_RGBA)
 185		
 186//		,BGRA(GL.GL_BGRA)
 187		
 188		;
 189		
 190		private int glConstant;
 191        private INTERNAL_FORMAT(int glConstant) {
 192            this.glConstant = glConstant;
 193        }
 194        public int getGLConstant(){
 195            return glConstant;
 196        }
 197	}
 198	*/
 199	/*
 200	//FIXME use instead of hardcoded GL_UNSIGNED_BYTE?
 201	public enum GL_TYPE{
 202		INTEGER(GL.GL_INT),
 203		
 204		UNSIGNED_BYTE(GL.GL_UNSIGNED_BYTE);
 205		
 206		private int glConstant;
 207        private GL_TYPE(int glConstant) {
 208            this.glConstant = glConstant;
 209        }
 210        public int getGLConstant(){
 211            return glConstant;
 212        }
 213	}
 214	*/
 215	
 216	private PApplet app;
 217	
 218	private PGraphicsOpenGL pgl;
 219	
 220	private GL gl;
 221	
 222	protected boolean fboSupported;
 223	
 224	private boolean glTextureInitialized;
 225	
 226	protected int[] glTextureID = { 0 } ;
 227	
 228	private GLTextureSettings glTextureSettings;
 229	
 230	private int internalFormat;
 231	
 232	private boolean forcedRectMipMaps = false;
 233	
 234//	private int width;
 235//	private int height;
 236	
 237	//FIXME too many isPowerOfTwo checks (see space3d example texture creation)
 238	
 239	//TODO implement PBO texture upload
 240	//TODO initialize so that only the GLTexture object is initialized or nothing
 241	
 242	//TODO if shape useOpenGL/useProcessing changes check if PImage or OpenGL texture object is initialized and do if it isnt - on demand!
 243	
 244	//TODO need constructor that doesent init super so that when we create fbos at runtime we dont have toallocate huge pixel array!
 245	//TODO or mannualy set the fbo texture's width/height settings before 
 246	
 247	//TODO mark the constructors which may only be used in the OpenGL/MT4j/Processing Thread 
 248
 249	/**
 250	 * Instantiates a new gL texture.
 251	 *
 252	 * @param parent the parent
 253	 */
 254	public GLTexture(PApplet parent){
 255		//		this(parent, 2, 2, new GLTextureSettings()); //ORG
 256		this(parent, new GLTextureSettings());
 257	}
 258
 259	/**
 260	 * Instantiates a new gL texture.
 261	 *
 262	 * @param parent the parent
 263	 * @param settings the settings
 264	 */
 265	public GLTexture(PApplet parent, GLTextureSettings settings){
 266		super(0, 0, ARGB); 
 267
 268    	this.glTextureInitialized = false;
 269//    	this.pImageUpToDate = false;
 270    	this.glTextureSettings = settings;
 271
 272    	this.app = parent;
 273    	this.parent = parent;
 274    	pgl = (PGraphicsOpenGL)parent.g;
 275    	gl = pgl.gl;
 276	}
 277
 278	/**
 279	 * Instantiates a empty texture of the specified dimensions, using default GLTextureSettings.
 280	 * Image data can be uploaded by calling setTexture(), setGLTexture //TODO setPImageTexture
 281	 *
 282	 * @param parent the parent
 283	 * @param width the width
 284	 * @param height the height
 285	 */
 286	public GLTexture(PApplet parent, int width, int height){
 287		this(parent, width, height, new GLTextureSettings());
 288	}
 289
 290	//TODO maybe make same constructor but with boolean initialzeGLTexture = true/false!? 
 291	//-> see swingtexrenderer -> need to init with dimensions but not init gltex
 292
 293    /**
 294	 * Instantiates a new gL texture.
 295	 *
 296	 * @param parent the parent
 297	 * @param width the width
 298	 * @param height the height
 299	 * @param settings the settings
 300	 */
 301	public GLTexture(PApplet parent, int width, int height, GLTextureSettings settings){
 302    	//    	this(parent, width, height, new GLTextureSettings());
 303    	super(width, height, ARGB); //FIXME original! 
 304//    	super(2,2,ARGB);
 305
 306    	this.glTextureInitialized = false;
 307//    	this.pImageUpToDate = false;
 308    	this.glTextureSettings = settings;
 309
 310        //FIXME test - set target to RECTANGULAR_ARB if loaded image is NPOT
 311        if ( !(ToolsMath.isPowerOfTwo(width) && ToolsMath.isPowerOfTwo(height)) ){
 312       	 	this.glTextureSettings.target = TEXTURE_TARGET.RECTANGULAR;
 313        }
 314
 315    	this.app = parent;
 316    	this.parent = parent;
 317    	pgl = (PGraphicsOpenGL)parent.g;
 318    	gl = pgl.gl;
 319
 320//    	 		setTextureParams(params);
 321
 322//    	 		if (initGLTextureObject){
 323//    	 			initTexture(width, height);
 324//    	 		}
 325
 326//    	if (app.isRenderThreadCurrent()){ //FIXME really allow to delay this? what if other methods are invoked afterwards which depend on this being called?
 327    		setupGLTexture(width, height);
 328//    	}else{
 329//    		app.invokeLater(new Runnable() {
 330//    			public void run() {
 331//    				setupGLTexture(MTTexture.this.width, MTTexture.this.height); //FIXME check for initTexture calls in mt4j and remove them!
 332//    			}
 333//    		});
 334//    	}
 335    }
 336
 337    /**
 338     * Instantiates a new gL texture.
 339     *
 340     * @param parent the parent
 341     * @param fileName the file name
 342     */
 343    public GLTexture(PApplet parent, String fileName){
 344    	this(parent, fileName, new GLTextureSettings());
 345    }
 346
 347    /**
 348     * Instantiates a new gL texture.
 349     *
 350     * @param parent the parent
 351     * @param fileName the file name
 352     * @param settings the settings
 353     */
 354    public GLTexture(PApplet parent, String fileName, GLTextureSettings settings){
 355    	super(2, 2, ARGB);  //will get correct dimensions later at setTexture(PImage img, GLTextureSettings settings) -> init(..) call 
 356
 357    	this.glTextureInitialized = false;
 358//    	this.pImageUpToDate = false;
 359
 360    	this.app = parent;
 361    	this.parent = parent;
 362    	pgl = (PGraphicsOpenGL)app.g;
 363    	gl = pgl.gl;
 364    	this.glTextureSettings = settings;
 365    	this.loadTexture(fileName, this.glTextureSettings);
 366    } 
 367
 368	
 369    /**
 370     * Instantiates a new gL texture.
 371     *
 372     * @param parent the parent
 373     * @param pImage the image
 374     */
 375    public GLTexture(PApplet parent, PImage pImage){
 376    	this(parent, pImage, new GLTextureSettings());
 377    }
 378    
 379    
 380    /**
 381     * Instantiates a new texture using the specified settings and image data.
 382     * 
 383     * <br><b>NOTE: </b>This will make this texture share the specified PImage's pixel
 384     * array. So changes to the original PImage may change this texture.
 385     * 
 386     * @param parent the parent
 387     * @param pImage the image
 388     * @param settings the settings
 389     */
 390    public GLTexture(PApplet parent, PImage pImage, GLTextureSettings settings){
 391    	this(parent, pImage.width, pImage.height, settings);
 392    	
 393    	if (pImage.pixels == null || pImage.pixels.length == 0){
 394    		pImage.loadPixels();
 395    	}
 396    	this.pixels = pImage.pixels; //Dont copy the pixels for performance
 397    	this.width 	= pImage.width;
 398    	this.height = pImage.height;
 399    	
 400    	//this.loadPixels(); //FIXME neccessary? if we assigned the pixel array it should be loaded already!
 401        updateGLTextureFromPImage(); //TODO invokelater if not gl thread
 402        updatePixels();
 403    } 
 404    
 405	
 406//    private void init(int width, int height){
 407//    	if (this.glTextureSettings == null){
 408//    		 this.init(width, height, new GLTextureSettings());
 409//    	}else{
 410//    		 this.init(width, height, this.glTextureSettings);
 411//    	}
 412//    }
 413
 414
 415    /**
 416     * Initializes the empty PImage AND the OpenGL texture with the given dimension.
 417     * This can be used to change the texture dimension and reload a different texture into it.
 418     *
 419     * @param width the width
 420     * @param height the height
 421     * @param texSettings the tex settings
 422     */
 423    private void init(int width, int height, GLTextureSettings texSettings){
 424    	//        super.init(1, 1, ARGB);
 425    	super.init(width, height, ARGB);
 426
 427//    	pImageUpToDate = false;
 428
 429    	this.glTextureSettings = texSettings;
 430
 431    	boolean POT = (ToolsMath.isPowerOfTwo(width) && ToolsMath.isPowerOfTwo(height)) ;
 432    	if (POT){
 433    		this.glTextureSettings.target = TEXTURE_TARGET.TEXTURE_2D;
 434    	}else{
 435    		this.glTextureSettings.target = TEXTURE_TARGET.RECTANGULAR;
 436    	}
 437
 438    	//FIXME TEST -> only init GL texture if in opengl thread!
 439    	if (app instanceof MTApplication && ((MTApplication)app).isRenderThreadCurrent()) {
 440			setupGLTexture(width, height);
 441		}
 442    }	
 443
 444    
 445//	protected void apply(GLTextureSettings settings){ //TODO
 446//		if (this.glTextureSettings == null || !this.glTextureSettings.equals(settings)){
 447//			
 448//		}
 449//	}
 450
 451    /**
 452     * Creates and sets up an empty OpenGL texture object with the specified dimensions and
 453     * this texture's GLTextureSettings.
 454     * <br><b>NOTE: </b>
 455     *
 456     * @param width the width
 457     * @param height the height
 458     */
 459    public void setupGLTexture(int width, int height){ //TODO make private/protected
 460    	if (this.glTextureID[0] != 0)
 461    		destroy();
 462    	
 463    	//FIXME if check done here, we can remove the check elsewhere?
 464    	boolean POT = (ToolsMath.isPowerOfTwo(width) && ToolsMath.isPowerOfTwo(height));
 465    	if (this.glTextureSettings.target != TEXTURE_TARGET.RECTANGULAR && !POT){
 466    		this.glTextureSettings.target = TEXTURE_TARGET.RECTANGULAR;
 467    	}
 468    	
 469    	//FIXME TEST gluBuild2DMimaps with NPOT TEXTURE -> stretches the images to POT -> we can use normal TEXTURE2D target then
 470    	if (this.glTextureSettings.target == TEXTURE_TARGET.RECTANGULAR && this.glTextureSettings.shrinkFilter.usesMipMapLevels()){
 471    		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." );
 472    		this.glTextureSettings.target = TEXTURE_TARGET.TEXTURE_2D;
 473    		this.forcedRectMipMaps = true;
 474    	}
 475
 476    	//check if fbo and thus the glGenerateMipmapEXT(GL_TEXTURE_2D);
 477    	this.fboSupported = GLFBO.isSupported(app);
 478
 479    	// Target (GL_TEXTURE1D, GL_TEXTURE2D, GL_RECTANGLE_ARB ..)
 480		int textureTarget = glTextureSettings.target.getGLConstant();
 481
 482		//GL_REPEAT with a GL_RECTANGLE_ARB texture target are not supported! => use GL_CLAMP then.
 483		if (glTextureSettings.target == TEXTURE_TARGET.RECTANGULAR){
 484			//FIXME gluBuildmipmaps staucht NPOT texture auf pot zusammen?
 485			//BEi clamp komischer wasser fbo error
 486			if (glTextureSettings.wrappingHorizontal == WRAP_MODE.REPEAT){
 487				glTextureSettings.wrappingHorizontal = WRAP_MODE.CLAMP_TO_EDGE; //        		this.wrap_s = GL.GL_CLAMP;
 488			}
 489			if (glTextureSettings.wrappingVertical == WRAP_MODE.REPEAT){
 490				glTextureSettings.wrappingVertical = WRAP_MODE.CLAMP_TO_EDGE; //        		this.wrap_t = GL.GL_CLAMP;
 491			}
 492			
 493			//NPOT texture dont support mipmaps!
 494			if (glTextureSettings.shrinkFilter.usesMipMapLevels()){
 495				this.glTextureSettings.shrinkFilter = SHRINKAGE_FILTER.BilinearNoMipMaps;
 496			}
 497		}
 498		// Wrapping
 499		int wrap_s = glTextureSettings.wrappingHorizontal.getGLConstant();
 500		int wrap_t = glTextureSettings.wrappingVertical.getGLConstant();
 501
 502		//Filtering
 503		int minFilter = glTextureSettings.shrinkFilter.getGLConstant();
 504		int magFilter = glTextureSettings.expansionFilter.getGLConstant();
 505		
 506		// Texture internal format
 507		switch (this.format) {
 508		case PConstants.RGB:
 509			this.internalFormat = GL.GL_RGB;
 510			break;
 511		case PConstants.ARGB:
 512			this.internalFormat = GL.GL_RGBA;
 513			break;
 514		default:
 515			this.internalFormat = GL.GL_RGBA;
 516			break;
 517		}
 518
 519		//Create the texture object
 520		gl.glGenTextures(1, glTextureID, 0);
 521		//Bind the texture
 522		gl.glBindTexture(textureTarget, glTextureID[0]);
 523		//SET texture mag/min FILTER mode
 524		gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_MIN_FILTER, minFilter);
 525		gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_MAG_FILTER, magFilter);
 526		//Set texture wrapping mode
 527		gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_WRAP_S, wrap_s);
 528		gl.glTexParameteri(textureTarget, GL.GL_TEXTURE_WRAP_T, wrap_t);
 529
 530		switch (glTextureSettings.target) {
 531		case TEXTURE_1D:
 532			gl.glTexImage1D(textureTarget, 0, internalFormat, width, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, null); 
 533			break;
 534		default:
 535			gl.glTexImage2D(textureTarget, 0, internalFormat, width, height, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, null); //FIXME always use GL_RGBA as glformat??
 536//			gl.glTexImage2D(textureTarget, 0, internalFormat, width, height, 0, GL.GL_BGRA, GL.GL_UNSIGNED_BYTE, null);//ORIGINAL
 537			break;
 538		}
 539		gl.glBindTexture(textureTarget, 0); 
 540		this.glTextureInitialized = true;
 541	}
 542
 543	
 544	
 545	/**
 546     * Loads the image from the specifed file and the specified settings.
 547     * Then this PImage AND an OpenGL texture object are set up with the image data.
 548     * Re-initializes this texture if the old dimensions dont match the new image.
 549     *
 550     * @param filename the filename
 551     * @param settings the settings
 552     */
 553    public void loadTexture(String filename, GLTextureSettings settings){ 
 554    	PImage img = app.loadImage(filename);
 555    	this.glTextureSettings = settings;
 556        if (!(ToolsMath.isPowerOfTwo(img.width) && ToolsMath.isPowerOfTwo(img.height)) ){
 557       	 	this.glTextureSettings.target = TEXTURE_TARGET.RECTANGULAR;
 558        }
 559    	this.loadTexture(img, this.glTextureSettings);
 560    }
 561
 562    
 563    
 564    /**
 565     * Sets this texture to the data of the specified PImage.
 566     * Re-sets this texture's PImage data AND OpenGL texture object data.
 567     * <br><b>NOTE: </b>This will make this texture share the specified PImage's pixel
 568     * array. So changes to the original PImage may change this texture.
 569     * 
 570     * @param img the img
 571     * @param settings the settings
 572     */
 573    public void loadTexture(PImage img, GLTextureSettings settings) {
 574      img.loadPixels();
 575
 576      this.glTextureSettings = settings;
 577      if (!(ToolsMath.isPowerOfTwo(img.width) && ToolsMath.isPowerOfTwo(img.height)) ){
 578     	 	this.glTextureSettings.target = TEXTURE_TARGET.RECTANGULAR;
 579      }
 580      
 581      if ((img.width != this.width) || (img.height != this.height) || glTextureID[0] == 0) {
 582    	  this.init(img.width, img.height, settings);
 583      }
 584//      PApplet.arrayCopy(img.pixels, pixels); //TODO use same pixel array, avoid copy?
 585      this.pixels = img.pixels;
 586      
 587      this.updateGLTextureFromPImage();
 588      this.updatePixels();
 589    }
 590
 591    
 592    /**
 593     * Sets this texture to the data of the specified image.
 594     * Re-sets ONLY this texture's OpenGL texture object data! 
 595     * <br><b>NOTE: </b> This texture object should then only be rendered directly by OpenGL (not Processing)
 596     * <br>To also update the PImage's pixel data used by Processing, use <code>loadPImageTexture(...)</code> or
 597     * <code>updatePImageFromGLTexture()</code>
 598     *
 599     * @param img the new gL texture
 600     */
 601    public void loadGLTexture(PImage img) {
 602    	 if (!Tools3D.isPowerOfTwoDimension(img)){
 603       	  this.glTextureSettings.target = TEXTURE_TARGET.RECTANGULAR;
 604         }
 605    	 
 606      if ((img.width != width) || (img.height != height) ) {
 607        init(img.width, img.height, this.glTextureSettings);
 608      }
 609      this.updateGLTexture(img.pixels); 
 610    }
 611    
 612    
 613    /**
 614     * Sets this texture's PImage pixel data to the data of the specified PImage.
 615     * Re-sets only this texture's PImage data, not the OpenGL texture object! 
 616     * <br><b>NOTE: </b> This texture object should then only be rendered by Processing and not directly by OpenGL!
 617     * <br>To also update the OpenGL texture object, use <code>loadGLTexture(...)</code>or
 618     * <code>updateGLTextureFromPImage()</code>
 619     * 
 620     * @param img the new p image texture
 621     */
 622    public void loadPImageTexture(PImage img){
 623    	img.loadPixels();
 624    	
 625    	this.format = img.format;
 626        if ((img.width != width) || (img.height != height)){
 627//        	System.out.println("loadPImageTexture ..dimensions are different from former texture!");
 628            this.init(img.width, img.height, this.glTextureSettings); // original
 629//        	 super.init(img.width, img.height, img.format);
 630        }
 631//      PApplet.arrayCopy(img.pixels, pixels); //TODO use same pixel array, avoid copy?
 632        this.pixels = img.pixels;
 633        this.updatePixels();
 634    }
 635    
 636    
 637
 638	/**
 639	 * Updates only the OpenGL texture object with the data from the specified image data array.
 640	 * <br><b>NOTE:</b> The data has to match this texture's width/height dimensions! If it doesen't - 
 641	 * call <code>texture.init(newWidth, newHeight)</code> first!
 642	 *
 643	 * @param intArray the int array
 644	 */
 645	public void updateGLTexture(int[] intArray){ 
 646		this.updateGLTexture(IntBuffer.wrap(intArray));
 647	}
 648
 649
 650	/**
 651	 * Updates only the OpenGL texture object with the data from the specified image data buffer.
 652	 * <br><b>NOTE:</b> The data has to match this texture's width/height dimensions! If it doesen't - 
 653	 * call <code>texture.init(newWidth, newHeight)</code> first!
 654	 * 
 655	 * @param buffer the buffer
 656	 */
 657	public void updateGLTexture(IntBuffer buffer){
 658		if (this.glTextureID[0] == 0 || !this.glTextureInitialized){
 659			setupGLTexture(this.width, this.height); 
 660			System.out.println("calling setupGLTexture()" + " in " + "updateGLTexture() since texture wasnt initialized!" );
 661		}      
 662
 663		//      int glFormat = glTextureSettings.glType.getGLConstant();
 664		
 665		int glFormat 	= GL.GL_BGRA; 				//FIXME DONT HARDCODE!?
 666		int type 		= GL.GL_UNSIGNED_BYTE; 		//FIXME DONT HARDCODE!?
 667
 668		int textureTarget = glTextureSettings.target.getGLConstant();
 669		// int internalFormat = glTextureSettings.textureInternalFormat.getGLConstant();
 670		
 671		//FIXME TEST gluBuild2DMimaps with NPOT TEXTURE -> stretches the images to POT -> we can use normal TEXTURE2D target then
 672    	if (this.glTextureSettings.target == TEXTURE_TARGET.RECTANGULAR && this.glTextureSettings.shrinkFilter.usesMipMapLevels()){
 673    		this.glTextureSettings.target = TEXTURE_TARGET.TEXTURE_2D;
 674    		this.forcedRectMipMaps = true;
 675    	}
 676		
 677		//NPOT texture targets dont support mipmaps!
 678		if (glTextureSettings.target == TEXTURE_TARGET.RECTANGULAR){
 679			if (glTextureSettings.shrinkFilter.usesMipMapLevels()){
 680				this.glTextureSettings.shrinkFilter = SHRINKAGE_FILTER.BilinearNoMipMaps;
 681			}
 682		}
 683		
 684		switch (this.format) {
 685		case PConstants.RGB:
 686			this.internalFormat = GL.GL_RGB;
 687			break;
 688		case PConstants.ARGB:
 689			this.internalFormat = GL.GL_RGBA;
 690			break;
 691		default:
 692			this.internalFormat = GL.GL_RGBA;
 693			break;
 694		}
 695
 696		gl.glBindTexture(textureTarget, this.glTextureID[0]);
 697
 698		switch (glTextureSettings.target) {
 699		case TEXTURE_1D:
 700			if (glFormat == GL.GL_BGRA){ 
 701				glFormat = GL.GL_RGBA;
 702			}
 703			gl.glTexSubImage1D(textureTarget, 0, 0, this.width, glFormat, type, buffer);
 704			break;
 705		case TEXTURE_2D:
 706		case RECTANGULAR:
 707		default:
 708			//MipMapping wont work with RECTANGLE_ARB TARGET !
 709			if (glTextureSettings.shrinkFilter.usesMipMapLevels() 
 710				&& this.glTextureSettings.target != TEXTURE_TARGET.RECTANGULAR
 711			){
 712				//deprectated in opengl 3.0 -will always create mipmaps automatically if lvl 0 changes
 713//				gl.glTexParameteri( textureTarget, GL.GL_GENERATE_MIPMAP, GL.GL_TRUE ); 
 714				if (this.forcedRectMipMaps){
 715					//Resizes NPOT textures to POT
 716					GLU glu = ((PGraphicsOpenGL)this.parent.g).glu;
 717					glu.gluBuild2DMipmaps(textureTarget, internalFormat, this.width, this.height, glFormat, type, buffer);
 718				}else{
 719					if (this.fboSupported){ //Naive check if glGenerateMipmapEXT command is supported
 720						gl.glTexSubImage2D(textureTarget, 0, 0, 0, this.width, this.height, glFormat, type, buffer);
 721						gl.glGenerateMipmapEXT(textureTarget);  //newer OpenGL 3.x method of creating mip maps //TODO problems on ATI? use gl.glEnable(textureTarget) first? 
 722					}else{
 723						//Old school software method, will resize a NPOT texture to a POT texture
 724						GLU glu = ((PGraphicsOpenGL)this.parent.g).glu;
 725						glu.gluBuild2DMipmaps(textureTarget, internalFormat, this.width, this.height, glFormat, type, buffer);
 726					}
 727				}
 728			}
 729			else{
 730				gl.glTexSubImage2D(textureTarget, 0, 0, 0, width, height, glFormat, type, buffer); //ORG
 731//				gl.glTexSubImage2D(textureTarget, 0, 0, 0, width, height, GL.GL_RGB, type, buffer);
 732			}
 733			break;
 734		}
 735		gl.glBindTexture(textureTarget, 0);
 736	}
 737
 738
 739	
 740    /**
 741     * Updates the OpenGL texture object with the data from this PImage.pixels pixel
 742     * array. This method should be called if the pixel data has changed and the change
 743     * has to be reflected in the OpenGL texture object (probably because direct OpenGL texture rendering is used)
 744     * <b>NOTE:</b>The PImage pixel data dimensions have to match the OpenGL texture dimension! If not, use loadTexture()
 745     */
 746    public void updateGLTextureFromPImage(){ 
 747    	updateGLTexture(this.pixels);
 748    }
 749    
 750	
 751    /**
 752     * Updates the PImage pixel data from the texture's OpenGL texture object.
 753     * This method should be called if the OpenGL texture was changed and the change
 754     * has to be reflected in the PImage used by Processing 
 755     * (probably because Processings rendering pipeling is used instead of direct OpenGL)
 756     *
 757     */
 758    public void updatePImageFromGLTexture(){
 759        IntBuffer buff = BufferUtil.newIntBuffer(this.width * this.height);
 760        int textureTarget = this.glTextureSettings.target.getGLConstant();
 761        gl.glBindTexture(textureTarget, this.glTextureID[0]);
 762        gl.glGetTexImage(textureTarget, 0, GL.GL_BGRA, GL.GL_UNSIGNED_BYTE, buff);
 763        gl.glBindTexture(textureTarget, 0);
 764        buff.get(pixels);
 765    }
 766    
 767    
 768
 769	/**
 770     * Deletes the opengl texture object.
 771     */
 772    public void destroy(){
 773    	if (this.glTextureID[0] != 0){
 774	        gl.glDeleteTextures(1, this.glTextureID, 0);  
 775	        this.glTextureID[0] = 0;
 776    	}
 777    	
 778//    	releasePBO(); //FIXME implement
 779    }
 780    
 781    
 782	/**
 783	 * Sets the texture wrap mode.
 784	 *
 785	 * @param wrappingHorizontal the wrapping horizontal
 786	 * @param wrappingVertical the wrapping vertical
 787	 */
 788	public void setWrapMode(WRAP_MODE wrappingHorizontal, WRAP_MODE wrappingVertical){
 789		this.glTextureSettings.wrappingHorizontal = wrappingHorizontal;
 790		this.glTextureSettings.wrappingVertical = wrappingVertical;
 791		
 792		if (this.isGLTexObjectInitialized()){
 793			gl.glBindTexture(this.getTextureTarget(), this.getTextureID());
 794			gl.glTexParameteri(this.getTextureTarget(), GL.GL_TEXTURE_WRAP_S, this.glTextureSettings.wrappingHorizontal.getGLConstant());
 795			gl.glTexParameteri(this.getTextureTarget(), GL.GL_TEXTURE_WRAP_T, this.glTextureSettings.wrappingVertical.getGLConstant());
 796			gl.glBindTexture(this.getTextureTarget(), 0);
 797		}
 798	}
 799	
 800	public WRAP_MODE getWrappingHorizontal(){
 801		return this.glTextureSettings.wrappingHorizontal;
 802	}
 803	
 804	public WRAP_MODE getWrappingVertical(){
 805		return this.glTextureSettings.wrappingVertical;
 806	}
 807	
 808	/**
 809	 * Sets the texture filtes.
 810	 *
 811	 * @param minFilter the min filter
 812	 * @param magFilter the mag filter
 813	 */
 814	public void setFilter(SHRINKAGE_FILTER minFilter, EXPANSION_FILTER magFilter){
 815		if (this.forcedRectMipMaps){
 816			//Because current target is TEXTURE_2D although it was a NPOT texture which was rescaled using glubuild2dmipmaps
 817			//and if another filter is chosen that doesent use mip maps and an updating method is called it would choose a RECTANGULAR target = conflict
 818			System.err.println("INFO: Changing the texture filter for NPOT texture in combination with MipMapping isnt allowed atm.");
 819		}
 820		boolean usedMipMapPreviously = this.glTextureSettings.shrinkFilter.usesMipMapLevels();
 821		
 822		this.glTextureSettings.shrinkFilter = minFilter;
 823		this.glTextureSettings.expansionFilter = magFilter;
 824		
 825		if (this.isGLTexObjectInitialized()){
 826			gl.glBindTexture(this.getTextureTarget(), this.getTextureID());
 827			 gl.glTexParameteri(this.getTextureTarget(), GL.GL_TEXTURE_MIN_FILTER, this.glTextureSettings.shrinkFilter.getGLConstant());
 828			 gl.glTexParameteri(this.getTextureTarget(), GL.GL_TEXTURE_MAG_FILTER, this.glTextureSettings.expansionFilter.getGLConstant());
 829			gl.glBindTexture(this.getTextureTarget(), 0);
 830		}
 831		
 832		//FIXME pixels may be empty/not current - just create mipmaps with gl code ourselves!!
 833		if (!usedMipMapPreviously && this.glTextureSettings.shrinkFilter.usesMipMapLevels()){
 834			this.updateGLTexture(this.pixels); 
 835		}
 836	}
 837	
 838	public SHRINKAGE_FILTER getShrinkageFilter(){
 839		return this.glTextureSettings.shrinkFilter;
 840	}
 841	
 842	public EXPANSION_FILTER getExpansionFilter(){
 843		return this.glTextureSettings.expansionFilter;
 844	}
 845	
 846	/**
 847	 * Gets the OpenGL texture id.
 848	 *
 849	 * @return the texture id
 850	 */
 851	public int getTextureID(){
 852		return this.glTextureID[0];
 853	}
 854	
 855	public int getTextureTarget(){
 856		return this.glTextureSettings.target.getGLConstant();
 857	}
 858	
 859	public TEXTURE_TARGET getTextureTargetEnum(){
 860		return this.glTextureSettings.target;
 861	}
 862	
 863	public boolean isGLTexObjectInitialized(){
 864		return this.glTextureInitialized;
 865	}
 866	
 867
 868/*
 869	private int[] toRGBA(int[] intArray, int arrayFormat) {
 870		int t = 0;
 871		int p = 0;
 872		int twidth = width;
 873		
 874		int[] tIntArray = new int[width * height];
 875		if (PGraphicsOpenGL.BIG_ENDIAN) {
 876			switch (arrayFormat) {
 877			case ALPHA:
 878
 879				for (int y = 0; y < height; y++) {
 880					for (int x = 0; x < width; x++) {
 881						tIntArray[t++] = 0xFFFFFF00 | intArray[p++];
 882					}
 883					t += twidth - width;
 884				}
 885				break;
 886			case RGB:
 887				for (int y = 0; y < height; y++) {
 888					for (int x = 0; x < width; x++) {
 889						int pixel = intArray[p++];
 890						tIntArray[t++] = (pixel << 8) | 0xff;
 891					}
 892					t += twidth - width;
 893				}
 894				break;
 895			case ARGB:
 896				for (int y = 0; y < height; y++) {
 897					for (int x = 0; x < width; x++) {
 898						int pixel = intArray[p++];
 899						tIntArray[t++] = (pixel << 8) | ((pixel >>4) & 0xff);
 900					}
 901					t += twidth - width;
 902				}
 903				break;
 904			}
 905		} else {
 906			// LITTLE_ENDIAN
 907			// ARGB native, and RGBA opengl means ABGR on windows
 908			// for the most part just need to swap two components here
 909			// the sun.cpu.endian here might be "false", oddly enough..
 910			// (that's why just using an "else", rather than check for "little")
 911			switch (arrayFormat) {
 912			case ALPHA:
 913				for (int y = 0; y < height; y++) {
 914					for (int x = 0; x < width; x++) {
 915						tIntArray[t++] = (intArray[p++] <<4) | 0x00FFFFFF;
 916					}
 917					t += twidth - width;
 918				}
 919				break;
 920			case RGB:
 921				for (int y = 0; y < height; y++) {
 922					for (int x = 0; x < width; x++) {
 923						int pixel = intArray[p++];
 924						// needs to be ABGR, stored in memory xRGB
 925						// so R and B must be swapped, and the x just made FF
 926						tIntArray[t++] = 0xff000000
 927						| // force opacity for good measure
 928						((pixel & 0xFF) <<6) | ((pixel & 0xFF0000) >>6)
 929						| (pixel & 0x0000FF00);
 930					}
 931					t += twidth - width;
 932				}
 933				break;
 934			case ARGB:
 935				for (int y = 0; y < height; y++) {
 936					for (int x = 0; x < width; x++) {
 937						int pixel = intArray[p++];
 938						// needs to be ABGR stored in memory ARGB
 939						// so R and B must be swapped, A and G just brought back in
 940						tIntArray[t++] = ((pixel & 0xFF) <<6)
 941						| ((pixel & 0xFF0000) >>6) | (pixel & 0xFF00FF00);
 942					}
 943					t += twidth - width;
 944				}
 945				break;
 946			}
 947		}
 948		return tIntArray;
 949	}
 950
 951	
 952	private int[] toARGB(int[] intArray) {
 953		int t = 0;
 954		int p = 0;
 955		int twidth = width;
 956		int[] tIntArray = new int[width * height];
 957		if (PGraphicsOpenGL.BIG_ENDIAN) {
 958			for (int y = 0; y < height; y++) {
 959				for (int x = 0; x < width; x++) {
 960					int pixel = intArray[p++];
 961					tIntArray[t++] = (pixel >> 8) | ((pixel <<4) & 0xff);
 962				}
 963				t += twidth - width;
 964			}
 965		} else {
 966			// LITTLE_ENDIAN
 967			// ARGB native, and RGBA opengl means ABGR on windows
 968			// for the most part just need to swap two components here
 969			// the sun.cpu.endian here might be "false", oddly enough..
 970			// (that's why just using an "else", rather than check for "little")
 971			for (int y = 0; y < height; y++) {
 972				for (int x = 0; x < width; x++) {
 973					int pixel = intArray[p++];
 974					// needs to be ARGB stored in memory ABGR (RGBA = ABGR -> ARGB)
 975					// so R and B must be swapped, A and G just brought back in
 976					tIntArray[t++] = ((pixel & 0xFF) <<6) | ((pixel & 0xFF0000) >>6)
 977					| (pixel & 0xFF00FF00);
 978				}
 979				t += twidth - width;
 980			}
 981		}
 982		return tIntArray;
 983	}
 984*/
 985
 986	
 987	/*
 988	 
 989	 if (tpixels == null) {
 990        twidth = width2;
 991        theight = height2;
 992        tpixels = new int[twidth * theight];
 993        tbuffer = BufferUtil.newIntBuffer(twidth * theight);
 994      }
 995
 996      // copy image data into the texture
 997      int p = 0;
 998      int t = 0;
 999
1000      if (BIG_ENDIAN) {
1001        switch (source.format) {
1002        case ALPHA:
1003          for (int y = 0; y < source.height; y++) {
1004            for (int x = 0; x < source.width; x++) {
1005              tpixels[t++] = 0xFFFFFF00 | source.pixels[p++];
1006            }
1007            t += twidth - source.width;
1008          }
1009          break;
1010
1011        case RGB:
1012          for (int y = 0; y < source.height; y++) {
1013            for (int x = 0; x < source.width; x++) {
1014              int pixel = source.pixels[p++];
1015              tpixels[t++] = (pixel << 8) | 0xff;
1016            }
1017            t += twidth - source.width;
1018          }
1019          break;
1020
1021        case ARGB:
1022          for (int y = 0; y < source.height; y++) {
1023            for (int x = 0; x < source.width; x++) {
1024              int pixel = source.pixels[p++];
1025              tpixels[t++] = (pixel << 8) | ((pixel >> 24) & 0xff);
1026            }
1027            t += twidth - source.width;
1028          }
1029          break;
1030        }
1031
1032      } else {  // LITTLE_ENDIAN
1033        // ARGB native, and RGBA opengl means ABGR on windows
1034        // for the most part just need to swap two components here
1035        // the sun.cpu.endian here might be "false", oddly enough..
1036        // (that's why just using an "else", rather than check for "little")
1037
1038        switch (source.format) {
1039        case ALPHA:
1040          for (int y = 0; y < source.height; y++) {
1041            for (int x = 0; x < source.width; x++) {
1042              tpixels[t++] = (source.pixels[p++] << 24) | 0x00FFFFFF;
1043            }
1044            t += twidth - source.width;
1045          }
1046          break;
1047
1048        case RGB:
1049          for (int y = 0; y < source.height; y++) {
1050            for (int x = 0; x < source.width; x++) {
1051              int pixel = source.pixels[p++];
1052              // needs to be ABGR, stored in memory xRGB
1053              // so R and B must be swapped, and the x just made FF
1054              tpixels[t++] =
1055                0xff000000 |  // force opacity for good measure
1056                ((pixel & 0xFF) << 16) |
1057                ((pixel & 0xFF0000) >> 16) |
1058                (pixel & 0x0000FF00);
1059            }
1060            t += twidth - source.width;
1061          }
1062          break;
1063
1064        case ARGB:
1065          for (int y = 0; y < source.height; y++) {
1066            for (int x = 0; x < source.width; x++) {
1067              int pixel = source.pixels[p++];
1068              // needs to be ABGR stored in memory ARGB
1069              // so R and B must be swapped, A and G just brought back in
1070              tpixels[t++] =
1071                ((pixel & 0xFF) << 16) |
1072                ((pixel & 0xFF0000) >> 16) |
1073                (pixel & 0xFF00FF00);
1074            }
1075            t += twidth - source.width;
1076          }
1077          break;
1078        }
1079      }
1080      tbuffer.put(tpixels);
1081      tbuffer.rewind();
1082*/
1083	
1084	public boolean isGLTextureInitialized(){
1085		return this.glTextureInitialized && this.glTextureID[0] != 0;
1086	}
1087	
1088	/*
1089	  However, non-power-of-two sized textures have limitations that
1090     do not apply to power-of-two sized textures.  NPOTS textures may
1091     not use mipmap filtering; POTS textures support both mipmapped
1092     and non-mipmapped filtering.  NPOTS textures support only the
1093     GL_CLAMP, GL_CLAMP_TO_EDGE, and GL_CLAMP_TO_BORDER wrap modes;
1094     POTS textures support GL_CLAMP_TO_EDGE, GL_REPEAT, GL_CLAMP,
1095     GL_MIRRORED_REPEAT, and GL_CLAMP_TO_BORDER (and GL_MIRROR_CLAMP_ATI
1096     and GL_MIRROR_CLAMP_TO_EDGE_ATI if ATI_texture_mirror_once is
1097     supported) .  NPOTS textures do not support an optional 1-texel
1098     border; POTS textures do support an optional 1-texel border.
1099
1100	 */
1101	
1102	
1103	@Override
1104	protected void finalize() throws Throwable {
1105		//System.out.println("Finalizing GLTEXTURE - " + this);
1106		if (this.app instanceof MTApplication) {
1107			MTApplication mtApp = (MTApplication) this.app;
1108			mtApp.invokeLater(new Runnable() {
1109				public void run() {
1110					destroy();
1111				}
1112			});
1113		}else{
1114			//TODO use registerPre()?
1115			//is the object even valid after finalize() is called??
1116		}
1117		super.finalize();
1118	}
1119
1120}