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