Files
processing4/android/core/src/processing/opengl/PGL.java
T
2012-08-14 17:39:33 +00:00

2217 lines
71 KiB
Java

/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2011 Andres Colubri
Copyright (c) 2010 Ben Fry and Casey Reas
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License version 2.1 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.opengl;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.Arrays;
import processing.core.PApplet;
import processing.opengl.tess.PGLU;
import processing.opengl.tess.PGLUtessellator;
import processing.opengl.tess.PGLUtessellatorCallbackAdapter;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.opengles.*;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView.EGLConfigChooser;
import android.opengl.GLSurfaceView.Renderer;
import android.opengl.GLSurfaceView;
import android.opengl.GLU;
/**
* Processing-OpenGL abstraction layer.
*
*/
public class PGL {
/** Size of a short (in bytes). */
protected static final int SIZEOF_SHORT = Short.SIZE / 8;
/** Size of an int (in bytes). */
protected static final int SIZEOF_INT = Integer.SIZE / 8;
/** Size of a float (in bytes). */
protected static final int SIZEOF_FLOAT = Float.SIZE / 8;
/** Size of a byte (in bytes). */
protected static final int SIZEOF_BYTE = Byte.SIZE / 8;
/** Size of a vertex index. */
protected static final int SIZEOF_INDEX = SIZEOF_SHORT;
/** Type of a vertex index. */
protected static final int INDEX_TYPE = GLES20.GL_UNSIGNED_SHORT;
/** Initial sizes for arrays of input and tessellated data. */
protected static final int DEFAULT_IN_VERTICES = 16;
protected static final int DEFAULT_IN_EDGES = 32;
protected static final int DEFAULT_IN_TEXTURES = 16;
protected static final int DEFAULT_TESS_VERTICES = 16;
protected static final int DEFAULT_TESS_INDICES = 32;
/** Maximum lights by default is 8, the minimum defined by OpenGL. */
protected static final int MAX_LIGHTS = 8;
/** Maximum index value of a tessellated vertex. GLES restricts the vertex
* indices to be of type unsigned short. Since Java only supports signed
* shorts as primitive type we have 2^15 = 32768 as the maximum number of
* vertices that can be referred to within a single VBO. */
protected static final int MAX_VERTEX_INDEX = 32767;
protected static final int MAX_VERTEX_INDEX1 = MAX_VERTEX_INDEX + 1;
/** Count of tessellated fill, line or point vertices that will
* trigger a flush in the immediate mode. It doesn't necessarily
* be equal to MAX_VERTEX_INDEX1, since the number of vertices can
* be effectively much large since the renderer uses offsets to
* refer to vertices beyond the MAX_VERTEX_INDEX limit.
*/
protected static final int FLUSH_VERTEX_COUNT = MAX_VERTEX_INDEX1;
/** Maximum dimension of a texture used to hold font data. **/
protected static final int MAX_FONT_TEX_SIZE = 512;
/** Minimum stroke weight needed to apply the full path stroking
* algorithm that properly generates caps and joing.
*/
protected static final float MIN_CAPS_JOINS_WEIGHT = 2.f;
/** Maximum length of linear paths to be stroked with the
* full algorithm that generates accurate caps and joins.
*/
protected static final int MAX_CAPS_JOINS_LENGTH = 1000;
/** Minimum array size to use arrayCopy method(). **/
protected static final int MIN_ARRAYCOPY_SIZE = 2;
/** Enables/disables mipmap use. **/
protected static final boolean MIPMAPS_ENABLED = false;
/** Machine Epsilon for float precision. **/
protected static float FLOAT_EPS = Float.MIN_VALUE;
// Calculation of the Machine Epsilon for float precision. From:
// http://en.wikipedia.org/wiki/Machine_epsilon#Approximation_using_Java
static {
float eps = 1.0f;
do {
eps /= 2.0f;
} while ((float)(1.0 + (eps / 2.0)) != 1.0);
FLOAT_EPS = eps;
}
/**
* Set to true if the host system is big endian (PowerPC, MIPS, SPARC), false
* if little endian (x86 Intel for Mac or PC).
*/
protected static boolean BIG_ENDIAN = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN;
protected static final String SHADER_PREPROCESSOR_DIRECTIVE = "#ifdef GL_ES\n" +
"precision mediump float;\n" +
"precision mediump int;\n" +
"#endif\n";
///////////////////////////////////////////////////////////////////////////////////
// OpenGL constants
// The values for constants not defined in the GLES20 interface can be found in this file:
// http://androidxref.com/source/raw/development/tools/glesv2debugger/src/com/android/glesv2debugger/GLEnum.java
public static final int FALSE = GLES20.GL_FALSE;
public static final int TRUE = GLES20.GL_TRUE;
public static final int LESS = GLES20.GL_LESS;
public static final int LEQUAL = GLES20.GL_LEQUAL;
public static final int CCW = GLES20.GL_CCW;
public static final int CW = GLES20.GL_CW;
public static final int CULL_FACE = GLES20.GL_CULL_FACE;
public static final int FRONT = GLES20.GL_FRONT;
public static final int BACK = GLES20.GL_BACK;
public static final int FRONT_AND_BACK = GLES20.GL_FRONT_AND_BACK;
public static final int VIEWPORT = GLES20.GL_VIEWPORT;
public static final int SCISSOR_TEST = GLES20.GL_SCISSOR_TEST;
public static final int DEPTH_TEST = GLES20.GL_DEPTH_TEST;
public static final int DEPTH_WRITEMASK = GLES20.GL_DEPTH_WRITEMASK;
public static final int COLOR_BUFFER_BIT = GLES20.GL_COLOR_BUFFER_BIT;
public static final int DEPTH_BUFFER_BIT = GLES20.GL_DEPTH_BUFFER_BIT;
public static final int STENCIL_BUFFER_BIT = GLES20.GL_STENCIL_BUFFER_BIT;
public static final int FUNC_ADD = GLES20.GL_FUNC_ADD;
public static final int FUNC_MIN = 0x8007;
public static final int FUNC_MAX = 0x8008;
public static final int FUNC_REVERSE_SUBTRACT = GLES20.GL_FUNC_REVERSE_SUBTRACT;
public static final int TEXTURE_2D = GLES20.GL_TEXTURE_2D;
public static final int TEXTURE_BINDING_2D = GLES20.GL_TEXTURE_BINDING_2D;
public static final int RGB = GLES20.GL_RGB;
public static final int RGBA = GLES20.GL_RGBA;
public static final int ALPHA = GLES20.GL_ALPHA;
public static final int UNSIGNED_INT = GLES20.GL_UNSIGNED_INT;
public static final int UNSIGNED_BYTE = GLES20.GL_UNSIGNED_BYTE;
public static final int UNSIGNED_SHORT = GLES20.GL_UNSIGNED_SHORT;
public static final int FLOAT = GLES20.GL_FLOAT;
public static final int NEAREST = GLES20.GL_NEAREST;
public static final int LINEAR = GLES20.GL_LINEAR;
public static final int LINEAR_MIPMAP_NEAREST = GLES20.GL_LINEAR_MIPMAP_NEAREST;
public static final int LINEAR_MIPMAP_LINEAR = GLES20.GL_LINEAR_MIPMAP_LINEAR;
public static final int CLAMP_TO_EDGE = GLES20.GL_CLAMP_TO_EDGE;
public static final int REPEAT = GLES20.GL_REPEAT;
public static final int RGBA8 = -1;
public static final int DEPTH24_STENCIL8 = 0x88F0;
public static final int DEPTH_COMPONENT = GLES20.GL_DEPTH_COMPONENT;
public static final int DEPTH_COMPONENT16 = GLES20.GL_DEPTH_COMPONENT16;
public static final int DEPTH_COMPONENT24 = 0x81A6;
public static final int DEPTH_COMPONENT32 = 0x81A7;
public static final int STENCIL_INDEX = GLES20.GL_STENCIL_INDEX;
public static final int STENCIL_INDEX1 = 0x8D46;
public static final int STENCIL_INDEX4 = 0x8D47;
public static final int STENCIL_INDEX8 = GLES20.GL_STENCIL_INDEX8;
public static final int ARRAY_BUFFER = GLES20.GL_ARRAY_BUFFER;
public static final int ELEMENT_ARRAY_BUFFER = GLES20.GL_ELEMENT_ARRAY_BUFFER;
public static final int SAMPLES = GLES20.GL_SAMPLES;
public static final int FRAMEBUFFER_COMPLETE = GLES20.GL_FRAMEBUFFER_COMPLETE;
public static final int FRAMEBUFFER_INCOMPLETE_ATTACHMENT = GLES20.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
public static final int FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = GLES20.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
public static final int FRAMEBUFFER_INCOMPLETE_DIMENSIONS = GLES20.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
public static final int FRAMEBUFFER_INCOMPLETE_FORMATS = 0x8CDA;
public static final int FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER = -1;
public static final int FRAMEBUFFER_INCOMPLETE_READ_BUFFER = -1;
public static final int FRAMEBUFFER_UNSUPPORTED = GLES20.GL_FRAMEBUFFER_UNSUPPORTED;
public static final int STATIC_DRAW = GLES20.GL_STATIC_DRAW;
public static final int DYNAMIC_DRAW = GLES20.GL_DYNAMIC_DRAW;
public static final int STREAM_DRAW = GLES20.GL_STREAM_DRAW;
public static final int READ_ONLY = -1;
public static final int WRITE_ONLY = -1;
public static final int READ_WRITE = -1;
public static final int TRIANGLE_FAN = GLES20.GL_TRIANGLE_FAN;
public static final int TRIANGLE_STRIP = GLES20.GL_TRIANGLE_STRIP;
public static final int TRIANGLES = GLES20.GL_TRIANGLES;
public static final int VENDOR = GLES20.GL_VENDOR;
public static final int RENDERER = GLES20.GL_RENDERER;
public static final int VERSION = GLES20.GL_VERSION;
public static final int EXTENSIONS = GLES20.GL_EXTENSIONS;
public static final int SHADING_LANGUAGE_VERSION = GLES20.GL_SHADING_LANGUAGE_VERSION;
public static final int MAX_TEXTURE_SIZE = GLES20.GL_MAX_TEXTURE_SIZE;
public static final int MAX_SAMPLES = -1;
public static final int ALIASED_LINE_WIDTH_RANGE = GLES20.GL_ALIASED_LINE_WIDTH_RANGE;
public static final int ALIASED_POINT_SIZE_RANGE = GLES20.GL_ALIASED_POINT_SIZE_RANGE;
public static final int DEPTH_BITS = GLES20.GL_DEPTH_BITS;
public static final int STENCIL_BITS = GLES20.GL_STENCIL_BITS;
public static final int TESS_WINDING_NONZERO = PGLU.GLU_TESS_WINDING_NONZERO;
public static final int TESS_WINDING_ODD = PGLU.GLU_TESS_WINDING_ODD;
public static final int TEXTURE0 = GLES20.GL_TEXTURE0;
public static final int TEXTURE1 = GLES20.GL_TEXTURE1;
public static final int TEXTURE2 = GLES20.GL_TEXTURE2;
public static final int TEXTURE3 = GLES20.GL_TEXTURE3;
public static final int TEXTURE_MIN_FILTER = GLES20.GL_TEXTURE_MIN_FILTER;
public static final int TEXTURE_MAG_FILTER = GLES20.GL_TEXTURE_MAG_FILTER;
public static final int TEXTURE_WRAP_S = GLES20.GL_TEXTURE_WRAP_S;
public static final int TEXTURE_WRAP_T = GLES20.GL_TEXTURE_WRAP_T;
public static final int BLEND = GLES20.GL_BLEND;
public static final int ONE = GLES20.GL_ONE;
public static final int ZERO = GLES20.GL_ZERO;
public static final int SRC_ALPHA = GLES20.GL_SRC_ALPHA;
public static final int DST_ALPHA = GLES20.GL_DST_ALPHA;
public static final int ONE_MINUS_SRC_ALPHA = GLES20.GL_ONE_MINUS_SRC_ALPHA;
public static final int ONE_MINUS_DST_COLOR = GLES20.GL_ONE_MINUS_DST_COLOR;
public static final int ONE_MINUS_SRC_COLOR = GLES20.GL_ONE_MINUS_SRC_COLOR;
public static final int DST_COLOR = GLES20.GL_DST_COLOR;
public static final int SRC_COLOR = GLES20.GL_SRC_COLOR;
public static final int FRAMEBUFFER = GLES20.GL_FRAMEBUFFER;
public static final int COLOR_ATTACHMENT0 = GLES20.GL_COLOR_ATTACHMENT0;
public static final int COLOR_ATTACHMENT1 = -1;
public static final int COLOR_ATTACHMENT2 = -1;
public static final int COLOR_ATTACHMENT3 = -1;
public static final int RENDERBUFFER = GLES20.GL_RENDERBUFFER;
public static final int DEPTH_ATTACHMENT = GLES20.GL_DEPTH_ATTACHMENT;
public static final int STENCIL_ATTACHMENT = GLES20.GL_STENCIL_ATTACHMENT;
public static final int READ_FRAMEBUFFER = -1;
public static final int DRAW_FRAMEBUFFER = -1;
public static final int VERTEX_SHADER = GLES20.GL_VERTEX_SHADER;
public static final int FRAGMENT_SHADER = GLES20.GL_FRAGMENT_SHADER;
public static final int INFO_LOG_LENGTH = GLES20.GL_INFO_LOG_LENGTH;
public static final int SHADER_SOURCE_LENGTH = GLES20.GL_SHADER_SOURCE_LENGTH;
public static final int COMPILE_STATUS = GLES20.GL_COMPILE_STATUS;
public static final int LINK_STATUS = GLES20.GL_LINK_STATUS;
public static final int VALIDATE_STATUS = GLES20.GL_VALIDATE_STATUS;
public static final int MULTISAMPLE = -1;
public static final int POINT_SMOOTH = -1;
public static final int LINE_SMOOTH = -1;
public static final int POLYGON_SMOOTH = -1;
// Some EGL constants needed to initialize an GLES2 context.
protected static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
protected static final int EGL_OPENGL_ES2_BIT = 0x0004;
/** Basic GLES 1.0 interface */
public GL10 gl;
/** GLU interface **/
public PGLU glu;
/** The PGraphics object using this interface */
protected PGraphicsOpenGL pg;
/** Whether OpenGL has been initialized or not */
protected boolean initialized;
/** The renderer object driving the rendering loop,
* analogous to the GLEventListener in JOGL */
protected AndroidRenderer renderer;
/** Which texturing targets are enabled */
protected static boolean[] texturingTargets = { false };
/** Which textures are bound to each target */
protected static int[] boundTextures = { 0 };
///////////////////////////////////////////////////////////////////////////////////
// FBO for incremental drawing
protected static final boolean FORCE_SCREEN_FBO = false;
protected boolean firstOnscreenFrame = true;
protected int fboWidth, fboHeight;
protected int backTex, frontTex;
protected int[] glColorTex = { 0, 0 };
protected int[] glColorFbo = { 0 };
///////////////////////////////////////////////////////////////////////////////////
// Texture rendering
protected boolean loadedTexShader = false;
protected int texShaderProgram;
protected int texVertShader;
protected int texFragShader;
protected int texVertLoc;
protected int texTCoordLoc;
protected float[] texCoords = {
// X, Y, U, V
-1.0f, -1.0f, 0.0f, 0.0f,
+1.0f, -1.0f, 1.0f, 0.0f,
-1.0f, +1.0f, 0.0f, 1.0f,
+1.0f, +1.0f, 1.0f, 1.0f
};
protected FloatBuffer texData;
protected String texVertShaderSource = "attribute vec2 inVertex;" +
"attribute vec2 inTexcoord;" +
"varying vec2 vertTexcoord;" +
"void main() {" +
" gl_Position = vec4(inVertex, 0, 1);" +
" vertTexcoord = inTexcoord;" +
"}";
protected String texFragShaderSource = SHADER_PREPROCESSOR_DIRECTIVE +
"uniform sampler2D textureSampler;" +
"varying vec2 vertTexcoord;" +
"void main() {" +
" gl_FragColor = texture2D(textureSampler, vertTexcoord.st);" +
"}";
///////////////////////////////////////////////////////////////////////////////////
// Rectangle rendering
protected boolean loadedRectShader = false;
protected int rectShaderProgram;
protected int rectVertShader;
protected int rectFragShader;
protected int rectVertLoc;
protected int rectColorLoc;
protected float[] rectCoords = {
// X, Y
-1.0f, -1.0f,
+1.0f, -1.0f,
-1.0f, +1.0f,
+1.0f, +1.0f,
};
protected FloatBuffer rectData;
protected String rectVertShaderSource = "attribute vec2 inVertex;" +
"void main() {" +
" gl_Position = vec4(inVertex, 0, 1);" +
"}";
protected String rectFragShaderSource = SHADER_PREPROCESSOR_DIRECTIVE +
"uniform vec4 rectColor;" +
"void main() {" +
" gl_FragColor = rectColor;" +
"}";
///////////////////////////////////////////////////////////////////////////////////
// 1-pixel color, depth, stencil buffers
protected IntBuffer colorBuffer;
protected FloatBuffer depthBuffer;
protected ByteBuffer stencilBuffer;
///////////////////////////////////////////////////////////////////////////////////
// Intialization, finalization
public PGL(PGraphicsOpenGL pg) {
this.pg = pg;
renderer = new AndroidRenderer();
glu = new PGLU();
initialized = false;
}
protected void setFrameRate(float framerate) {
}
protected void initPrimarySurface(int antialias) {
// We do the initialization in updatePrimary() because
// at the moment initPrimarySurface() gets called we
// cannot rely on the GL surface actually being
// available.
}
protected void initOffscreenSurface(PGL primary) {
initialized = true;
}
protected void updatePrimary() {
if (!initialized) {
String ext = GLES20.glGetString(GLES20.GL_EXTENSIONS);
if (-1 < ext.indexOf("texture_non_power_of_two")) {
fboWidth = pg.width;
fboHeight = pg.height;
} else {
fboWidth = PGL.nextPowerOfTwo(pg.width);
fboHeight = PGL.nextPowerOfTwo(pg.height);
}
boolean packed = ext.indexOf("packed_depth_stencil") != -1;
// We create the GL resources we need to draw incrementally, ie: not clearing
// the screen in each frame. Because the way Android handles double buffering
// we need to handle our own custom buffering using FBOs.
GLES20.glGenTextures(2, glColorTex, 0);
for (int i = 0; i < 2; i++) {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, glColorTex[i]);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, fboWidth, fboHeight, 0, PGL.RGBA, PGL.UNSIGNED_BYTE, null);
initTexture(GLES20.GL_TEXTURE_2D, PGL.RGBA, fboWidth, fboHeight);
}
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
GLES20.glGenFramebuffers(1, glColorFbo, 0);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, glColorFbo[0]);
if (packed) { // packed depth+stencil buffer
int[] depthStencil = { 0 };
GLES20.glGenRenderbuffers(1, depthStencil, 0);
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, depthStencil[0]);
GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, DEPTH24_STENCIL8, fboWidth, fboHeight);
GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER, depthStencil[0]);
GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_STENCIL_ATTACHMENT, GLES20.GL_RENDERBUFFER, depthStencil[0]);
} else { // separate depth and stencil buffers
int[] depth = { 0 };
int[] stencil = { 0 };
GLES20.glGenRenderbuffers(1, depth, 0);
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, depth[0]);
GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, fboWidth, fboHeight);
GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER, depth[0]);
int[] temp = new int[1];
GLES20.glGetIntegerv(GLES20.GL_STENCIL_BITS, temp, 0);
int stencilBits = temp[0];
if (stencilBits == 8) {
GLES20.glGenRenderbuffers(1, stencil, 0);
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, stencil[0]);
GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_STENCIL_INDEX8, fboWidth, fboHeight);
GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_STENCIL_ATTACHMENT, GLES20.GL_RENDERBUFFER, stencil[0]);
}
}
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
backTex = 1;
frontTex = 0;
initialized = true;
}
}
protected void updateOffscreen(PGL primary) {
gl = primary.gl;
}
protected boolean primaryIsDoubleBuffered() {
return PGraphicsOpenGL.screenFramebuffer.glFbo != 0;
}
protected boolean primaryIsFboBacked() {
return PGraphicsOpenGL.screenFramebuffer.glFbo != 0;
}
protected int getFboTexTarget() {
return GLES20.GL_TEXTURE_2D;
}
protected int getFboTexName() {
return glColorTex[0];
}
protected int getFboWidth() {
return fboWidth;
}
protected int getFboHeight() {
return fboHeight;
}
protected void bindPrimaryColorFBO() {
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, glColorFbo[0]);
PGraphicsOpenGL.screenFramebuffer.glFbo = glColorFbo[0];
// Make the color buffer opaque so it doesn't show
// the background when drawn on top of another surface.
GLES20.glColorMask(false, false, false, true);
GLES20.glClearColor(0, 0, 0, 1);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glColorMask(true, true, true, true);
}
protected void bindPrimaryMultiFBO() {
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, glColorFbo[0]);
PGraphicsOpenGL.screenFramebuffer.glFbo = glColorFbo[0];
}
///////////////////////////////////////////////////////////////////////////////////
// Frame rendering
protected void beginOnscreenDraw(boolean clear) {
if (clear && !FORCE_SCREEN_FBO) {
// Simplest scenario: clear mode means we clear both the color and depth buffers.
// No need for saving front color buffer, etc.
GLES20.glClearColor(0, 0, 0, 0);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
PGraphicsOpenGL.screenFramebuffer.glFbo = 0;
} else {
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, glColorFbo[0]);
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, glColorTex[frontTex], 0);
validateFramebuffer();
// We need to save the color buffer after finishing with the rendering of this frame,
// to use is as the background for the next frame ("incremental drawing").
GLES20.glClearColor(0, 0, 0, 0);
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_STENCIL_BUFFER_BIT);
if (firstOnscreenFrame) {
// No need to draw back color buffer because we are in the first frame.
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
} else {
// Render previous draw texture as background.
drawTexture(GLES20.GL_TEXTURE_2D, glColorTex[backTex], fboWidth, fboHeight, 0, 0, pg.width, pg.height, 0, 0, pg.width, pg.height);
}
PGraphicsOpenGL.screenFramebuffer.glFbo = glColorFbo[0];
}
if (firstOnscreenFrame) {
firstOnscreenFrame = false;
}
}
protected void endOnscreenDraw(boolean clear0) {
if (!clear0 || FORCE_SCREEN_FBO) {
// We are in the primary surface, and no clear mode, this means that the current
// contents of the front buffer needs to be used in the next frame as the background.
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
GLES20.glClearDepthf(1);
GLES20.glClearColor(0, 0, 0, 0);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
// Render current front texture to screen, without blending.
GLES20.glDisable(GLES20.GL_BLEND);
drawTexture(GLES20.GL_TEXTURE_2D, glColorTex[frontTex], fboWidth, fboHeight, 0, 0, pg.width, pg.height, 0, 0, pg.width, pg.height);
// Swapping front and back textures.
int temp = frontTex;
frontTex = backTex;
backTex = temp;
}
}
protected void beginOffscreenDraw(boolean clear) {
}
protected void endOffscreenDraw(boolean clear0) {
}
protected boolean canDraw() {
return true;
}
protected void requestDraw() {
pg.parent.andresNeedsBetterAPI();
}
///////////////////////////////////////////////////////////////////////////////////
// Caps query
public String getString(int name) {
return GLES20.glGetString(name);
}
public void getIntegerv(int name, int[] values, int offset) {
if (-1 < name) {
GLES20.glGetIntegerv(name, values, offset);
} else {
Arrays.fill(values, 0);
}
}
public void getBooleanv(int name, boolean[] values, int offset) {
if (-1 < name) {
GLES20.glGetBooleanv(name, values, offset);
} else {
Arrays.fill(values, false);
}
}
///////////////////////////////////////////////////////////////////////////////////
// Enable/disable caps
public void enable(int cap) {
if (-1 < cap) {
GLES20.glEnable(cap);
}
}
public void disable(int cap) {
if (-1 < cap) {
GLES20.glDisable(cap);
}
}
///////////////////////////////////////////////////////////////////////////////////
// Render control
public void flush() {
GLES20.glFlush();
}
public void finish() {
GLES20.glFinish();
}
/////////////////////////////////////////////////////////////////////////////////
// Error handling
public int getError() {
return GLES20.glGetError();
}
public String errorString(int err) {
return GLU.gluErrorString(err);
}
/////////////////////////////////////////////////////////////////////////////////
// Rendering options
public void frontFace(int mode) {
GLES20.glFrontFace(mode);
}
public void cullFace(int mode) {
GLES20.glCullFace(mode);
}
public void depthMask(boolean flag) {
GLES20.glDepthMask(flag);
}
public void depthFunc(int func) {
GLES20.glDepthFunc(func);
}
/////////////////////////////////////////////////////////////////////////////////
// Textures
public void genTextures(int n, int[] ids, int offset) {
GLES20.glGenTextures(n, ids, offset);
}
public void deleteTextures(int n, int[] ids, int offset) {
GLES20.glDeleteTextures(n, ids, offset);
}
public void activeTexture(int unit) {
GLES20.glActiveTexture(unit);
}
public void bindTexture(int target, int id) {
GLES20.glBindTexture(target, id);
if (target == TEXTURE_2D) {
boundTextures[0] = id;
}
}
public void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, Buffer data) {
GLES20.glTexImage2D(target, level, internalFormat, width, height, border, format, type, data);
}
public void texSubImage2D(int target, int level, int xOffset, int yOffset, int width, int height, int format, int type, Buffer data) {
GLES20.glTexSubImage2D(target, level, xOffset, yOffset, width, height, format, type, data);
}
public void texParameteri(int target, int param, int value) {
GLES20.glTexParameteri(target, param, value);
}
public void getTexParameteriv(int target, int param, int[] values, int offset) {
GLES20.glGetTexParameteriv(target, param, values, offset);
}
public void generateMipmap(int target) {
GLES20.glGenerateMipmap(target);
}
/////////////////////////////////////////////////////////////////////////////////
// Vertex Buffers
public void genBuffers(int n, int[] ids, int offset) {
GLES20.glGenBuffers(n, ids, offset);
}
public void deleteBuffers(int n, int[] ids, int offset) {
GLES20.glDeleteBuffers(n, ids, offset);
}
public void bindBuffer(int target, int id) {
GLES20.glBindBuffer(target, id);
}
public void bufferData(int target, int size, Buffer data, int usage) {
GLES20.glBufferData(target, size, data, usage);
}
public void bufferSubData(int target, int offset, int size, Buffer data) {
GLES20.glBufferSubData(target, offset, size, data);
}
public void drawArrays(int mode, int first, int count) {
GLES20.glDrawArrays(mode, first, count);
}
public void drawElements(int mode, int count, int type, int offset) {
GLES20.glDrawElements(mode, count, type, offset);
}
public void enableVertexAttribArray(int loc) {
GLES20.glEnableVertexAttribArray(loc);
}
public void disableVertexAttribArray(int loc) {
GLES20.glDisableVertexAttribArray(loc);
}
public void vertexAttribPointer(int loc, int size, int type, boolean normalized, int stride, int offset) {
GLES20.glVertexAttribPointer(loc, size, type, normalized, stride, offset);
}
public void vertexAttribPointer(int loc, int size, int type, boolean normalized, int stride, Buffer data) {
GLES20.glVertexAttribPointer(loc, size, type, normalized, stride, data);
}
public ByteBuffer mapBuffer(int target, int access) {
//return gl2f.glMapBuffer(GL.GL_ARRAY_BUFFER, GL2.GL_READ_WRITE);
return null;
}
public ByteBuffer mapBufferRange(int target, int offset, int length, int access) {
//return gl2x.glMapBufferRange(GL.GL_ARRAY_BUFFER, offset, length, GL2.GL_READ_WRITE);
return null;
}
public void unmapBuffer(int target) {
//gl2f.glUnmapBuffer(GL.GL_ARRAY_BUFFER);
}
/////////////////////////////////////////////////////////////////////////////////
// Framebuffers, renderbuffers
public void genFramebuffers(int n, int[] ids, int offset) {
GLES20.glGenFramebuffers(n, ids, offset);
}
public void deleteFramebuffers(int n, int[] ids, int offset) {
GLES20.glDeleteFramebuffers(n, ids, offset);
}
public void genRenderbuffers(int n, int[] ids, int offset) {
GLES20.glGenRenderbuffers(n, ids, offset);
}
public void deleteRenderbuffers(int n, int[] ids, int offset) {
GLES20.glDeleteRenderbuffers(n, ids, offset);
}
public void bindFramebuffer(int target, int id) {
GLES20.glBindFramebuffer(target, id);
}
public void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
// GLES20.glBlitFramebuffer(0, 0, srcW, srcH, 0, 0, destW, destH, GLES20.GL_COLOR_BUFFER_BIT, GLES20.GL_NEAREST);
}
public void framebufferTexture2D(int target, int attachment, int texTarget, int texId, int level) {
GLES20.glFramebufferTexture2D(target, attachment, texTarget, texId, level);
}
public void bindRenderbuffer(int target, int id) {
GLES20.glBindRenderbuffer(target, id);
}
public void renderbufferStorageMultisample(int target, int samples, int format, int width, int height) {
// GLES20.glRenderbufferStorageMultisample(GLES20.GL_RENDERBUFFER, samples, format, w, h);
}
public void renderbufferStorage(int target, int format, int width, int height) {
GLES20.glRenderbufferStorage(target, format, width, height);
}
public void framebufferRenderbuffer(int target, int attachment, int rendbufTarget, int rendbufId) {
GLES20.glFramebufferRenderbuffer(target, attachment, rendbufTarget, rendbufId);
}
public int checkFramebufferStatus(int target) {
return GLES20.glCheckFramebufferStatus(target);
}
/////////////////////////////////////////////////////////////////////////////////
// Shaders
public int createProgram() {
return GLES20.glCreateProgram();
}
public void deleteProgram(int id) {
GLES20.glDeleteProgram(id);
}
public int createShader(int type) {
return GLES20.glCreateShader(type);
}
public void deleteShader(int id) {
GLES20.glDeleteShader(id);
}
public void linkProgram(int prog) {
GLES20.glLinkProgram(prog);
}
public void validateProgram(int prog) {
GLES20.glValidateProgram(prog);
}
public void useProgram(int prog) {
GLES20.glUseProgram(prog);
}
public int getAttribLocation(int prog, String name) {
return GLES20.glGetAttribLocation(prog, name);
}
public int getUniformLocation(int prog, String name) {
return GLES20.glGetUniformLocation(prog, name);
}
public void uniform1i(int loc, int value) {
GLES20.glUniform1i(loc, value);
}
public void uniform2i(int loc, int value0, int value1) {
GLES20.glUniform2i(loc, value0, value1);
}
public void uniform3i(int loc, int value0, int value1, int value2) {
GLES20.glUniform3i(loc, value0, value1, value2);
}
public void uniform4i(int loc, int value0, int value1, int value2, int value3) {
GLES20.glUniform4i(loc, value0, value1, value2, value3);
}
public void uniform1f(int loc, float value) {
GLES20.glUniform1f(loc, value);
}
public void uniform2f(int loc, float value0, float value1) {
GLES20.glUniform2f(loc, value0, value1);
}
public void uniform3f(int loc, float value0, float value1, float value2) {
GLES20.glUniform3f(loc, value0, value1, value2);
}
public void uniform4f(int loc, float value0, float value1, float value2, float value3) {
GLES20.glUniform4f(loc, value0, value1, value2, value3);
}
public void uniform1iv(int loc, int count, int[] v, int offset) {
GLES20.glUniform1iv(loc, count, v, offset);
}
public void uniform2iv(int loc, int count, int[] v, int offset) {
GLES20.glUniform2iv(loc, count, v, offset);
}
public void uniform3iv(int loc, int count, int[] v, int offset) {
GLES20.glUniform3iv(loc, count, v, offset);
}
public void uniform4iv(int loc, int count, int[] v, int offset) {
GLES20.glUniform4iv(loc, count, v, offset);
}
public void uniform1fv(int loc, int count, float[] v, int offset) {
GLES20.glUniform1fv(loc, count, v, offset);
}
public void uniform2fv(int loc, int count, float[] v, int offset) {
GLES20.glUniform2fv(loc, count, v, offset);
}
public void uniform3fv(int loc, int count, float[] v, int offset) {
GLES20.glUniform3fv(loc, count, v, offset);
}
public void uniform4fv(int loc, int count, float[] v, int offset) {
GLES20.glUniform4fv(loc, count, v, offset);
}
public void uniformMatrix2fv(int loc, int count, boolean transpose, float[] mat, int offset) {
GLES20.glUniformMatrix2fv(loc, count, transpose, mat, offset);
}
public void uniformMatrix3fv(int loc, int count, boolean transpose, float[] mat, int offset) {
GLES20.glUniformMatrix3fv(loc, count, transpose, mat, offset);
}
public void uniformMatrix4fv(int loc, int count, boolean transpose, float[] mat, int offset) {
GLES20.glUniformMatrix4fv(loc, count, transpose, mat, offset);
}
public void vertexAttrib1f(int loc, float value) {
GLES20.glVertexAttrib1f(loc, value);
}
public void vertexAttrib2f(int loc, float value0, float value1) {
GLES20.glVertexAttrib2f(loc, value0, value1);
}
public void vertexAttrib3f(int loc, float value0, float value1, float value2) {
GLES20.glVertexAttrib3f(loc, value0, value1, value2);
}
public void vertexAttrib4f(int loc, float value0, float value1, float value2, float value3) {
GLES20.glVertexAttrib4f(loc, value0, value1, value2, value3);
}
public void vertexAttrib1fv(int loc, float[] v, int offset) {
GLES20.glVertexAttrib1fv(loc, v, offset);
}
public void vertexAttrib2fv(int loc, float[] v, int offset) {
GLES20.glVertexAttrib2fv(loc, v, offset);
}
public void vertexAttrib3fv(int loc, float[] v, int offset) {
GLES20.glVertexAttrib3fv(loc, v, offset);
}
public void vertexAttrib4fv(int loc, float[] v, int offset) {
GLES20.glVertexAttrib4fv(loc, v, offset);
}
public void shaderSource(int id, String source) {
GLES20.glShaderSource(id, source);
}
public void compileShader(int id) {
GLES20.glCompileShader(id);
}
public void attachShader(int prog, int shader) {
GLES20.glAttachShader(prog, shader);
}
public void getShaderiv(int shader, int pname, int[] params, int offset) {
GLES20.glGetShaderiv(shader, pname, params, offset);
}
public String getShaderInfoLog(int shader) {
return GLES20.glGetShaderInfoLog(shader);
}
public void getProgramiv(int prog, int pname, int[] params, int offset) {
GLES20.glGetProgramiv(prog, pname, params, offset);
}
public String getProgramInfoLog(int prog) {
return GLES20.glGetProgramInfoLog(prog);
}
/////////////////////////////////////////////////////////////////////////////////
// Viewport
public void viewport(int x, int y, int width, int height) {
GLES20.glViewport(x, y, width, height);
}
/////////////////////////////////////////////////////////////////////////////////
// Clipping (scissor test)
public void scissor(int x, int y, int w, int h) {
GLES20.glScissor(x, y, w, h);
}
/////////////////////////////////////////////////////////////////////////////////
// Blending
public void blendEquation(int eq) {
GLES20.glBlendEquation(eq);
}
public void blendFunc(int srcFactor, int dstFactor) {
GLES20.glBlendFunc(srcFactor, dstFactor);
}
/////////////////////////////////////////////////////////////////////////////////
// Pixels
public void readBuffer(int buf) {
// GLES20.glReadBuffer(buf);
}
public void readPixels(int x, int y, int width, int height, int format, int type, Buffer buffer) {
GLES20.glReadPixels(x, y, width, height, format, type, buffer);
}
public void drawBuffer(int buf) {
// GLES20.glDrawBuffer(GLES20.GL_COLOR_ATTACHMENT0 + buf);
}
public void clearDepth(float d) {
GLES20.glClearDepthf(d);
}
public void clearStencil(int s) {
GLES20.glClearStencil(s);
}
public void colorMask(boolean wr, boolean wg, boolean wb, boolean wa) {
GLES20.glColorMask(wr, wg, wb, wa);
}
public void clearColor(float r, float g, float b, float a) {
GLES20.glClearColor(r, g, b, a);
}
public void clear(int mask) {
GLES20.glClear(mask);
}
/////////////////////////////////////////////////////////////////////////////////
// Context interface
protected Context createEmptyContext() {
return new Context();
}
protected Context getCurrentContext() {
return new Context();
}
protected class Context {
Context() {
}
boolean current() {
return true;
}
boolean equal() {
return true;
}
int code() {
return 0;
}
}
/////////////////////////////////////////////////////////////////////////////////
// Tessellator interface
protected Tessellator createTessellator(TessellatorCallback callback) {
return new Tessellator(callback);
}
protected class Tessellator {
protected PGLUtessellator tess;
protected TessellatorCallback callback;
protected GLUCallback gluCallback;
public Tessellator(TessellatorCallback callback) {
this.callback = callback;
tess = PGLU.gluNewTess();
gluCallback = new GLUCallback();
PGLU.gluTessCallback(tess, PGLU.GLU_TESS_BEGIN, gluCallback);
PGLU.gluTessCallback(tess, PGLU.GLU_TESS_END, gluCallback);
PGLU.gluTessCallback(tess, PGLU.GLU_TESS_VERTEX, gluCallback);
PGLU.gluTessCallback(tess, PGLU.GLU_TESS_COMBINE, gluCallback);
PGLU.gluTessCallback(tess, PGLU.GLU_TESS_ERROR, gluCallback);
}
public void beginPolygon() {
PGLU.gluTessBeginPolygon(tess, null);
}
public void endPolygon() {
PGLU.gluTessEndPolygon(tess);
}
public void setWindingRule(int rule) {
PGLU.gluTessProperty(tess, PGLU.GLU_TESS_WINDING_RULE, rule);
}
public void beginContour() {
PGLU.gluTessBeginContour(tess);
}
public void endContour() {
PGLU.gluTessEndContour(tess);
}
public void addVertex(double[] v) {
PGLU.gluTessVertex(tess, v, 0, v);
}
protected class GLUCallback extends PGLUtessellatorCallbackAdapter {
public void begin(int type) {
callback.begin(type);
}
public void end() {
callback.end();
}
public void vertex(Object data) {
callback.vertex(data);
}
public void combine(double[] coords, Object[] data,
float[] weight, Object[] outData) {
callback.combine(coords, data, weight, outData);
}
public void error(int errnum) {
callback.error(errnum);
}
}
}
protected String tessError(int err) {
return PGLU.gluErrorString(err);
}
protected interface TessellatorCallback {
public void begin(int type);
public void end();
public void vertex(Object data);
public void combine(double[] coords, Object[] data,
float[] weight, Object[] outData);
public void error(int errnum);
}
///////////////////////////////////////////////////////////////////////////////////
// Utility functions
protected boolean contextIsCurrent(Context other) {
return other == null || other.current();
}
protected void enableTexturing(int target) {
if (target == TEXTURE_2D) {
texturingTargets[0] = true;
}
}
protected void disableTexturing(int target) {
if (target == TEXTURE_2D) {
texturingTargets[0] = false;
}
}
protected boolean texturingIsEnabled(int target) {
if (target == TEXTURE_2D) {
return texturingTargets[0];
} else {
return false;
}
}
protected boolean textureIsBound(int target, int id) {
if (target == TEXTURE_2D) {
return boundTextures[0] == id;
} else {
return false;
}
}
public void initTexture(int target, int format, int width, int height) {
// Doing in patches of 16x16 pixels to avoid creating a (potentially)
// very large transient array which in certain situations (memory-
// constrained android devices) might lead to an out-of-memory error.
int[] texels = new int[16 * 16];
for (int y = 0; y < height; y += 16) {
int h = PApplet.min(16, height - y);
for (int x = 0; x < width; x += 16) {
int w = PApplet.min(16, width - x);
texSubImage2D(target, 0, x, y, w, h, format, UNSIGNED_BYTE, IntBuffer.wrap(texels));
}
}
}
public void copyToTexture(int target, int format, int id, int x, int y, int w, int h, IntBuffer buffer) {
activeTexture(TEXTURE0);
boolean enabledTex = false;
if (!texturingIsEnabled(target)) {
enableTexturing(target);
enabledTex = true;
}
bindTexture(target, id);
texSubImage2D(target, 0, x, y, w, h, format, UNSIGNED_BYTE, buffer);
bindTexture(target, 0);
if (enabledTex) {
disableTexturing(target);
}
}
public void drawTexture(int target, int id, int width, int height,
int X0, int Y0, int X1, int Y1) {
drawTexture(target, id, width, height, X0, Y0, X1, Y1, X0, Y0, X1, Y1);
}
public void drawTexture(int target, int id, int width, int height,
int texX0, int texY0, int texX1, int texY1,
int scrX0, int scrY0, int scrX1, int scrY1) {
if (!loadedTexShader) {
texVertShader = createShader(VERTEX_SHADER, texVertShaderSource);
texFragShader = createShader(FRAGMENT_SHADER, texFragShaderSource);
if (0 < texVertShader && 0 < texFragShader) {
texShaderProgram = createProgram(texVertShader, texFragShader);
}
if (0 < texShaderProgram) {
texVertLoc = getAttribLocation(texShaderProgram, "inVertex");
texTCoordLoc = getAttribLocation(texShaderProgram, "inTexcoord");
}
texData = allocateDirectFloatBuffer(texCoords.length);
loadedTexShader = true;
}
if (0 < texShaderProgram) {
// The texture overwrites anything drawn earlier.
boolean[] depthTest = new boolean[1];
getBooleanv(DEPTH_TEST, depthTest, 0);
disable(DEPTH_TEST);
// When drawing the texture we don't write to the
// depth mask, so the texture remains in the background
// and can be occluded by anything drawn later, even if
// if it is behind it.
boolean[] depthMask = new boolean[1];
getBooleanv(DEPTH_WRITEMASK, depthMask, 0);
depthMask(false);
useProgram(texShaderProgram);
enableVertexAttribArray(texVertLoc);
enableVertexAttribArray(texTCoordLoc);
// Vertex coordinates of the textured quad are specified
// in normalized screen space (-1, 1):
// Corner 1
texCoords[ 0] = 2 * (float)scrX0 / pg.width - 1;
texCoords[ 1] = 2 * (float)scrY0 / pg.height - 1;
texCoords[ 2] = (float)texX0 / width;
texCoords[ 3] = (float)texY0 / height;
// Corner 2
texCoords[ 4] = 2 * (float)scrX1 / pg.width - 1;
texCoords[ 5] = 2 * (float)scrY0 / pg.height - 1;
texCoords[ 6] = (float)texX1 / width;
texCoords[ 7] = (float)texY0 / height;
// Corner 3
texCoords[ 8] = 2 * (float)scrX0 / pg.width - 1;
texCoords[ 9] = 2 * (float)scrY1 / pg.height - 1;
texCoords[10] = (float)texX0 / width;
texCoords[11] = (float)texY1 / height;
// Corner 4
texCoords[12] = 2 * (float)scrX1 / pg.width - 1;
texCoords[13] = 2 * (float)scrY1 / pg.height - 1;
texCoords[14] = (float)texX1 / width;
texCoords[15] = (float)texY1 / height;
texData.rewind();
texData.put(texCoords);
activeTexture(TEXTURE0);
boolean enabledTex = false;
if (!texturingIsEnabled(TEXTURE_2D)) {
enableTexturing(TEXTURE_2D);
enabledTex = true;
}
bindTexture(target, id);
texData.position(0);
vertexAttribPointer(texVertLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, texData);
texData.position(2);
vertexAttribPointer(texTCoordLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, texData);
drawArrays(TRIANGLE_STRIP, 0, 4);
bindTexture(target, 0);
if (enabledTex) {
disableTexturing(TEXTURE_2D);
}
disableVertexAttribArray(texVertLoc);
disableVertexAttribArray(texTCoordLoc);
useProgram(0);
if (depthTest[0]) {
enable(DEPTH_TEST);
} else {
disable(DEPTH_TEST);
}
depthMask(depthMask[0]);
}
}
public void drawRectangle(float r, float g, float b, float a,
int scrX0, int scrY0, int scrX1, int scrY1) {
if (!loadedRectShader) {
rectVertShader = createShader(VERTEX_SHADER, rectVertShaderSource);
rectFragShader = createShader(FRAGMENT_SHADER, rectFragShaderSource);
if (0 < rectVertShader && 0 < rectFragShader) {
rectShaderProgram = createProgram(rectVertShader, rectFragShader);
}
if (0 < rectShaderProgram) {
rectVertLoc = getAttribLocation(rectShaderProgram, "inVertex");
rectColorLoc = getUniformLocation(rectShaderProgram, "rectColor");
}
rectData = allocateDirectFloatBuffer(rectCoords.length);
loadedRectShader = true;
}
if (0 < rectShaderProgram) {
// The rectangle overwrites anything drawn earlier.
boolean[] depthTest = new boolean[1];
getBooleanv(DEPTH_TEST, depthTest, 0);
disable(DEPTH_TEST);
// When drawing the rectangle we don't write to the
// depth mask, so the rectangle remains in the background
// and can be occluded by anything drawn later, even if
// if it is behind it.
boolean[] depthMask = new boolean[1];
getBooleanv(DEPTH_WRITEMASK, depthMask, 0);
depthMask(false);
useProgram(rectShaderProgram);
enableVertexAttribArray(rectVertLoc);
uniform4f(rectColorLoc, r, g, b, a);
// Vertex coordinates of the rectangle are specified
// in normalized screen space (-1, 1):
// Corner 1
rectCoords[0] = 2 * (float)scrX0 / pg.width - 1;
rectCoords[1] = 2 * (float)scrY0 / pg.height - 1;
// Corner 2
rectCoords[2] = 2 * (float)scrX1 / pg.width - 1;
rectCoords[3] = 2 * (float)scrY0 / pg.height - 1;
// Corner 3
rectCoords[4] = 2 * (float)scrX0 / pg.width - 1;
rectCoords[5] = 2 * (float)scrY1 / pg.height - 1;
// Corner 4
rectCoords[6] = 2 * (float)scrX1 / pg.width - 1;
rectCoords[7] = 2 * (float)scrY1 / pg.height - 1;
rectData.rewind();
rectData.put(rectCoords);
rectData.position(0);
vertexAttribPointer(rectVertLoc, 2, FLOAT, false, 2 * SIZEOF_FLOAT, rectData);
drawArrays(TRIANGLE_STRIP, 0, 4);
disableVertexAttribArray(rectVertLoc);
useProgram(0);
if (depthTest[0]) {
enable(DEPTH_TEST);
} else {
disable(DEPTH_TEST);
}
depthMask(depthMask[0]);
}
}
public int getColorValue(int scrX, int scrY) {
if (colorBuffer == null) {
colorBuffer = IntBuffer.allocate(1);
}
colorBuffer.rewind();
readPixels(scrX, pg.height - scrY - 1, 1, 1, RGBA, UNSIGNED_BYTE, colorBuffer);
return colorBuffer.get();
}
public float getDepthValue(int scrX, int scrY) {
// http://stackoverflow.com/questions/2596682/opengl-es-2-0-read-depth-buffer
return 0;
}
public byte getStencilValue(int scrX, int scrY) {
return 0;
}
// bit shifting this might be more efficient
public static int nextPowerOfTwo(int val) {
int ret = 1;
while (ret < val) {
ret <<= 1;
}
return ret;
}
/**
* Converts input native OpenGL value (RGBA on big endian, ABGR on little
* endian) to Java ARGB.
*/
public static int nativeToJavaARGB(int color) {
if (BIG_ENDIAN) { // RGBA to ARGB
return (color & 0xff000000) |
((color >> 8) & 0x00ffffff);
} else { // ABGR to ARGB
return (color & 0xff000000) |
((color << 16) & 0xff0000) |
(color & 0xff00) |
((color >> 16) & 0xff);
}
}
/**
* Converts input array of native OpenGL values (RGBA on big endian, ABGR on little
* endian) representing an image of width x height resolution to Java ARGB.
* It also rearranges the elements in the array so that the image is flipped
* vertically.
*/
public static void nativeToJavaARGB(int[] pixels, int width, int height) {
int index = 0;
int yindex = (height - 1) * width;
for (int y = 0; y < height / 2; y++) {
if (BIG_ENDIAN) { // RGBA to ARGB
for (int x = 0; x < width; x++) {
int temp = pixels[index];
pixels[index] = (pixels[yindex] & 0xff000000) |
((pixels[yindex] >> 8) & 0x00ffffff);
pixels[yindex] = (temp & 0xff000000) |
((temp >> 8) & 0x00ffffff);
index++;
yindex++;
}
} else { // ABGR to ARGB
for (int x = 0; x < width; x++) {
int temp = pixels[index];
pixels[index] = (pixels[yindex] & 0xff000000) |
((pixels[yindex] << 16) & 0xff0000) |
(pixels[yindex] & 0xff00) |
((pixels[yindex] >> 16) & 0xff);
pixels[yindex] = (temp & 0xff000000) |
((temp << 16) & 0xff0000) |
(temp & 0xff00) |
((temp >> 16) & 0xff);
index++;
yindex++;
}
}
yindex -= width * 2;
}
// Flips image
if ((height % 2) == 1) {
index = (height / 2) * width;
if (BIG_ENDIAN) { // RGBA to ARGB
for (int x = 0; x < width; x++) {
pixels[index] = (pixels[index] & 0xff000000) |
((pixels[index] >> 8) & 0x00ffffff);
index++;
}
} else { // ABGR to ARGB
for (int x = 0; x < width; x++) {
pixels[index] = (pixels[index] & 0xff000000) |
((pixels[index] << 16) & 0xff0000) |
(pixels[index] & 0xff00) |
((pixels[index] >> 16) & 0xff);
index++;
}
}
}
}
/**
* Converts input native OpenGL value (RGBA on big endian, ABGR on little
* endian) to Java RGB, so that the alpha component of the result is set
* to opaque (255).
*/
public static int nativeToJavaRGB(int color) {
if (BIG_ENDIAN) { // RGBA to ARGB
return ((color << 8) & 0xffffff00) | 0xff;
} else { // ABGR to ARGB
return 0xff000000 | ((color << 16) & 0xff0000) |
(color & 0xff00) |
((color >> 16) & 0xff);
}
}
/**
* Converts input array of native OpenGL values (RGBA on big endian, ABGR on little
* endian) representing an image of width x height resolution to Java RGB,
* so that the alpha component of all pixels is set to opaque (255). It also
* rearranges the elements in the array so that the image is flipped vertically.
*/
public static void nativeToJavaRGB(int[] pixels, int width, int height) {
int index = 0;
int yindex = (height - 1) * width;
for (int y = 0; y < height / 2; y++) {
if (BIG_ENDIAN) { // RGBA to ARGB
for (int x = 0; x < width; x++) {
int temp = pixels[index];
pixels[index] = 0xff000000 | ((pixels[yindex] >> 8) & 0x00ffffff);
pixels[yindex] = 0xff000000 | ((temp >> 8) & 0x00ffffff);
index++;
yindex++;
}
} else { // ABGR to ARGB
for (int x = 0; x < width; x++) {
int temp = pixels[index];
pixels[index] = 0xff000000 | ((pixels[yindex] << 16) & 0xff0000) |
(pixels[yindex] & 0xff00) |
((pixels[yindex] >> 16) & 0xff);
pixels[yindex] = 0xff000000 | ((temp << 16) & 0xff0000) |
(temp & 0xff00) |
((temp >> 16) & 0xff);
index++;
yindex++;
}
}
yindex -= width * 2;
}
// Flips image
if ((height % 2) == 1) {
index = (height / 2) * width;
if (BIG_ENDIAN) { // RGBA to ARGB
for (int x = 0; x < width; x++) {
pixels[index] = 0xff000000 | ((pixels[index] >> 8) & 0x00ffffff);
index++;
}
} else { // ABGR to ARGB
for (int x = 0; x < width; x++) {
pixels[index] = 0xff000000 | ((pixels[index] << 16) & 0xff0000) |
(pixels[index] & 0xff00) |
((pixels[index] >> 16) & 0xff);
index++;
}
}
}
}
/**
* Converts input Java ARGB value to native OpenGL format (RGBA on big endian,
* BGRA on little endian).
*/
public static int javaToNativeARGB(int color) {
if (BIG_ENDIAN) { // ARGB to RGBA
return ((color >> 24) & 0xff) |
((color << 8) & 0xffffff00);
} else { // ARGB to ABGR
return (color & 0xff000000) |
((color << 16) & 0xff0000) |
(color & 0xff00) |
((color >> 16) & 0xff);
}
}
/**
* Converts input array of Java ARGB values representing an image of width x height
* resolution to native OpenGL format (RGBA on big endian, BGRA on little endian).
* It also rearranges the elements in the array so that the image is flipped
* vertically.
*/
public static void javaToNativeARGB(int[] pixels, int width, int height) {
int index = 0;
int yindex = (height - 1) * width;
for (int y = 0; y < height / 2; y++) {
if (BIG_ENDIAN) { // ARGB to RGBA
for (int x = 0; x < width; x++) {
int temp = pixels[index];
pixels[index] = ((pixels[yindex] >> 24) & 0xff) |
((pixels[yindex] << 8) & 0xffffff00);
pixels[yindex] = ((temp >> 24) & 0xff) |
((temp << 8) & 0xffffff00);
index++;
yindex++;
}
} else { // ARGB to ABGR
for (int x = 0; x < width; x++) {
int temp = pixels[index];
pixels[index] = (pixels[yindex] & 0xff000000) |
((pixels[yindex] << 16) & 0xff0000) |
(pixels[yindex] & 0xff00) |
((pixels[yindex] >> 16) & 0xff);
pixels[yindex] = (pixels[yindex] & 0xff000000) |
((temp << 16) & 0xff0000) |
(temp & 0xff00) |
((temp >> 16) & 0xff);
index++;
yindex++;
}
}
yindex -= width * 2;
}
// Flips image
if ((height % 2) == 1) {
index = (height / 2) * width;
if (BIG_ENDIAN) { // ARGB to RGBA
for (int x = 0; x < width; x++) {
pixels[index] = ((pixels[index] >> 24) & 0xff) |
((pixels[index] << 8) & 0xffffff00);
index++;
}
} else { // ARGB to ABGR
for (int x = 0; x < width; x++) {
pixels[index] = (pixels[index] & 0xff000000) |
((pixels[index] << 16) & 0xff0000) |
(pixels[index] & 0xff00) |
((pixels[index] >> 16) & 0xff);
index++;
}
}
}
}
/**
* Converts input Java ARGB value to native OpenGL format (RGBA on big endian,
* BGRA on little endian), setting alpha component to opaque (255).
*/
public static int javaToNativeRGB(int color) {
if (BIG_ENDIAN) { // ARGB to RGBA
return ((color << 8) & 0xffffff00) | 0xff;
} else { // ARGB to ABGR
return 0xff000000 | ((color << 16) & 0xff0000) |
(color & 0xff00) |
((color >> 16) & 0xff);
}
}
/**
* Converts input array of Java ARGB values representing an image of width x height
* resolution to native OpenGL format (RGBA on big endian, BGRA on little endian),
* while setting alpha component of all pixels to opaque (255). It also rearranges
* the elements in the array so that the image is flipped vertically.
*/
public static void javaToNativeRGB(int[] pixels, int width, int height) {
int index = 0;
int yindex = (height - 1) * width;
for (int y = 0; y < height / 2; y++) {
if (BIG_ENDIAN) { // ARGB to RGBA
for (int x = 0; x < width; x++) {
int temp = pixels[index];
pixels[index] = ((pixels[yindex] << 8) & 0xffffff00) | 0xff;
pixels[yindex] = ((temp << 8) & 0xffffff00) | 0xff;
index++;
yindex++;
}
} else {
for (int x = 0; x < width; x++) { // ARGB to ABGR
int temp = pixels[index];
pixels[index] = 0xff000000 | ((pixels[yindex] << 16) & 0xff0000) |
(pixels[yindex] & 0xff00) |
((pixels[yindex] >> 16) & 0xff);
pixels[yindex] = 0xff000000 | ((temp << 16) & 0xff0000) |
(temp & 0xff00) |
((temp >> 16) & 0xff);
index++;
yindex++;
}
}
yindex -= width * 2;
}
// Flips image
if ((height % 2) == 1) { // ARGB to RGBA
index = (height / 2) * width;
if (BIG_ENDIAN) {
for (int x = 0; x < width; x++) {
pixels[index] = ((pixels[index] << 8) & 0xffffff00) | 0xff;
index++;
}
} else { // ARGB to ABGR
for (int x = 0; x < width; x++) {
pixels[index] = 0xff000000 | ((pixels[index] << 16) & 0xff0000) |
(pixels[index] & 0xff00) |
((pixels[index] >> 16) & 0xff);
index++;
}
}
}
}
public int createShader(int shaderType, String source) {
int shader = createShader(shaderType);
if (shader != 0) {
shaderSource(shader, source);
compileShader(shader);
int[] compiled = new int[1];
getShaderiv(shader, COMPILE_STATUS, compiled, 0);
if (compiled[0] == FALSE) {
System.err.println("Could not compile shader " + shaderType + ":");
System.err.println(getShaderInfoLog(shader));
deleteShader(shader);
shader = 0;
}
}
return shader;
}
public int createProgram(int vertexShader, int fragmentShader) {
int program = createProgram();
if (program != 0) {
attachShader(program, vertexShader);
attachShader(program, fragmentShader);
linkProgram(program);
int[] linked = new int[1];
getProgramiv(program, LINK_STATUS, linked, 0);
if (linked[0] == FALSE) {
System.err.println("Could not link program: ");
System.err.println(getProgramInfoLog(program));
deleteProgram(program);
program = 0;
}
}
return program;
}
public boolean validateFramebuffer() {
int status = checkFramebufferStatus(FRAMEBUFFER);
if (status == FRAMEBUFFER_COMPLETE) {
return true;
} else if (status == FRAMEBUFFER_INCOMPLETE_ATTACHMENT) {
throw new RuntimeException("PFramebuffer: GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT (" + Integer.toHexString(status) + ")");
} else if (status == FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) {
throw new RuntimeException("PFramebuffer: GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT (" + Integer.toHexString(status) + ")");
} else if (status == FRAMEBUFFER_INCOMPLETE_DIMENSIONS) {
throw new RuntimeException("PFramebuffer: GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS (" + Integer.toHexString(status) + ")");
} else if (status == FRAMEBUFFER_INCOMPLETE_FORMATS) {
throw new RuntimeException("PFramebuffer: GL_FRAMEBUFFER_INCOMPLETE_FORMATS (" + Integer.toHexString(status) + ")");
} else if (status == FRAMEBUFFER_UNSUPPORTED) {
throw new RuntimeException("PFramebuffer: GL_FRAMEBUFFER_UNSUPPORTED" + Integer.toHexString(status));
} else {
throw new RuntimeException("PFramebuffer: unknown framebuffer error (" + Integer.toHexString(status) + ")");
}
}
public static ByteBuffer allocateDirectByteBuffer(int size) {
return ByteBuffer.allocateDirect(size * SIZEOF_BYTE).order(ByteOrder.nativeOrder());
}
public static IntBuffer allocateDirectIntBuffer(int size) {
return ByteBuffer.allocateDirect(size * SIZEOF_INT).order(ByteOrder.nativeOrder()).asIntBuffer();
}
public static FloatBuffer allocateDirectFloatBuffer(int size) {
return ByteBuffer.allocateDirect(size * SIZEOF_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer();
}
/////////////////////////////////////////////////////////////////////////////////
// Android specific stuff
public AndroidRenderer getRenderer() {
return renderer;
}
public AndroidContextFactory getContextFactory() {
return new AndroidContextFactory();
}
public AndroidConfigChooser getConfigChooser(int r, int g, int b, int a, int d, int s) {
return new AndroidConfigChooser(r, g, b, a, d, s);
}
protected class AndroidRenderer implements Renderer {
public AndroidRenderer() {
}
public void onDrawFrame(GL10 igl) {
gl = igl;
pg.parent.handleDraw();
}
public void onSurfaceChanged(GL10 igl, int iwidth, int iheight) {
gl = igl;
// Here is where we should initialize native libs...
// lib.init(iwidth, iheight);
pg.setSize(iwidth, iheight);
}
public void onSurfaceCreated(GL10 igl, EGLConfig config) {
gl = igl;
}
}
protected class AndroidContextFactory implements GLSurfaceView.EGLContextFactory {
public EGLContext createContext(EGL10 egl, EGLDisplay display,
EGLConfig eglConfig) {
int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2,
EGL10.EGL_NONE };
EGLContext context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
return context;
}
public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) {
egl.eglDestroyContext(display, context);
}
}
protected class AndroidConfigChooser implements EGLConfigChooser {
// Desired size (in bits) for the rgba color, depth and stencil buffers.
public int redTarget;
public int greenTarget;
public int blueTarget;
public int alphaTarget;
public int depthTarget;
public int stencilTarget;
// Actual rgba color, depth and stencil sizes (in bits) supported by the device.
public int redBits;
public int greenBits;
public int blueBits;
public int alphaBits;
public int depthBits;
public int stencilBits;
public int[] tempValue = new int[1];
// The attributes we want in the frame buffer configuration for Processing.
// For more details on other attributes, see:
// http://www.khronos.org/opengles/documentation/opengles1_0/html/eglChooseConfig.html
protected int[] configAttribsGL = { EGL10.EGL_RED_SIZE, 4,
EGL10.EGL_GREEN_SIZE, 4,
EGL10.EGL_BLUE_SIZE, 4,
EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL10.EGL_NONE };
public AndroidConfigChooser(int r, int g, int b, int a, int d, int s) {
redTarget = r;
greenTarget = g;
blueTarget = b;
alphaTarget = a;
depthTarget = d;
stencilTarget = s;
}
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
// Get the number of minimally matching EGL configurations
int[] num_config = new int[1];
egl.eglChooseConfig(display, configAttribsGL, null, 0, num_config);
int numConfigs = num_config[0];
if (numConfigs <= 0) {
throw new IllegalArgumentException("No EGL configs match configSpec");
}
// Allocate then read the array of minimally matching EGL configs
EGLConfig[] configs = new EGLConfig[numConfigs];
egl.eglChooseConfig(display, configAttribsGL, configs, numConfigs,
num_config);
if (PApplet.DEBUG) {
for (EGLConfig config : configs) {
String configStr = "P3D - selected EGL config : "
+ printConfig(egl, display, config);
System.out.println(configStr);
}
}
// Now return the configuration that best matches the target one.
return chooseBestConfig(egl, display, configs);
}
public EGLConfig chooseBestConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) {
EGLConfig bestConfig = null;
float bestScore = 1000;
for (EGLConfig config : configs) {
int gl = findConfigAttrib(egl, display, config, EGL10.EGL_RENDERABLE_TYPE, 0);
if (gl == EGL_OPENGL_ES2_BIT) {
int d = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0);
int s = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0);
int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);
int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
float score = 0.20f * PApplet.abs(r - redTarget) +
0.20f * PApplet.abs(g - greenTarget) +
0.20f * PApplet.abs(b - blueTarget) +
0.15f * PApplet.abs(a - blueTarget) +
0.15f * PApplet.abs(d - depthTarget) +
0.10f * PApplet.abs(s - stencilTarget);
if (score < bestScore) {
// We look for the config closest to the target config.
// Closeness is measured by the score function defined above:
// we give more weight to the RGB components, followed by the
// alpha, depth and finally stencil bits.
bestConfig = config;
bestScore = score;
redBits = r;
greenBits = g;
blueBits = b;
alphaBits = a;
depthBits = d;
stencilBits = s;
}
}
}
if (PApplet.DEBUG) {
String configStr = "P3D - selected EGL config : "
+ printConfig(egl, display, bestConfig);
System.out.println(configStr);
}
return bestConfig;
}
protected String printConfig(EGL10 egl, EGLDisplay display, EGLConfig config) {
int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);
int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
int d = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0);
int s = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0);
int type = findConfigAttrib(egl, display, config, EGL10.EGL_RENDERABLE_TYPE, 0);
int nat = findConfigAttrib(egl, display, config, EGL10.EGL_NATIVE_RENDERABLE, 0);
int bufSize = findConfigAttrib(egl, display, config, EGL10.EGL_BUFFER_SIZE, 0);
int bufSurf = findConfigAttrib(egl, display, config, EGL10.EGL_RENDER_BUFFER, 0);
return String.format("EGLConfig rgba=%d%d%d%d depth=%d stencil=%d", r,g,b,a,d,s)
+ " type=" + type
+ " native=" + nat
+ " buffer size=" + bufSize
+ " buffer surface=" + bufSurf +
String.format(" caveat=0x%04x", findConfigAttrib(egl, display, config, EGL10.EGL_CONFIG_CAVEAT, 0));
}
protected int findConfigAttrib(EGL10 egl, EGLDisplay display,
EGLConfig config, int attribute, int defaultValue) {
if (egl.eglGetConfigAttrib(display, config, attribute, tempValue)) {
return tempValue[0];
}
return defaultValue;
}
}
}