2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2009-2012 - DIGITEO - Pierre Lando
5 * This file must be used under the terms of the CeCILL.
6 * This source file is licensed as described in the file COPYING, which
7 * you should have received as part of this distribution. The terms
8 * are also available at
9 * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
12 package org.scilab.forge.scirenderer.implementation.jogl.texture;
14 import com.jogamp.opengl.util.texture.TextureIO;
15 import com.jogamp.opengl.util.texture.TextureCoords;
17 import org.scilab.forge.scirenderer.SciRendererException;
18 import org.scilab.forge.scirenderer.buffers.ElementsBuffer;
19 import org.scilab.forge.scirenderer.implementation.jogl.JoGLCanvas;
20 import org.scilab.forge.scirenderer.implementation.jogl.JoGLDrawingTools;
21 import org.scilab.forge.scirenderer.texture.AbstractTexture;
22 import org.scilab.forge.scirenderer.texture.AnchorPosition;
23 import org.scilab.forge.scirenderer.texture.Texture;
24 import org.scilab.forge.scirenderer.texture.TextureManager;
25 import org.scilab.forge.scirenderer.texture.TextureDataProvider;
26 import org.scilab.forge.scirenderer.tranformations.Transformation;
27 import org.scilab.forge.scirenderer.tranformations.TransformationManager;
28 import org.scilab.forge.scirenderer.tranformations.Vector3d;
31 import javax.media.opengl.GL;
32 import javax.media.opengl.GL2;
33 import javax.media.opengl.GL2ES1;
34 import javax.media.opengl.GL2GL3;
35 import javax.media.opengl.GLProfile;
36 import java.awt.Dimension;
37 import java.nio.ByteBuffer;
38 import java.nio.FloatBuffer;
39 import java.util.Collection;
40 import java.util.HashSet;
44 * @author Pierre Lando
46 public class JoGLTextureManager implements TextureManager {
48 private final Set<JoGLTexture> allTextures = new HashSet<JoGLTexture>();
51 public JoGLTextureManager(JoGLCanvas canvas) {
57 * Bind the given texture to the given OpenGl context.
58 * @param drawingTools drawing tools.
59 * @param texture given texture.
60 * @throws org.scilab.forge.scirenderer.SciRendererException if the texture can't be bind.
62 public void bind(JoGLDrawingTools drawingTools, Texture texture) throws SciRendererException {
63 if ((texture instanceof JoGLTexture) && (allTextures.contains((JoGLTexture) texture))) {
64 ((JoGLTexture) texture).bind(drawingTools);
69 * Draw the given texture.
70 * @param drawingTools used drawing tools.
71 * @param texture the texture too drawn.
72 * @throws org.scilab.forge.scirenderer.SciRendererException if the texture is invalid.
74 public void draw(JoGLDrawingTools drawingTools, Texture texture) throws SciRendererException {
75 if ((texture instanceof JoGLTexture) && (allTextures.contains((JoGLTexture) texture))) {
76 final JoGLTexture jt = (JoGLTexture) texture;
77 if (jt.preDraw(drawingTools)) {
78 jt.draw(drawingTools);
79 jt.postDraw(drawingTools);
84 public void draw(JoGLDrawingTools drawingTools, Texture texture, AnchorPosition anchor, ElementsBuffer positions, double rotationAngle) throws SciRendererException {
85 if ((texture instanceof JoGLTexture) && (allTextures.contains((JoGLTexture) texture))) {
86 if (positions != null) {
87 FloatBuffer data = positions.getData();
90 float[] position = {0, 0, 0, 1};
91 final JoGLTexture jt = (JoGLTexture) texture;
92 if (jt.preDraw(drawingTools)) {
93 while (data.remaining() >= 4) {
95 jt.draw(drawingTools, anchor, new Vector3d(position), rotationAngle);
97 jt.postDraw(drawingTools);
104 public void draw(JoGLDrawingTools drawingTools, Texture texture, AnchorPosition anchor, Vector3d position, double rotationAngle) throws SciRendererException {
105 if ((texture instanceof JoGLTexture) && (allTextures.contains((JoGLTexture) texture))) {
106 final JoGLTexture jt = (JoGLTexture) texture;
107 jt.preDraw(drawingTools);
108 jt.draw(drawingTools, anchor, position, rotationAngle);
109 jt.postDraw(drawingTools);
113 /** Called when gl context is gone. */
114 public void glReload() {
115 for (JoGLTexture texture : allTextures) {
121 public Texture createTexture() {
122 JoGLTexture texture = new JoGLTexture();
123 allTextures.add(texture);
128 public void dispose(Collection<Texture> textures) {
129 for (Texture texture : textures) {
135 public void dispose(Texture texture) {
136 if ((texture instanceof JoGLTexture) && (allTextures.contains((JoGLTexture) texture))) {
137 allTextures.remove((JoGLTexture) texture);
138 ((JoGLTexture) texture).dispose();
143 * Inner class for {@link Texture} implementation.
145 public class JoGLTexture extends AbstractTexture implements Texture {
146 private com.jogamp.opengl.util.texture.Texture[] textures;
147 private JoGLTextureData[] textureData;
150 private double sfactor = 1;
151 private double tfactor = 1;
152 private ByteBuffer buffer;
153 private TextureDataProvider.ImageType previousType;
154 private JoGLDrawingTools drawingTools;
157 * Default constructor.
159 public JoGLTexture() {
162 public final boolean isRowMajorOrder() {
163 return getDataProvider().isRowMajorOrder();
167 * Bind the texture in the OpenGl context.
168 * @param drawingTools current drawing tools.
169 * @throws SciRendererException if the texture is invalid.
171 public synchronized void bind(JoGLDrawingTools drawingTools) throws SciRendererException {
172 if (this.drawingTools == null) {
173 this.drawingTools = this.drawingTools;
176 GL2 gl = drawingTools.getGl().getGL2();
178 checkData(drawingTools);
179 if (textures.length == 1) {
180 gl.glEnable(GL2.GL_TEXTURE_2D);
181 gl.glEnable(GL2.GL_BLEND);
182 gl.glBlendFunc(GL2.GL_ONE, GL2.GL_ONE_MINUS_SRC_ALPHA);
183 gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_REPLACE);
185 textures[0].setTexParameteri(gl, GL2.GL_TEXTURE_MAG_FILTER, getAsGLFilter(getMagnificationFilter(), false));
186 textures[0].setTexParameteri(gl, GL2.GL_TEXTURE_MIN_FILTER, getAsGLFilter(getMinifyingFilter(), false));
187 if (gl.isNPOTTextureAvailable()) {
188 textures[0].setTexParameteri(gl, GL2.GL_TEXTURE_WRAP_S, getAsGLWrappingMode(getSWrappingMode()));
189 textures[0].setTexParameteri(gl, GL2.GL_TEXTURE_WRAP_T, getAsGLWrappingMode(getTWrappingMode()));
191 textures[0].setTexParameteri(gl, GL2.GL_TEXTURE_WRAP_S, GL2.GL_CLAMP_TO_EDGE);
192 textures[0].setTexParameteri(gl, GL2.GL_TEXTURE_WRAP_T, GL2.GL_CLAMP_TO_EDGE);
194 textures[0].bind(gl);
196 /* sfactor and tfactor are useful to have the correct texture coordinates when the texture
197 was transformed into a power-of-two texture */
198 Dimension textureSize = getDataProvider().getTextureSize();
199 sfactor = (double) textureSize.width / (double) textures[0].getWidth();
200 tfactor = (double) textureSize.height / (double) textures[0].getHeight();
202 throw new SciRendererException("Texture is too large");
205 throw new SciRendererException("Texture have no data.");
210 * Check if the texture data are up to date.
211 * @param drawingTools the drawing tools.
212 * @throws SciRendererException if the texture is too big.
214 private synchronized void checkData(JoGLDrawingTools drawingTools) throws SciRendererException {
215 if (isValid() && !upToDate) {
216 GL2 gl = drawingTools.getGl().getGL2();
218 Dimension textureSize = getDataProvider().getTextureSize();
219 int maxSize = drawingTools.getGLCapacity().getMaximumTextureSize();
220 wCuts = (int) Math.ceil(textureSize.getWidth() / maxSize);
221 hCuts = (int) Math.ceil(textureSize.getHeight() / maxSize);
222 ByteBuffer newBuffer = getDataProvider().getData();
223 TextureDataProvider.ImageType newType = getDataProvider().getImageType();
225 if (newBuffer != null && textureSize.width != 0 && textureSize.height != 0) {
226 boolean hasChanged = false;
227 if (newBuffer != buffer || newType != previousType) {
229 if (newBuffer != null) {
230 textures = new com.jogamp.opengl.util.texture.Texture[wCuts * hCuts];
231 textureData = new JoGLTextureData[wCuts * hCuts];
234 previousType = newType;
238 if (newBuffer != null || buffer != null) {
239 if (wCuts == 1 && hCuts == 1) {
241 if (isRowMajorOrder()) {
242 textureData[0] = new JoGLTextureData(gl.getGLProfile(), newType, buffer, textureSize.width, textureSize.height);
244 textureData[0] = new JoGLTextureData(gl.getGLProfile(), newType, buffer, textureSize.height, textureSize.width);
247 textures[0] = TextureIO.newTexture(textureData[0]);
248 } catch (Exception e) {
249 System.err.println(e);
253 textures[0].updateSubImage(gl, textureData[0], 0, 0, 0);
254 } catch (Exception e) {
255 System.err.println(e);
260 for (int i = 0; i < wCuts; i++) {
261 for (int j = 0; j < hCuts; j++) {
263 final int x = i * maxSize;
264 final int y = j * maxSize;
265 final int width = getSubTextureSize((i == (wCuts - 1)), textureSize.width, maxSize);
266 final int height = getSubTextureSize((j == (hCuts - 1)), textureSize.height, maxSize);
267 if (isRowMajorOrder()) {
268 textureData[k] = new JoGLTextureData(gl.getGLProfile(), newType, buffer, width, height, x, y, textureSize.width, textureSize.height);
270 textureData[k] = new JoGLTextureData(gl.getGLProfile(), newType, buffer, height, width, y, x, textureSize.height, textureSize.width);
272 textures[k] = TextureIO.newTexture(textureData[k]);
274 textures[k].updateSubImage(gl, textureData[k], 0, 0, 0);
282 if (textures != null) {
288 private void releaseTextures(GL2 gl) {
289 if (textures != null) {
290 for (com.jogamp.opengl.util.texture.Texture texture : textures) {
291 if (texture != null) {
298 if (textureData != null) {
299 for (JoGLTextureData td : textureData) {
308 public void dispose() {
309 if (drawingTools != null) {
310 releaseTextures(drawingTools.getGl().getGL2());
314 public boolean preDraw(JoGLDrawingTools drawingTools) throws SciRendererException {
315 checkData(drawingTools);
317 if (textures == null) {
321 final GL2 gl = drawingTools.getGl().getGL2();
323 gl.glMatrixMode(GL2.GL_TEXTURE);
327 gl.glMatrixMode(GL2.GL_PROJECTION);
331 gl.glEnable(GL2.GL_TEXTURE_2D);
332 gl.glEnable(GL2.GL_BLEND);
333 gl.glBlendFunc(GL2.GL_ONE, GL2.GL_ONE_MINUS_SRC_ALPHA);
335 gl.glEnable(GL2.GL_ALPHA_TEST);
336 gl.glAlphaFunc(GL2.GL_GREATER, 0.0f);
338 gl.glPushAttrib(GL2.GL_ALL_ATTRIB_BITS);
340 gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_REPLACE);
342 for (int k = 0; k < wCuts * hCuts; k++) {
343 textures[k].enable(gl);
344 textures[k].setTexParameteri(gl, GL2.GL_TEXTURE_MAG_FILTER, getAsGLFilter(getMagnificationFilter(), false));
345 textures[k].setTexParameteri(gl, GL2.GL_TEXTURE_MIN_FILTER, getAsGLFilter(getMinifyingFilter(), false));
346 if (gl.isNPOTTextureAvailable()) {
347 textures[k].setTexParameteri(gl, GL2.GL_TEXTURE_WRAP_S, getAsGLWrappingMode(getSWrappingMode()));
348 textures[k].setTexParameteri(gl, GL2.GL_TEXTURE_WRAP_T, getAsGLWrappingMode(getTWrappingMode()));
350 textures[k].setTexParameteri(gl, GL2.GL_TEXTURE_WRAP_S, GL2.GL_CLAMP_TO_EDGE);
351 textures[k].setTexParameteri(gl, GL2.GL_TEXTURE_WRAP_T, GL2.GL_CLAMP_TO_EDGE);
358 public void draw(JoGLDrawingTools drawingTools, AnchorPosition anchor, Vector3d position, double rotationAngle) throws SciRendererException {
359 TransformationManager transformationManager = drawingTools.getTransformationManager();
360 Transformation canvasProjection = transformationManager.getCanvasProjection();
361 boolean sceneCoordinate = drawingTools.getTransformationManager().isUsingSceneCoordinate();
362 Dimension dimension = drawingTools.getCanvas().getDimension();
364 final GL2 gl = drawingTools.getGl().getGL2();
366 if (sceneCoordinate) {
367 projected = canvasProjection.project(position);
369 projected = position;
372 gl.glMatrixMode(GL2.GL_MODELVIEW);
375 gl.glOrtho(0, dimension.width, 0, dimension.height, 1, -1);
377 if (rotationAngle == 0) {
378 gl.glTranslated(Math.round(projected.getX() + getAnchorDeltaX(anchor)), Math.round(projected.getY() + getAnchorDeltaY(anchor)), projected.getZ());
380 gl.glTranslated(Math.round(projected.getX()), Math.round(projected.getY()), projected.getZ());
381 gl.glRotated(rotationAngle, 0, 0, 1);
382 gl.glTranslated(Math.round(getAnchorDeltaX(anchor)), Math.round(getAnchorDeltaY(anchor)), 0);
387 gl.glMatrixMode(GL2.GL_MODELVIEW);
391 public void postDraw(JoGLDrawingTools drawingTools) {
392 final GL2 gl = drawingTools.getGl().getGL2();
394 for (int k = 0; k < wCuts * hCuts; k++) {
395 textures[k].disable(gl);
399 gl.glDisable(GL2.GL_ALPHA_TEST);
400 gl.glDisable(GL2.GL_TEXTURE_2D);
401 gl.glDisable(GL2.GL_BLEND);
403 gl.glMatrixMode(GL2.GL_PROJECTION);
406 gl.glMatrixMode(GL2.GL_TEXTURE);
411 * Draw the texture in XY plane.
412 * @param drawingTools the drawing tools.
413 * @throws SciRendererException if the texture is invalid.
415 public void draw(JoGLDrawingTools drawingTools) throws SciRendererException {
416 final int maxSize = drawingTools.getGLCapacity().getMaximumTextureSize();
417 final Dimension textureSize = getDataProvider().getTextureSize();
418 final GL2 gl = drawingTools.getGl().getGL2();
420 if (textureSize != null && textures != null) {
421 if (wCuts == 1 && hCuts == 1) {
422 final TextureCoords coords;
423 if (isRowMajorOrder()) {
424 coords = textures[0].getSubImageTexCoords(0, 0, textureSize.width, textureSize.height);
426 coords = textures[0].getSubImageTexCoords(0, 0, textureSize.height, textureSize.width);
428 textures[0].bind(gl);
430 gl.glBegin(GL2.GL_QUADS);
431 gl.glTexCoord2f(coords.left(), coords.bottom());
432 if (isRowMajorOrder()) {
435 gl.glVertex2f(textureSize.width, textureSize.height);
437 gl.glTexCoord2f(coords.right(), coords.bottom());
438 gl.glVertex2f(textureSize.width, 0);
439 gl.glTexCoord2f(coords.right(), coords.top());
440 if (isRowMajorOrder()) {
441 gl.glVertex2f(textureSize.width, textureSize.height);
445 gl.glTexCoord2f(coords.left(), coords.top());
446 gl.glVertex2f(0, textureSize.height);
450 for (int i = 0; i < wCuts; i++) {
451 for (int j = 0; j < hCuts; j++) {
452 final int x = i * maxSize;
453 final int y = j * maxSize;
454 final int width = getSubTextureSize((i == (wCuts - 1)), textureSize.width, maxSize);
455 final int height = getSubTextureSize((j == (hCuts - 1)), textureSize.height, maxSize);
457 // if the texture has been transformed in a power-of-two texture we need to have the correct coords.
458 final TextureCoords coords;
459 if (isRowMajorOrder()) {
460 coords = textures[k].getSubImageTexCoords(0, 0, width, height);
462 coords = textures[k].getSubImageTexCoords(0, 0, height, width);
464 textures[k].bind(gl);
466 gl.glBegin(GL2.GL_QUADS);
467 gl.glTexCoord2f(coords.left(), coords.top());
468 gl.glVertex2f(x, textureSize.height - y);
469 gl.glTexCoord2f(coords.right(), coords.top());
470 if (isRowMajorOrder()) {
471 gl.glVertex2f(x + width, textureSize.height - y);
473 gl.glVertex2f(x, textureSize.height - y - height);
475 gl.glTexCoord2f(coords.right(), coords.bottom());
476 gl.glVertex2f(x + width, textureSize.height - y - height);
477 gl.glTexCoord2f(coords.left(), coords.bottom());
478 if (isRowMajorOrder()) {
479 gl.glVertex2f(x, textureSize.height - y - height);
481 gl.glVertex2f(x + width, textureSize.height - y);
493 public double getSScaleFactor() {
498 public double getTScaleFactor() {
503 * Compute the sub texture size.
504 * @param lastPart true if this is the last part.
505 * @param textureSize the global texture size.
506 * @param maxSize the maximum sub-texture size.
507 * @return the sub texture size.
509 private int getSubTextureSize(boolean lastPart, int textureSize, int maxSize) {
511 int lastSize = textureSize % maxSize;
522 private int getAsGLWrappingMode(Texture.Wrap wrappingMode) {
523 switch (wrappingMode) {
525 return GL2.GL_CLAMP_TO_EDGE;
527 return GL2.GL_REPEAT;
529 return GL2.GL_REPEAT;
533 private int getAsGLFilter(Texture.Filter filter, boolean mipmap) {
538 returnedValue = GL2.GL_LINEAR_MIPMAP_LINEAR;
541 returnedValue = GL2.GL_NEAREST_MIPMAP_NEAREST;
544 returnedValue = GL2.GL_NEAREST;
550 returnedValue = GL2.GL_LINEAR;
553 returnedValue = GL2.GL_NEAREST;
556 returnedValue = GL2.GL_NEAREST;
560 return returnedValue;
563 /** Called when gl context is gone. */
564 public void glReload() {
571 * Return the deltaX to apply to the sprite in regards to the given anchor.
572 * @param anchor the given anchor.
573 * @return the deltaX to apply to the sprite in regards to the given anchor.
575 protected double getAnchorDeltaX(AnchorPosition anchor) {
576 int spriteWidth = getDataProvider().getTextureSize().width;
585 return -spriteWidth / 2f;
596 * Return the deltaY to apply to the sprite in regards to the given anchor.
597 * @param anchor the given anchor.
598 * @return the deltaY to apply to the sprite in regards to the given anchor.
600 protected double getAnchorDeltaY(AnchorPosition anchor) {
601 int spriteHeight = getDataProvider().getTextureSize().height;
606 return -spriteHeight;
610 return -spriteHeight / 2f;
621 private static class JoGLTextureData extends com.jogamp.opengl.util.texture.TextureData {
623 private ByteBuffer data;
626 public JoGLTextureData(GLProfile glp, int[] infos, ByteBuffer data, int width, int height, int x, int y, int totalWidth, int totalHeight) {
627 super(glp, infos[0], width, height, 0, infos[1], infos[2], false, false, true, null, null);
629 this.shift = ((x == 0 && y == 0) || data.capacity() == 0) ? 0 : (data.capacity() / (totalWidth * totalHeight) * (x + y * totalWidth));
630 this.rowLength = totalWidth;
631 this.alignment = infos[3];
634 public JoGLTextureData(GLProfile glp, TextureDataProvider.ImageType type, ByteBuffer data, int width, int height) {
635 this(glp, getInfos(type), data, width, height, 0, 0, width, height);
638 public JoGLTextureData(GLProfile glp, TextureDataProvider.ImageType type, ByteBuffer data, int width, int height, int x, int y, int totalWidth, int totalHeight) {
639 this(glp, getInfos(type), data, width, height, x, y, totalWidth, totalHeight);
642 public ByteBuffer getBuffer() {
646 data.position(shift);
647 ByteBuffer b = data.slice();
654 public void setData(ByteBuffer data) {
658 public void destroy() {
663 public static int[] getInfos(TextureDataProvider.ImageType type) {
666 //internalFormat, pixelFormat, pixelType, alignment
667 return new int[] {GL2.GL_RGB, GL.GL_RGB, GL2.GL_UNSIGNED_BYTE, 1};
669 return new int[] {GL2.GL_RGB, GL.GL_RGBA, GL2.GL_UNSIGNED_INT_8_8_8_8, 4};
671 return new int[] {GL2GL3.GL_RGB, GL2GL3.GL_BGR, GL.GL_UNSIGNED_BYTE, 1};
673 return new int[] {GL.GL_LUMINANCE, GL.GL_LUMINANCE, GL.GL_UNSIGNED_BYTE, 1};
675 return new int[] {GL2.GL_LUMINANCE16, GL2.GL_LUMINANCE, GL2.GL_UNSIGNED_SHORT, 2};
677 return new int[] {GL2.GL_RGBA, GL.GL_RGBA, GL2.GL_UNSIGNED_INT_8_8_8_8, 4};
679 return new int[] {GL2.GL_RGBA, GL2.GL_RGBA, GL2.GL_UNSIGNED_INT_8_8_8_8_REV, 4};
681 return new int[] {GL2.GL_RGBA, GL2.GL_ABGR_EXT, GL.GL_UNSIGNED_BYTE, 1};
683 return new int[] {GL2.GL_R3_G3_B2, GL2.GL_RGB, GL2.GL_UNSIGNED_BYTE_3_3_2, 1};
685 return new int[] {GL2.GL_RED, GL2.GL_RED, GL2.GL_UNSIGNED_BYTE, 1};
687 return new int[] {GL2.GL_RGB8, GL2.GL_GREEN, GL2.GL_UNSIGNED_BYTE, 1};
689 return new int[] {GL2.GL_RGB8, GL2.GL_BLUE, GL2.GL_UNSIGNED_BYTE, 1};
691 return new int[] {GL2.GL_INTENSITY, GL2.GL_LUMINANCE, GL2.GL_UNSIGNED_BYTE, 1};
693 return new int[] {GL2.GL_RGBA4, GL2.GL_RGBA, GL2.GL_UNSIGNED_SHORT_4_4_4_4, 2};
695 return new int[] {GL2.GL_RGB5_A1, GL2.GL_RGBA, GL2.GL_UNSIGNED_SHORT_5_5_5_1, 2};
697 return new int[] {GL2ES1.GL_RGB16F, GL2.GL_RGB, GL2.GL_FLOAT, 4};
699 return new int[] {GL2.GL_RGBA16F, GL2.GL_RGBA, GL2.GL_FLOAT, 4};
701 return new int[] {GL2.GL_LUMINANCE16F, GL2.GL_LUMINANCE, GL2.GL_FLOAT, 4};
703 return new int[] {GL2.GL_RGB16, GL2.GL_RED, GL2.GL_UNSIGNED_SHORT, 2};
705 return new int[] {GL2.GL_RGB16, GL2.GL_GREEN, GL2.GL_UNSIGNED_SHORT, 2};
707 return new int[] {GL2.GL_RGB16, GL2.GL_BLUE, GL2.GL_UNSIGNED_SHORT, 2};
709 return new int[] {GL2ES1.GL_RGB16F, GL2.GL_RED, GL2.GL_FLOAT, 4};
711 return new int[] {GL2ES1.GL_RGB16F, GL2.GL_GREEN, GL2.GL_FLOAT, 4};
713 return new int[] {GL2ES1.GL_RGB16F, GL2.GL_BLUE, GL2.GL_FLOAT, 4};
715 return new int[] {GL2.GL_RGBA, GL2.GL_RGBA, GL2.GL_UNSIGNED_BYTE, 1};
718 return new int[] {GL2.GL_RGB, GL.GL_RGB, GL2.GL_UNSIGNED_BYTE, 1};