Java tutorial
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* Part of the Processing project - http://processing.org Copyright (c) 2011-12 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 as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. 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.lwjgl; import java.awt.BorderLayout; import java.awt.Canvas; import java.awt.Color; import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.nio.ShortBuffer; import java.util.Arrays; import org.lwjgl.BufferUtils; import org.lwjgl.LWJGLException; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; import org.lwjgl.opengl.Display; import org.lwjgl.opengl.DisplayMode; import org.lwjgl.opengl.EXTFramebufferObject; import org.lwjgl.opengl.EXTTextureFilterAnisotropic; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; import org.lwjgl.opengl.GL13; import org.lwjgl.opengl.GL14; import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL30; import org.lwjgl.opengl.GL31; import org.lwjgl.util.glu.GLU; import org.lwjgl.util.glu.GLUtessellator; import org.lwjgl.util.glu.GLUtessellatorCallbackAdapter; import org.lwjgl.opengl.PixelFormat; import processing.core.PApplet; import processing.core.PConstants; import processing.event.Event; import processing.event.KeyEvent; import processing.event.MouseEvent; import processing.opengl.PGraphicsOpenGL; import processing.opengl.Texture; /** * Processing-OpenGL abstraction layer. * * Warnings are suppressed for static access because presumably on Android, * the GL2 vs GL distinctions are necessary, whereas on desktop they are not. * * This version of PGL uses LWJGL, see some issues with it: * http://lwjgl.org/forum/index.php/topic,4711.0.html * http://www.java-gaming.org/topics/cannot-add-mouselistener-to-java-awt-canvas-with-lwjgl-on-windows/24650/view.html * */ @SuppressWarnings("static-access") public class PGL extends processing.opengl.PGL { /////////////////////////////////////////////////////////// // Parameters public static boolean FORCE_SCREEN_FBO = false; public static final boolean USE_DIRECT_BUFFERS = true; public static final int MIN_DIRECT_BUFFER_SIZE = 16; public static final boolean SAVE_SURFACE_TO_PIXELS = true; /** Enables/disables mipmap use. **/ protected static final boolean MIPMAPS_ENABLED = true; /** Initial sizes for arrays of input and tessellated data. */ protected static final int DEFAULT_IN_VERTICES = 64; protected static final int DEFAULT_IN_EDGES = 128; protected static final int DEFAULT_IN_TEXTURES = 64; protected static final int DEFAULT_TESS_VERTICES = 64; protected static final int DEFAULT_TESS_INDICES = 128; /** 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 = 1024; /** Minimum stroke weight needed to apply the full path stroking * algorithm that properly generates caps and joins. */ protected static final float MIN_CAPS_JOINS_WEIGHT = 2f; /** 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 = 5000; /** Minimum array size to use arrayCopy method(). **/ protected static final int MIN_ARRAYCOPY_SIZE = 2; protected static int request_depth_bits = 24; protected static int request_stencil_bits = 8; protected static int request_alpha_bits = 8; protected static final int SIZEOF_SHORT = Short.SIZE / 8; protected static final int SIZEOF_INT = Integer.SIZE / 8; protected static final int SIZEOF_FLOAT = Float.SIZE / 8; protected static final int SIZEOF_BYTE = Byte.SIZE / 8; protected static final int SIZEOF_INDEX = SIZEOF_SHORT; protected static final int INDEX_TYPE = GL11.GL_UNSIGNED_SHORT; /** 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 public static final int FALSE = GL11.GL_FALSE; public static final int TRUE = GL11.GL_TRUE; public static final int LESS = GL11.GL_LESS; public static final int LEQUAL = GL11.GL_LEQUAL; public static final int CCW = GL11.GL_CCW; public static final int CW = GL11.GL_CW; public static final int CULL_FACE = GL11.GL_CULL_FACE; public static final int FRONT = GL11.GL_FRONT; public static final int BACK = GL11.GL_BACK; public static final int FRONT_AND_BACK = GL11.GL_FRONT_AND_BACK; public static final int VIEWPORT = GL11.GL_VIEWPORT; public static final int SCISSOR_TEST = GL11.GL_SCISSOR_TEST; public static final int DEPTH_TEST = GL11.GL_DEPTH_TEST; public static final int DEPTH_WRITEMASK = GL11.GL_DEPTH_WRITEMASK; public static final int COLOR_BUFFER_BIT = GL11.GL_COLOR_BUFFER_BIT; public static final int DEPTH_BUFFER_BIT = GL11.GL_DEPTH_BUFFER_BIT; public static final int STENCIL_BUFFER_BIT = GL11.GL_STENCIL_BUFFER_BIT; public static final int FUNC_ADD = GL14.GL_FUNC_ADD; public static final int FUNC_MIN = GL14.GL_MIN; public static final int FUNC_MAX = GL14.GL_MAX; public static final int FUNC_REVERSE_SUBTRACT = GL14.GL_FUNC_REVERSE_SUBTRACT; public static final int TEXTURE_2D = GL11.GL_TEXTURE_2D; public static final int TEXTURE_RECTANGLE = GL31.GL_TEXTURE_RECTANGLE; public static final int TEXTURE_BINDING_2D = GL11.GL_TEXTURE_BINDING_2D; public static final int TEXTURE_BINDING_RECTANGLE = GL31.GL_TEXTURE_BINDING_RECTANGLE; public static final int RGB = GL11.GL_RGB; public static final int RGBA = GL11.GL_RGBA; public static final int ALPHA = GL11.GL_ALPHA; public static final int UNSIGNED_INT = GL11.GL_UNSIGNED_INT; public static final int UNSIGNED_BYTE = GL11.GL_UNSIGNED_BYTE; public static final int UNSIGNED_SHORT = GL11.GL_UNSIGNED_SHORT; public static final int FLOAT = GL11.GL_FLOAT; public static final int NEAREST = GL11.GL_NEAREST; public static final int LINEAR = GL11.GL_LINEAR; public static final int LINEAR_MIPMAP_NEAREST = GL11.GL_LINEAR_MIPMAP_NEAREST; public static final int LINEAR_MIPMAP_LINEAR = GL11.GL_LINEAR_MIPMAP_LINEAR; public static final int TEXTURE_MAX_ANISOTROPY = EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT; public static final int MAX_TEXTURE_MAX_ANISOTROPY = EXTTextureFilterAnisotropic.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT; public static final int CLAMP_TO_EDGE = GL12.GL_CLAMP_TO_EDGE; public static final int REPEAT = GL11.GL_REPEAT; public static final int RGBA8 = GL11.GL_RGBA8; public static final int DEPTH24_STENCIL8 = GL30.GL_DEPTH24_STENCIL8; public static final int DEPTH_COMPONENT = GL11.GL_DEPTH_COMPONENT; public static final int DEPTH_COMPONENT16 = GL14.GL_DEPTH_COMPONENT16; public static final int DEPTH_COMPONENT24 = GL14.GL_DEPTH_COMPONENT24; public static final int DEPTH_COMPONENT32 = GL14.GL_DEPTH_COMPONENT32; public static final int STENCIL_INDEX = GL11.GL_STENCIL_INDEX; public static final int STENCIL_INDEX1 = GL30.GL_STENCIL_INDEX1; public static final int STENCIL_INDEX4 = GL30.GL_STENCIL_INDEX4; public static final int STENCIL_INDEX8 = GL30.GL_STENCIL_INDEX8; public static final int ARRAY_BUFFER = GL15.GL_ARRAY_BUFFER; public static final int ELEMENT_ARRAY_BUFFER = GL15.GL_ELEMENT_ARRAY_BUFFER; public static final int SAMPLES = GL13.GL_SAMPLES; public static final int FRAMEBUFFER_COMPLETE = GL30.GL_FRAMEBUFFER_COMPLETE; public static final int FRAMEBUFFER_INCOMPLETE_ATTACHMENT = GL30.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; public static final int FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = GL30.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; public static final int FRAMEBUFFER_INCOMPLETE_DIMENSIONS = EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT; public static final int FRAMEBUFFER_INCOMPLETE_FORMATS = EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT; public static final int FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER = GL30.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER; public static final int FRAMEBUFFER_INCOMPLETE_READ_BUFFER = GL30.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER; public static final int FRAMEBUFFER_UNSUPPORTED = GL30.GL_FRAMEBUFFER_UNSUPPORTED; public static final int STATIC_DRAW = GL15.GL_STATIC_DRAW; public static final int DYNAMIC_DRAW = GL15.GL_DYNAMIC_DRAW; public static final int STREAM_DRAW = GL15.GL_STREAM_DRAW; public static final int READ_ONLY = GL15.GL_READ_ONLY; public static final int WRITE_ONLY = GL15.GL_WRITE_ONLY; public static final int READ_WRITE = GL15.GL_READ_WRITE; public static final int TRIANGLE_FAN = GL11.GL_TRIANGLE_FAN; public static final int TRIANGLE_STRIP = GL11.GL_TRIANGLE_STRIP; public static final int TRIANGLES = GL11.GL_TRIANGLES; public static final int VENDOR = GL11.GL_VENDOR; public static final int RENDERER = GL11.GL_RENDERER; public static final int VERSION = GL11.GL_VERSION; public static final int EXTENSIONS = GL11.GL_EXTENSIONS; public static final int SHADING_LANGUAGE_VERSION = GL20.GL_SHADING_LANGUAGE_VERSION; public static final int MAX_TEXTURE_SIZE = GL11.GL_MAX_TEXTURE_SIZE; public static final int MAX_SAMPLES = GL30.GL_MAX_SAMPLES; public static final int ALIASED_LINE_WIDTH_RANGE = GL12.GL_ALIASED_LINE_WIDTH_RANGE; public static final int ALIASED_POINT_SIZE_RANGE = GL12.GL_ALIASED_POINT_SIZE_RANGE; public static final int DEPTH_BITS = GL11.GL_DEPTH_BITS; public static final int STENCIL_BITS = GL11.GL_STENCIL_BITS; public static final int TESS_WINDING_NONZERO = GLU.GLU_TESS_WINDING_NONZERO; public static final int TESS_WINDING_ODD = GLU.GLU_TESS_WINDING_ODD; public static final int TEXTURE0 = GL13.GL_TEXTURE0; public static final int TEXTURE1 = GL13.GL_TEXTURE1; public static final int TEXTURE2 = GL13.GL_TEXTURE2; public static final int TEXTURE3 = GL13.GL_TEXTURE3; public static final int TEXTURE_MIN_FILTER = GL11.GL_TEXTURE_MIN_FILTER; public static final int TEXTURE_MAG_FILTER = GL11.GL_TEXTURE_MAG_FILTER; public static final int TEXTURE_WRAP_S = GL11.GL_TEXTURE_WRAP_S; public static final int TEXTURE_WRAP_T = GL11.GL_TEXTURE_WRAP_T; public static final int BLEND = GL11.GL_BLEND; public static final int ONE = GL11.GL_ONE; public static final int ZERO = GL11.GL_ZERO; public static final int SRC_ALPHA = GL11.GL_SRC_ALPHA; public static final int DST_ALPHA = GL11.GL_DST_ALPHA; public static final int ONE_MINUS_SRC_ALPHA = GL11.GL_ONE_MINUS_SRC_ALPHA; public static final int ONE_MINUS_DST_COLOR = GL11.GL_ONE_MINUS_DST_COLOR; public static final int ONE_MINUS_SRC_COLOR = GL11.GL_ONE_MINUS_SRC_COLOR; public static final int DST_COLOR = GL11.GL_DST_COLOR; public static final int SRC_COLOR = GL11.GL_SRC_COLOR; public static final int FRAMEBUFFER = GL30.GL_FRAMEBUFFER; public static final int COLOR_ATTACHMENT0 = GL30.GL_COLOR_ATTACHMENT0; public static final int COLOR_ATTACHMENT1 = GL30.GL_COLOR_ATTACHMENT1; public static final int COLOR_ATTACHMENT2 = GL30.GL_COLOR_ATTACHMENT2; public static final int COLOR_ATTACHMENT3 = GL30.GL_COLOR_ATTACHMENT3; public static final int RENDERBUFFER = GL30.GL_RENDERBUFFER; public static final int DEPTH_ATTACHMENT = GL30.GL_DEPTH_ATTACHMENT; public static final int STENCIL_ATTACHMENT = GL30.GL_STENCIL_ATTACHMENT; public static final int READ_FRAMEBUFFER = GL30.GL_READ_FRAMEBUFFER; public static final int DRAW_FRAMEBUFFER = GL30.GL_DRAW_FRAMEBUFFER; public static final int VERTEX_SHADER = GL20.GL_VERTEX_SHADER; public static final int FRAGMENT_SHADER = GL20.GL_FRAGMENT_SHADER; public static final int INFO_LOG_LENGTH = GL20.GL_INFO_LOG_LENGTH; public static final int SHADER_SOURCE_LENGTH = GL20.GL_SHADER_SOURCE_LENGTH; public static final int COMPILE_STATUS = GL20.GL_COMPILE_STATUS; public static final int LINK_STATUS = GL20.GL_LINK_STATUS; public static final int VALIDATE_STATUS = GL20.GL_VALIDATE_STATUS; public static final int MULTISAMPLE = GL13.GL_MULTISAMPLE; public static final int POINT_SMOOTH = GL11.GL_POINT_SMOOTH; public static final int LINE_SMOOTH = GL11.GL_LINE_SMOOTH; public static final int POLYGON_SMOOTH = GL11.GL_POLYGON_SMOOTH; /** GLU interface **/ public static GLU glu; /** The canvas where OpenGL rendering takes place */ public static Canvas canvas; /** OpenGL thread */ protected static Thread glThread; /** Just holds a unique ID */ protected static int context; /** The PGraphics object using this interface */ protected PGraphicsOpenGL pg; /** Poller threads to get the keyboard/mouse events from LWJGL */ protected static KeyPoller keyPoller; protected static MousePoller mousePoller; /** Which texturing targets are enabled */ protected static boolean[] texturingTargets = { false, false }; /** Which textures are bound to each target */ protected static int[] boundTextures = { 0, 0 }; /////////////////////////////////////////////////////////// // FBO layer protected static boolean fboLayerByDefault = FORCE_SCREEN_FBO; protected static boolean fboLayerCreated = false; protected static boolean fboLayerInUse = false; protected static boolean firstFrame = true; protected static int reqNumSamples; protected static int numSamples; protected static IntBuffer glColorFbo; protected static IntBuffer glMultiFbo; protected static IntBuffer glColorBuf; protected static IntBuffer glColorTex; protected static IntBuffer glDepthStencil; protected static IntBuffer glDepth; protected static IntBuffer glStencil; protected static int fboWidth, fboHeight; protected static int backTex, frontTex; protected static boolean needToClearBuffers; /////////////////////////////////////////////////////////// // Texture rendering protected static boolean loadedTex2DShader = false; protected static int tex2DShaderProgram; protected static int tex2DVertShader; protected static int tex2DFragShader; protected static int tex2DShaderContext; protected static int tex2DVertLoc; protected static int tex2DTCoordLoc; protected static boolean loadedTexRectShader = false; protected static int texRectShaderProgram; protected static int texRectVertShader; protected static int texRectFragShader; protected static int texRectShaderContext; protected static int texRectVertLoc; protected static int texRectTCoordLoc; protected static 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 static FloatBuffer texData; protected static String texVertShaderSource = "attribute vec2 inVertex;" + "attribute vec2 inTexcoord;" + "varying vec2 vertTexcoord;" + "void main() {" + " gl_Position = vec4(inVertex, 0, 1);" + " vertTexcoord = inTexcoord;" + "}"; protected static String tex2DFragShaderSource = SHADER_PREPROCESSOR_DIRECTIVE + "uniform sampler2D textureSampler;" + "varying vec2 vertTexcoord;" + "void main() {" + " gl_FragColor = texture2D(textureSampler, vertTexcoord.st);" + "}"; protected static String texRectFragShaderSource = SHADER_PREPROCESSOR_DIRECTIVE + "uniform sampler2DRect textureSampler;" + "varying vec2 vertTexcoord;" + "void main() {" + " gl_FragColor = texture2DRect(textureSampler, vertTexcoord.st);" + "}"; /////////////////////////////////////////////////////////// // Utilities protected ByteBuffer byteBuffer; protected IntBuffer intBuffer; protected IntBuffer colorBuffer; protected FloatBuffer depthBuffer; protected ByteBuffer stencilBuffer; /////////////////////////////////////////////////////////// // Initialization, finalization public PGL(PGraphicsOpenGL pg) { this.pg = pg; if (glu == null) { glu = new GLU(); } if (glColorTex == null) { glColorTex = allocateIntBuffer(2); glColorFbo = allocateIntBuffer(1); glMultiFbo = allocateIntBuffer(1); glColorBuf = allocateIntBuffer(1); glDepthStencil = allocateIntBuffer(1); glDepth = allocateIntBuffer(1); glStencil = allocateIntBuffer(1); fboLayerCreated = false; fboLayerInUse = false; firstFrame = false; needToClearBuffers = false; } byteBuffer = allocateByteBuffer(1); intBuffer = allocateIntBuffer(1); } protected void setFrameRate(float framerate) { } protected void initSurface(int antialias) { if (canvas != null) { keyPoller.requestStop(); mousePoller.requestStop(); try { Display.setParent(null); } catch (LWJGLException e) { e.printStackTrace(); } Display.destroy(); pg.parent.remove(canvas); } canvas = new Canvas(); canvas.setFocusable(true); canvas.requestFocus(); canvas.setBackground(new Color(pg.backgroundColor, true)); canvas.setBounds(0, 0, pg.parent.width, pg.parent.height); pg.parent.setLayout(new BorderLayout()); pg.parent.add(canvas, BorderLayout.CENTER); try { PixelFormat format = new PixelFormat(32, request_alpha_bits, request_depth_bits, request_stencil_bits, 1); Display.setDisplayMode(new DisplayMode(pg.parent.width, pg.parent.height)); int argb = pg.backgroundColor; float r = ((argb >> 16) & 0xff) / 255.0f; float g = ((argb >> 8) & 0xff) / 255.0f; float b = ((argb) & 0xff) / 255.0f; Display.setInitialBackground(r, g, b); Display.setParent(canvas); Display.create(format); Display.setVSyncEnabled(true); } catch (LWJGLException e) { e.printStackTrace(); } context = Display.getDrawable().hashCode(); keyPoller = new KeyPoller(pg.parent); keyPoller.start(); mousePoller = new MousePoller(pg.parent); mousePoller.start(); reqNumSamples = qualityToSamples(antialias); fboLayerCreated = false; fboLayerInUse = false; firstFrame = true; needToClearBuffers = true; } protected void deleteSurface() { if (glColorTex != null) { deleteTextures(2, glColorTex); deleteFramebuffers(1, glColorFbo); deleteFramebuffers(1, glMultiFbo); deleteRenderbuffers(1, glColorBuf); deleteRenderbuffers(1, glDepthStencil); deleteRenderbuffers(1, glDepth); deleteRenderbuffers(1, glStencil); } fboLayerCreated = false; fboLayerInUse = false; firstFrame = false; needToClearBuffers = false; } protected void update() { if (!fboLayerCreated) { String ext = getString(EXTENSIONS); if (-1 < ext.indexOf("texture_non_power_of_two")) { fboWidth = pg.width; fboHeight = pg.height; } else { fboWidth = nextPowerOfTwo(pg.width); fboHeight = nextPowerOfTwo(pg.height); } if (-1 < ext.indexOf("_framebuffer_multisample")) { numSamples = reqNumSamples; } else { numSamples = 1; } boolean multisample = 1 < numSamples; boolean packed = ext.indexOf("packed_depth_stencil") != -1; int depthBits = getDepthBits(); int stencilBits = getStencilBits(); genTextures(2, glColorTex); for (int i = 0; i < 2; i++) { bindTexture(TEXTURE_2D, glColorTex.get(i)); texParameteri(TEXTURE_2D, TEXTURE_MIN_FILTER, NEAREST); texParameteri(TEXTURE_2D, TEXTURE_MAG_FILTER, NEAREST); texParameteri(TEXTURE_2D, TEXTURE_WRAP_S, CLAMP_TO_EDGE); texParameteri(TEXTURE_2D, TEXTURE_WRAP_T, CLAMP_TO_EDGE); texImage2D(TEXTURE_2D, 0, RGBA, fboWidth, fboHeight, 0, RGBA, UNSIGNED_BYTE, null); initTexture(TEXTURE_2D, RGBA, fboWidth, fboHeight, pg.backgroundColor); } bindTexture(TEXTURE_2D, 0); backTex = 0; frontTex = 1; genFramebuffers(1, glColorFbo); bindFramebuffer(FRAMEBUFFER, glColorFbo.get(0)); framebufferTexture2D(FRAMEBUFFER, COLOR_ATTACHMENT0, TEXTURE_2D, glColorTex.get(backTex), 0); if (multisample) { // Creating multisampled FBO genFramebuffers(1, glMultiFbo); bindFramebuffer(FRAMEBUFFER, glMultiFbo.get(0)); // color render buffer... genRenderbuffers(1, glColorBuf); bindRenderbuffer(RENDERBUFFER, glColorBuf.get(0)); renderbufferStorageMultisample(RENDERBUFFER, numSamples, RGBA8, fboWidth, fboHeight); framebufferRenderbuffer(FRAMEBUFFER, COLOR_ATTACHMENT0, RENDERBUFFER, glColorBuf.get(0)); } // Creating depth and stencil buffers if (packed && depthBits == 24 && stencilBits == 8) { // packed depth+stencil buffer genRenderbuffers(1, glDepthStencil); bindRenderbuffer(RENDERBUFFER, glDepthStencil.get(0)); if (multisample) { renderbufferStorageMultisample(RENDERBUFFER, numSamples, DEPTH24_STENCIL8, fboWidth, fboHeight); } else { renderbufferStorage(RENDERBUFFER, DEPTH24_STENCIL8, fboWidth, fboHeight); } framebufferRenderbuffer(FRAMEBUFFER, DEPTH_ATTACHMENT, RENDERBUFFER, glDepthStencil.get(0)); framebufferRenderbuffer(FRAMEBUFFER, STENCIL_ATTACHMENT, RENDERBUFFER, glDepthStencil.get(0)); } else { // separate depth and stencil buffers if (0 < depthBits) { int depthComponent = DEPTH_COMPONENT16; if (depthBits == 32) { depthComponent = DEPTH_COMPONENT32; } else if (depthBits == 24) { depthComponent = DEPTH_COMPONENT24; } else if (depthBits == 16) { depthComponent = DEPTH_COMPONENT16; } genRenderbuffers(1, glDepth); bindRenderbuffer(RENDERBUFFER, glDepth.get(0)); if (multisample) { renderbufferStorageMultisample(RENDERBUFFER, numSamples, depthComponent, fboWidth, fboHeight); } else { renderbufferStorage(RENDERBUFFER, depthComponent, fboWidth, fboHeight); } framebufferRenderbuffer(FRAMEBUFFER, DEPTH_ATTACHMENT, RENDERBUFFER, glDepth.get(0)); } if (0 < stencilBits) { int stencilIndex = STENCIL_INDEX1; if (stencilBits == 8) { stencilIndex = STENCIL_INDEX8; } else if (stencilBits == 4) { stencilIndex = STENCIL_INDEX4; } else if (stencilBits == 1) { stencilIndex = STENCIL_INDEX1; } genRenderbuffers(1, glStencil); bindRenderbuffer(RENDERBUFFER, glStencil.get(0)); if (multisample) { renderbufferStorageMultisample(RENDERBUFFER, numSamples, stencilIndex, fboWidth, fboHeight); } else { renderbufferStorage(RENDERBUFFER, stencilIndex, fboWidth, fboHeight); } framebufferRenderbuffer(FRAMEBUFFER, STENCIL_ATTACHMENT, RENDERBUFFER, glStencil.get(0)); } } validateFramebuffer(); // Clear all buffers. clearDepth(1); clearStencil(0); int argb = pg.backgroundColor; float a = ((argb >> 24) & 0xff) / 255.0f; float r = ((argb >> 16) & 0xff) / 255.0f; float g = ((argb >> 8) & 0xff) / 255.0f; float b = ((argb) & 0xff) / 255.0f; clearColor(r, g, b, a); clear(DEPTH_BUFFER_BIT | STENCIL_BUFFER_BIT | COLOR_BUFFER_BIT); bindFramebuffer(FRAMEBUFFER, 0); fboLayerCreated = true; } } protected int getReadFramebuffer() { if (fboLayerInUse) { return glColorFbo.get(0); } else { return 0; } } protected int getDrawFramebuffer() { if (fboLayerInUse) { if (1 < numSamples) { return glMultiFbo.get(0); } else { return glColorFbo.get(0); } } else { return 0; } } protected int getDefaultDrawBuffer() { if (fboLayerInUse) { return COLOR_ATTACHMENT0; } else { return BACK; } } protected int getDefaultReadBuffer() { if (fboLayerInUse) { return COLOR_ATTACHMENT0; } else { return FRONT; } } protected boolean isFBOBacked() { return fboLayerInUse; } protected void needFBOLayer() { FORCE_SCREEN_FBO = true; } protected boolean isMultisampled() { return 1 < numSamples; } protected int getDepthBits() { intBuffer.rewind(); getIntegerv(DEPTH_BITS, intBuffer); return intBuffer.get(0); } protected int getStencilBits() { intBuffer.rewind(); getIntegerv(STENCIL_BITS, intBuffer); return intBuffer.get(0); } protected boolean getDepthTest() { intBuffer.rewind(); getBooleanv(DEPTH_TEST, intBuffer); return intBuffer.get(0) == 0 ? false : true; } protected boolean getDepthWriteMask() { intBuffer.rewind(); getBooleanv(DEPTH_WRITEMASK, intBuffer); return intBuffer.get(0) == 0 ? false : true; } protected Texture wrapBackTexture() { Texture tex = new Texture(pg.parent); tex.init(pg.width, pg.height, glColorTex.get(backTex), TEXTURE_2D, RGBA, fboWidth, fboHeight, NEAREST, NEAREST, CLAMP_TO_EDGE, CLAMP_TO_EDGE); tex.invertedY(true); tex.colorBufferOf(pg); pg.setCache(pg, tex); return tex; } protected Texture wrapFrontTexture() { Texture tex = new Texture(pg.parent); tex.init(pg.width, pg.height, glColorTex.get(frontTex), TEXTURE_2D, RGBA, fboWidth, fboHeight, NEAREST, NEAREST, CLAMP_TO_EDGE, CLAMP_TO_EDGE); tex.invertedY(true); tex.colorBufferOf(pg); return tex; } protected int getBackTextureName() { return glColorTex.get(backTex); } protected int getFrontTextureName() { return glColorTex.get(frontTex); } protected void bindFrontTexture() { if (!texturingIsEnabled(TEXTURE_2D)) { enableTexturing(TEXTURE_2D); } bindTexture(TEXTURE_2D, glColorTex.get(frontTex)); } protected void unbindFrontTexture() { if (textureIsBound(TEXTURE_2D, glColorTex.get(frontTex))) { // We don't want to unbind another texture // that might be bound instead of this one. if (!texturingIsEnabled(TEXTURE_2D)) { enableTexturing(TEXTURE_2D); bindTexture(TEXTURE_2D, 0); disableTexturing(TEXTURE_2D); } else { bindTexture(TEXTURE_2D, 0); } } } protected void syncBackTexture() { if (1 < numSamples) { bindFramebuffer(READ_FRAMEBUFFER, glMultiFbo.get(0)); bindFramebuffer(DRAW_FRAMEBUFFER, glColorFbo.get(0)); blitFramebuffer(0, 0, fboWidth, fboHeight, 0, 0, fboWidth, fboHeight, COLOR_BUFFER_BIT, NEAREST); } } protected int qualityToSamples(int quality) { if (quality <= 1) { return 1; } else { // Number of samples is always an even number: int n = 2 * (quality / 2); return n; } } /////////////////////////////////////////////////////////// // Frame rendering protected void beginDraw(boolean clear0) { if (fboLayerInUse(clear0)) { bindFramebuffer(FRAMEBUFFER, glColorFbo.get(0)); framebufferTexture2D(FRAMEBUFFER, COLOR_ATTACHMENT0, TEXTURE_2D, glColorTex.get(backTex), 0); if (1 < numSamples) { bindFramebuffer(FRAMEBUFFER, glMultiFbo.get(0)); } if (firstFrame) { // No need to draw back color buffer because we are in the first frame. int argb = pg.backgroundColor; float a = ((argb >> 24) & 0xff) / 255.0f; float r = ((argb >> 16) & 0xff) / 255.0f; float g = ((argb >> 8) & 0xff) / 255.0f; float b = ((argb) & 0xff) / 255.0f; clearColor(r, g, b, a); clear(COLOR_BUFFER_BIT); } else if (!clear0) { // Render previous back texture (now is the front) as background, // because no background() is being used ("incremental drawing") drawTexture(TEXTURE_2D, glColorTex.get(frontTex), fboWidth, fboHeight, 0, 0, pg.width, pg.height, 0, 0, pg.width, pg.height); } fboLayerInUse = true; } else { fboLayerInUse = false; } if (firstFrame) { firstFrame = false; } if (!fboLayerByDefault) { // The result of this assignment is the following: if the user requested // at some point the use of the FBO layer, but subsequently didn't do // request it again, then the rendering won't use the FBO layer if not // needed, since it is slower than simple onscreen rendering. FORCE_SCREEN_FBO = false; } } protected void endDraw(boolean clear0) { if (fboLayerInUse) { syncBackTexture(); // Draw the contents of the back texture to the screen framebuffer. bindFramebuffer(FRAMEBUFFER, 0); clearDepth(1); clearColor(0, 0, 0, 0); clear(COLOR_BUFFER_BIT | DEPTH_BUFFER_BIT); // Render current back texture to screen, without blending. disable(BLEND); drawTexture(TEXTURE_2D, glColorTex.get(backTex), 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; } flush(); } protected boolean canDraw() { return pg.initialized && pg.parent.isDisplayable(); } protected void requestDraw() { if (pg.initialized) { glThread = Thread.currentThread(); pg.parent.handleDraw(); Display.update(); } } protected boolean threadIsCurrent() { return Thread.currentThread() == glThread; } protected boolean fboLayerInUse(boolean clear0) { boolean cond = !clear0 || FORCE_SCREEN_FBO || 1 < numSamples; return cond && glColorFbo.get(0) != 0; } ////////////////////////////////////////////////////////////////////////////// // Caps query public String getString(int name) { return GL11.glGetString(name); } public void getIntegerv(int name, IntBuffer values) { if (-1 < name) { GL11.glGetInteger(name, values); } else { fillIntBuffer(values, 0, values.capacity() - 1, 0); } } public void getFloatv(int name, FloatBuffer values) { if (-1 < name) { GL11.glGetFloat(name, values); } else { fillFloatBuffer(values, 0, values.capacity() - 1, 0); } } public void getBooleanv(int name, IntBuffer values) { if (-1 < name) { if (byteBuffer.capacity() < values.capacity()) { byteBuffer = allocateDirectByteBuffer(values.capacity()); } GL11.glGetBoolean(name, byteBuffer); for (int i = 0; i < values.capacity(); i++) { values.put(i, byteBuffer.get(i)); } } else { fillIntBuffer(values, 0, values.capacity() - 1, 0); } } /////////////////////////////////////////////////////////// // Enable/disable caps public void enable(int cap) { if (-1 < cap) { GL11.glEnable(cap); } } public void disable(int cap) { if (-1 < cap) { GL11.glDisable(cap); } } /////////////////////////////////////////////////////////// // Render control public void flush() { GL11.glFlush(); } public void finish() { GL11.glFinish(); } /////////////////////////////////////////////////////////// // Error handling public int getError() { return GL11.glGetError(); } public String errorString(int err) { return glu.gluErrorString(err); } /////////////////////////////////////////////////////////// // Rendering options public void frontFace(int mode) { GL11.glFrontFace(mode); } public void cullFace(int mode) { GL11.glCullFace(mode); } public void depthMask(boolean flag) { GL11.glDepthMask(flag); } public void depthFunc(int func) { GL11.glDepthFunc(func); } /////////////////////////////////////////////////////////// // Textures public void genTextures(int n, IntBuffer ids) { GL11.glGenTextures(ids); } public void deleteTextures(int n, IntBuffer ids) { GL11.glDeleteTextures(ids); } public void activeTexture(int unit) { GL13.glActiveTexture(unit); } public void bindTexture(int target, int id) { GL11.glBindTexture(target, id); if (target == TEXTURE_2D) { boundTextures[0] = id; } else if (target == TEXTURE_RECTANGLE) { boundTextures[1] = id; } } public void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, Buffer data) { GL11.glTexImage2D(target, level, internalFormat, width, height, border, format, type, (IntBuffer) data); } public void texSubImage2D(int target, int level, int xOffset, int yOffset, int width, int height, int format, int type, Buffer data) { GL11.glTexSubImage2D(target, level, xOffset, yOffset, width, height, format, type, (IntBuffer) data); } public void texParameteri(int target, int param, int value) { GL11.glTexParameteri(target, param, value); } public void texParameterf(int target, int param, float value) { GL11.glTexParameterf(target, param, value); } public void getTexParameteriv(int target, int param, IntBuffer values) { GL11.glGetTexParameter(target, param, values); } public void generateMipmap(int target) { GL30.glGenerateMipmap(target); } /////////////////////////////////////////////////////////// // Vertex Buffers public void genBuffers(int n, IntBuffer ids) { GL15.glGenBuffers(ids); } public void deleteBuffers(int n, IntBuffer ids) { GL15.glDeleteBuffers(ids); } public void bindBuffer(int target, int id) { GL15.glBindBuffer(target, id); } public void bufferData(int target, int size, Buffer data, int usage) { if (data == null) { FloatBuffer empty = BufferUtils.createFloatBuffer(size); GL15.glBufferData(target, empty, usage); } else { if (data instanceof ByteBuffer) { GL15.glBufferData(target, (ByteBuffer) data, usage); } else if (data instanceof ShortBuffer) { GL15.glBufferData(target, (ShortBuffer) data, usage); } else if (data instanceof IntBuffer) { GL15.glBufferData(target, (IntBuffer) data, usage); } else if (data instanceof FloatBuffer) { GL15.glBufferData(target, (FloatBuffer) data, usage); } } } public void bufferSubData(int target, int offset, int size, Buffer data) { if (data instanceof ByteBuffer) { GL15.glBufferSubData(target, offset, (ByteBuffer) data); } else if (data instanceof ShortBuffer) { GL15.glBufferSubData(target, offset, (ShortBuffer) data); } else if (data instanceof IntBuffer) { GL15.glBufferSubData(target, offset, (IntBuffer) data); } else if (data instanceof FloatBuffer) { GL15.glBufferSubData(target, offset, (FloatBuffer) data); } } public void drawArrays(int mode, int first, int count) { GL11.glDrawArrays(mode, first, count); } public void drawElements(int mode, int count, int type, int offset) { GL11.glDrawElements(mode, count, type, offset); } public void enableVertexAttribArray(int loc) { GL20.glEnableVertexAttribArray(loc); } public void disableVertexAttribArray(int loc) { GL20.glDisableVertexAttribArray(loc); } public void vertexAttribPointer(int loc, int size, int type, boolean normalized, int stride, int offset) { GL20.glVertexAttribPointer(loc, size, type, normalized, stride, offset); } public void vertexAttribPointer(int loc, int size, int type, boolean normalized, int stride, Buffer data) { if (type == UNSIGNED_INT) { GL20.glVertexAttribPointer(loc, size, true, normalized, stride, (IntBuffer) data); } else if (type == UNSIGNED_BYTE) { GL20.glVertexAttribPointer(loc, size, true, normalized, stride, (ByteBuffer) data); } else if (type == UNSIGNED_SHORT) { GL20.glVertexAttribPointer(loc, size, true, normalized, stride, (ShortBuffer) data); } else if (type == FLOAT) { GL20.glVertexAttribPointer(loc, size, normalized, stride, (FloatBuffer) data); } } public ByteBuffer mapBuffer(int target, int access) { return GL15.glMapBuffer(target, access, null); } public ByteBuffer mapBufferRange(int target, int offset, int length, int access) { return GL30.glMapBufferRange(target, offset, length, access, null); } public void unmapBuffer(int target) { GL15.glUnmapBuffer(target); } /////////////////////////////////////////////////////////// // Framebuffers, renderbuffers public void genFramebuffers(int n, IntBuffer ids) { GL30.glGenFramebuffers(ids); } public void deleteFramebuffers(int n, IntBuffer ids) { GL30.glDeleteFramebuffers(ids); } public void genRenderbuffers(int n, IntBuffer ids) { GL30.glGenRenderbuffers(ids); } public void deleteRenderbuffers(int n, IntBuffer ids) { GL30.glDeleteRenderbuffers(ids); } public void bindFramebuffer(int target, int id) { GL30.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) { GL30.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); } public void framebufferTexture2D(int target, int attachment, int texTarget, int texId, int level) { GL30.glFramebufferTexture2D(target, attachment, texTarget, texId, level); } public void bindRenderbuffer(int target, int id) { GL30.glBindRenderbuffer(target, id); } public void renderbufferStorageMultisample(int target, int samples, int format, int width, int height) { GL30.glRenderbufferStorageMultisample(target, samples, format, width, height); } public void renderbufferStorage(int target, int format, int width, int height) { GL30.glRenderbufferStorage(target, format, width, height); } public void framebufferRenderbuffer(int target, int attachment, int rendbufTarget, int rendbufId) { GL30.glFramebufferRenderbuffer(target, attachment, rendbufTarget, rendbufId); } public int checkFramebufferStatus(int target) { return GL30.glCheckFramebufferStatus(target); } /////////////////////////////////////////////////////////// // Shaders public int createProgram() { return GL20.glCreateProgram(); } public void deleteProgram(int id) { GL20.glDeleteProgram(id); } public int createShader(int type) { return GL20.glCreateShader(type); } public void deleteShader(int id) { GL20.glDeleteShader(id); } public void linkProgram(int prog) { GL20.glLinkProgram(prog); } public void validateProgram(int prog) { GL20.glValidateProgram(prog); } public void useProgram(int prog) { GL20.glUseProgram(prog); } public int getAttribLocation(int prog, String name) { return GL20.glGetAttribLocation(prog, name); } public int getUniformLocation(int prog, String name) { return GL20.glGetUniformLocation(prog, name); } public void uniform1i(int loc, int value) { GL20.glUniform1i(loc, value); } public void uniform2i(int loc, int value0, int value1) { GL20.glUniform2i(loc, value0, value1); } public void uniform3i(int loc, int value0, int value1, int value2) { GL20.glUniform3i(loc, value0, value1, value2); } public void uniform4i(int loc, int value0, int value1, int value2, int value3) { GL20.glUniform4i(loc, value0, value1, value2, value3); } public void uniform1f(int loc, float value) { GL20.glUniform1f(loc, value); } public void uniform2f(int loc, float value0, float value1) { GL20.glUniform2f(loc, value0, value1); } public void uniform3f(int loc, float value0, float value1, float value2) { GL20.glUniform3f(loc, value0, value1, value2); } public void uniform4f(int loc, float value0, float value1, float value2, float value3) { GL20.glUniform4f(loc, value0, value1, value2, value3); } public void uniform1iv(int loc, int count, IntBuffer v) { v.limit(count); GL20.glUniform1(loc, v); v.clear(); } public void uniform2iv(int loc, int count, IntBuffer v) { v.limit(2 * count); GL20.glUniform2(loc, v); v.clear(); } public void uniform3iv(int loc, int count, IntBuffer v) { v.limit(3 * count); GL20.glUniform3(loc, v); v.clear(); } public void uniform4iv(int loc, int count, IntBuffer v) { v.limit(4 * count); GL20.glUniform4(loc, v); v.clear(); } public void uniform1fv(int loc, int count, FloatBuffer v) { v.limit(count); GL20.glUniform1(loc, v); v.clear(); } public void uniform2fv(int loc, int count, FloatBuffer v) { v.limit(2 * count); GL20.glUniform2(loc, v); v.clear(); } public void uniform3fv(int loc, int count, FloatBuffer v) { v.limit(3 * count); GL20.glUniform3(loc, v); v.clear(); } public void uniform4fv(int loc, int count, FloatBuffer v) { v.limit(4 * count); GL20.glUniform4(loc, v); v.clear(); } public void uniformMatrix2fv(int loc, int count, boolean transpose, FloatBuffer mat) { mat.limit(4); GL20.glUniformMatrix2(loc, transpose, mat); mat.clear(); } public void uniformMatrix3fv(int loc, int count, boolean transpose, FloatBuffer mat) { mat.limit(9); GL20.glUniformMatrix3(loc, transpose, mat); mat.clear(); } public void uniformMatrix4fv(int loc, int count, boolean transpose, FloatBuffer mat) { mat.limit(16); GL20.glUniformMatrix4(loc, transpose, mat); mat.clear(); } public void vertexAttrib1f(int loc, float value) { GL20.glVertexAttrib1f(loc, value); } public void vertexAttrib2f(int loc, float value0, float value1) { GL20.glVertexAttrib2f(loc, value0, value1); } public void vertexAttrib3f(int loc, float value0, float value1, float value2) { GL20.glVertexAttrib3f(loc, value0, value1, value2); } public void vertexAttrib4f(int loc, float value0, float value1, float value2, float value3) { GL20.glVertexAttrib4f(loc, value0, value1, value2, value3); } public void shaderSource(int id, String source) { GL20.glShaderSource(id, source); } public void compileShader(int id) { GL20.glCompileShader(id); } public void attachShader(int prog, int shader) { GL20.glAttachShader(prog, shader); } public void getShaderiv(int shader, int pname, IntBuffer params) { GL20.glGetShader(shader, pname, params); } public String getShaderInfoLog(int shader) { int len = GL20.glGetShaderi(shader, GL20.GL_INFO_LOG_LENGTH); return GL20.glGetShaderInfoLog(shader, len); } public void getProgramiv(int prog, int pname, IntBuffer params) { GL20.glGetProgram(prog, pname, params); } public String getProgramInfoLog(int prog) { int len = GL20.glGetProgrami(prog, GL20.GL_INFO_LOG_LENGTH); return GL20.glGetProgramInfoLog(prog, len); } /////////////////////////////////////////////////////////// // Viewport public void viewport(int x, int y, int width, int height) { GL11.glViewport(x, y, width, height); } /////////////////////////////////////////////////////////// // Clipping (scissor test) public void scissor(int x, int y, int w, int h) { GL11.glScissor(x, y, w, h); } /////////////////////////////////////////////////////////// // Blending public void blendEquation(int eq) { GL14.glBlendEquation(eq); } public void blendFunc(int srcFactor, int dstFactor) { GL11.glBlendFunc(srcFactor, dstFactor); } /////////////////////////////////////////////////////////// // Pixels public void readBuffer(int buf) { GL11.glReadBuffer(buf); } public void readPixels(int x, int y, int width, int height, int format, int type, Buffer buffer) { GL11.glReadPixels(x, y, width, height, format, type, (IntBuffer) buffer); } public void drawBuffer(int buf) { GL11.glDrawBuffer(buf); } public void clearDepth(float d) { GL11.glClearDepth(d); } public void clearStencil(int s) { GL11.glClearStencil(s); } public void colorMask(boolean wr, boolean wg, boolean wb, boolean wa) { GL11.glColorMask(wr, wg, wb, wa); } public void clearColor(float r, float g, float b, float a) { GL11.glClearColor(r, g, b, a); } public void clear(int mask) { GL11.glClear(mask); } /////////////////////////////////////////////////////////// // Context interface protected int createEmptyContext() { return -1; } protected int getCurrentContext() { return context; } /////////////////////////////////////////////////////////// // Tessellator interface protected Tessellator createTessellator(TessellatorCallback callback) { return new Tessellator(callback); } protected class Tessellator { protected GLUtessellator tess; protected TessellatorCallback callback; protected GLUCallback gluCallback; public Tessellator(TessellatorCallback callback) { this.callback = callback; tess = GLU.gluNewTess(); gluCallback = new GLUCallback(); tess.gluTessCallback(GLU.GLU_TESS_BEGIN, gluCallback); tess.gluTessCallback(GLU.GLU_TESS_END, gluCallback); tess.gluTessCallback(GLU.GLU_TESS_VERTEX, gluCallback); tess.gluTessCallback(GLU.GLU_TESS_COMBINE, gluCallback); tess.gluTessCallback(GLU.GLU_TESS_ERROR, gluCallback); } public void beginPolygon() { tess.gluTessBeginPolygon(null); } public void endPolygon() { tess.gluTessEndPolygon(); } public void setWindingRule(int rule) { tess.gluTessProperty(GLU.GLU_TESS_WINDING_RULE, rule); } public void beginContour() { tess.gluTessBeginContour(); } public void endContour() { tess.gluTessEndContour(); } public void addVertex(double[] v) { tess.gluTessVertex(v, 0, v); } protected class GLUCallback extends GLUtessellatorCallbackAdapter { @Override public void begin(int type) { callback.begin(type); } @Override public void end() { callback.end(); } @Override public void vertex(Object data) { callback.vertex(data); } @Override public void combine(double[] coords, Object[] data, float[] weight, Object[] outData) { callback.combine(coords, data, weight, outData); } @Override public void error(int errnum) { callback.error(errnum); } } } protected String tessError(int err) { return glu.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(int other) { return other == -1 || other == context; } protected void enableTexturing(int target) { enable(target); if (target == TEXTURE_2D) { texturingTargets[0] = true; } else if (target == TEXTURE_RECTANGLE) { texturingTargets[1] = true; } } protected void disableTexturing(int target) { disable(target); if (target == TEXTURE_2D) { texturingTargets[0] = false; } else if (target == TEXTURE_RECTANGLE) { texturingTargets[1] = false; } } protected boolean texturingIsEnabled(int target) { if (target == TEXTURE_2D) { return texturingTargets[0]; } else if (target == TEXTURE_RECTANGLE) { return texturingTargets[1]; } else { return false; } } protected boolean textureIsBound(int target, int id) { if (target == TEXTURE_2D) { return boundTextures[0] == id; } else if (target == TEXTURE_RECTANGLE) { return boundTextures[1] == id; } else { return false; } } protected void initTexture(int target, int format, int width, int height) { initTexture(target, format, width, height, 0); } protected void initTexture(int target, int format, int width, int height, int initColor) { int[] glcolor = new int[16 * 16]; Arrays.fill(glcolor, javaToNativeARGB(initColor)); IntBuffer texels = PGL.allocateDirectIntBuffer(16 * 16); texels.put(glcolor); texels.rewind(); 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, texels); } } } protected 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); } } protected 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); } protected 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 (target == TEXTURE_2D) { drawTexture2D(id, width, height, texX0, texY0, texX1, texY1, scrX0, scrY0, scrX1, scrY1); } else if (target == TEXTURE_RECTANGLE) { drawTextureRect(id, width, height, texX0, texY0, texX1, texY1, scrX0, scrY0, scrX1, scrY1); } } protected void drawTexture2D(int id, int width, int height, int texX0, int texY0, int texX1, int texY1, int scrX0, int scrY0, int scrX1, int scrY1) { if (!loadedTex2DShader || tex2DShaderContext != context) { tex2DVertShader = createShader(VERTEX_SHADER, texVertShaderSource); tex2DFragShader = createShader(FRAGMENT_SHADER, tex2DFragShaderSource); if (0 < tex2DVertShader && 0 < tex2DFragShader) { tex2DShaderProgram = createProgram(tex2DVertShader, tex2DFragShader); } if (0 < tex2DShaderProgram) { tex2DVertLoc = getAttribLocation(tex2DShaderProgram, "inVertex"); tex2DTCoordLoc = getAttribLocation(tex2DShaderProgram, "inTexcoord"); } loadedTex2DShader = true; tex2DShaderContext = context; } if (texData == null) { texData = allocateDirectFloatBuffer(texCoords.length); } if (0 < tex2DShaderProgram) { // The texture overwrites anything drawn earlier. boolean depthTest = getDepthTest(); 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 = getDepthWriteMask(); depthMask(false); useProgram(tex2DShaderProgram); enableVertexAttribArray(tex2DVertLoc); enableVertexAttribArray(tex2DTCoordLoc); // 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(TEXTURE_2D, id); bindBuffer(ARRAY_BUFFER, 0); // Making sure that no VBO is bound at this point. texData.position(0); vertexAttribPointer(tex2DVertLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, texData); texData.position(2); vertexAttribPointer(tex2DTCoordLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, texData); drawArrays(TRIANGLE_STRIP, 0, 4); bindTexture(TEXTURE_2D, 0); if (enabledTex) { disableTexturing(TEXTURE_2D); } disableVertexAttribArray(tex2DVertLoc); disableVertexAttribArray(tex2DTCoordLoc); useProgram(0); if (depthTest) { enable(DEPTH_TEST); } else { disable(DEPTH_TEST); } depthMask(depthMask); } } protected void drawTextureRect(int id, int width, int height, int texX0, int texY0, int texX1, int texY1, int scrX0, int scrY0, int scrX1, int scrY1) { if (!loadedTexRectShader || texRectShaderContext != context) { texRectVertShader = createShader(VERTEX_SHADER, texVertShaderSource); texRectFragShader = createShader(FRAGMENT_SHADER, texRectFragShaderSource); if (0 < texRectVertShader && 0 < texRectFragShader) { texRectShaderProgram = createProgram(texRectVertShader, texRectFragShader); } if (0 < texRectShaderProgram) { texRectVertLoc = getAttribLocation(texRectShaderProgram, "inVertex"); texRectTCoordLoc = getAttribLocation(texRectShaderProgram, "inTexcoord"); } loadedTexRectShader = true; texRectShaderContext = context; } if (texData == null) { texData = allocateDirectFloatBuffer(texCoords.length); } if (0 < texRectShaderProgram) { // The texture overwrites anything drawn earlier. boolean depthTest = getDepthTest(); 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 = getDepthWriteMask(); depthMask(false); useProgram(texRectShaderProgram); enableVertexAttribArray(texRectVertLoc); enableVertexAttribArray(texRectTCoordLoc); // 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] = texX0; texCoords[3] = texY0; // Corner 2 texCoords[4] = 2 * (float) scrX1 / pg.width - 1; texCoords[5] = 2 * (float) scrY0 / pg.height - 1; texCoords[6] = texX1; texCoords[7] = texY0; // Corner 3 texCoords[8] = 2 * (float) scrX0 / pg.width - 1; texCoords[9] = 2 * (float) scrY1 / pg.height - 1; texCoords[10] = texX0; texCoords[11] = texY1; // Corner 4 texCoords[12] = 2 * (float) scrX1 / pg.width - 1; texCoords[13] = 2 * (float) scrY1 / pg.height - 1; texCoords[14] = texX1; texCoords[15] = texY1; texData.rewind(); texData.put(texCoords); activeTexture(TEXTURE0); boolean enabledTex = false; if (!texturingIsEnabled(TEXTURE_RECTANGLE)) { enableTexturing(TEXTURE_RECTANGLE); enabledTex = true; } bindTexture(TEXTURE_RECTANGLE, id); bindBuffer(ARRAY_BUFFER, 0); // Making sure that no VBO is bound at this point. texData.position(0); vertexAttribPointer(texRectVertLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, texData); texData.position(2); vertexAttribPointer(texRectTCoordLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, texData); drawArrays(TRIANGLE_STRIP, 0, 4); bindTexture(TEXTURE_RECTANGLE, 0); if (enabledTex) { disableTexturing(TEXTURE_RECTANGLE); } disableVertexAttribArray(texRectVertLoc); disableVertexAttribArray(texRectTCoordLoc); useProgram(0); if (depthTest) { enable(DEPTH_TEST); } else { disable(DEPTH_TEST); } depthMask(depthMask); } } protected 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(); } protected float getDepthValue(int scrX, int scrY) { if (depthBuffer == null) { depthBuffer = FloatBuffer.allocate(1); } depthBuffer.rewind(); readPixels(scrX, pg.height - scrY - 1, 1, 1, DEPTH_COMPONENT, FLOAT, depthBuffer); return depthBuffer.get(0); } protected byte getStencilValue(int scrX, int scrY) { if (stencilBuffer == null) { stencilBuffer = ByteBuffer.allocate(1); } readPixels(scrX, pg.height - scrY - 1, 1, 1, STENCIL_INDEX, UNSIGNED_BYTE, stencilBuffer); return stencilBuffer.get(0); } // bit shifting this might be more efficient protected 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. */ protected 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. */ protected 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). */ protected 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. */ protected 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). */ protected 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. */ protected 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). */ protected 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. */ protected 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++; } } } } protected int createShader(int shaderType, String source) { int shader = createShader(shaderType); if (shader != 0) { shaderSource(shader, source); compileShader(shader); if (!compiled(shader)) { System.err.println("Could not compile shader " + shaderType + ":"); System.err.println(getShaderInfoLog(shader)); deleteShader(shader); shader = 0; } } return shader; } protected int createProgram(int vertexShader, int fragmentShader) { int program = createProgram(); if (program != 0) { attachShader(program, vertexShader); attachShader(program, fragmentShader); linkProgram(program); if (!linked(program)) { System.err.println("Could not link program: "); System.err.println(getProgramInfoLog(program)); deleteProgram(program); program = 0; } } return program; } protected boolean compiled(int shader) { intBuffer.rewind(); getShaderiv(shader, COMPILE_STATUS, intBuffer); return intBuffer.get(0) == 0 ? false : true; } protected boolean linked(int program) { intBuffer.rewind(); getProgramiv(program, LINK_STATUS, intBuffer); return intBuffer.get(0) == 0 ? false : true; } protected boolean validateFramebuffer() { int status = checkFramebufferStatus(FRAMEBUFFER); if (status == FRAMEBUFFER_COMPLETE) { return true; } else if (status == FRAMEBUFFER_INCOMPLETE_ATTACHMENT) { throw new RuntimeException( "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT (" + Integer.toHexString(status) + ")"); } else if (status == FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT) { throw new RuntimeException( "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT (" + Integer.toHexString(status) + ")"); } else if (status == FRAMEBUFFER_INCOMPLETE_DIMENSIONS) { throw new RuntimeException( "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS (" + Integer.toHexString(status) + ")"); } else if (status == FRAMEBUFFER_INCOMPLETE_FORMATS) { throw new RuntimeException("GL_FRAMEBUFFER_INCOMPLETE_FORMATS (" + Integer.toHexString(status) + ")"); } else if (status == FRAMEBUFFER_UNSUPPORTED) { throw new RuntimeException("GL_FRAMEBUFFER_UNSUPPORTED" + Integer.toHexString(status)); } else { throw new RuntimeException("unknown framebuffer error (" + Integer.toHexString(status) + ")"); } } protected int[] getGLVersion() { String version = getString(VERSION).trim(); int[] res = { 0, 0, 0 }; String[] parts = version.split(" "); for (int i = 0; i < parts.length; i++) { if (0 < parts[i].indexOf(".")) { String nums[] = parts[i].split("\\."); try { res[0] = Integer.parseInt(nums[0]); } catch (NumberFormatException e) { } if (1 < nums.length) { try { res[1] = Integer.parseInt(nums[1]); } catch (NumberFormatException e) { } } if (2 < nums.length) { try { res[2] = Integer.parseInt(nums[2]); } catch (NumberFormatException e) { } } break; } } return res; } protected static ByteBuffer allocateDirectByteBuffer(int size) { int bytes = PApplet.max(MIN_DIRECT_BUFFER_SIZE, size) * SIZEOF_BYTE; return ByteBuffer.allocateDirect(bytes).order(ByteOrder.nativeOrder()); } protected static ByteBuffer allocateByteBuffer(int size) { if (USE_DIRECT_BUFFERS) { return allocateDirectByteBuffer(size); } else { return ByteBuffer.allocate(size); } } protected static ByteBuffer allocateByteBuffer(byte[] arr) { if (USE_DIRECT_BUFFERS) { return PGL.allocateDirectByteBuffer(arr.length); } else { return ByteBuffer.wrap(arr); } } protected static ByteBuffer updateByteBuffer(ByteBuffer buf, byte[] arr, boolean wrap) { if (USE_DIRECT_BUFFERS) { if (buf == null || buf.capacity() < arr.length) { buf = PGL.allocateDirectByteBuffer(arr.length); } buf.position(0); buf.put(arr); buf.rewind(); } else { if (wrap) { buf = ByteBuffer.wrap(arr); } else { if (buf == null || buf.capacity() < arr.length) { buf = ByteBuffer.allocate(arr.length); } buf.position(0); buf.put(arr); buf.rewind(); } } return buf; } protected static void getByteArray(ByteBuffer buf, byte[] arr) { if (!buf.hasArray() || buf.array() != arr) { buf.position(0); buf.get(arr); buf.rewind(); } } protected static void putByteArray(ByteBuffer buf, byte[] arr) { if (!buf.hasArray() || buf.array() != arr) { buf.position(0); buf.put(arr); buf.rewind(); } } protected static void fillByteBuffer(ByteBuffer buf, int i0, int i1, byte val) { int n = i1 - i0; byte[] temp = new byte[n]; Arrays.fill(temp, 0, n, val); buf.position(i0); buf.put(temp, 0, n); buf.rewind(); } protected static ShortBuffer allocateDirectShortBuffer(int size) { int bytes = PApplet.max(MIN_DIRECT_BUFFER_SIZE, size) * SIZEOF_SHORT; return ByteBuffer.allocateDirect(bytes).order(ByteOrder.nativeOrder()).asShortBuffer(); } protected static ShortBuffer allocateShortBuffer(int size) { if (USE_DIRECT_BUFFERS) { return allocateDirectShortBuffer(size); } else { return ShortBuffer.allocate(size); } } protected static ShortBuffer allocateShortBuffer(short[] arr) { if (USE_DIRECT_BUFFERS) { return PGL.allocateDirectShortBuffer(arr.length); } else { return ShortBuffer.wrap(arr); } } protected static ShortBuffer updateShortBuffer(ShortBuffer buf, short[] arr, boolean wrap) { if (USE_DIRECT_BUFFERS) { if (buf == null || buf.capacity() < arr.length) { buf = PGL.allocateDirectShortBuffer(arr.length); } buf.position(0); buf.put(arr); buf.rewind(); } else { if (wrap) { buf = ShortBuffer.wrap(arr); } else { if (buf == null || buf.capacity() < arr.length) { buf = ShortBuffer.allocate(arr.length); } buf.position(0); buf.put(arr); buf.rewind(); } } return buf; } protected static void getShortArray(ShortBuffer buf, short[] arr) { if (!buf.hasArray() || buf.array() != arr) { buf.position(0); buf.get(arr); buf.rewind(); } } protected static void putShortArray(ShortBuffer buf, short[] arr) { if (!buf.hasArray() || buf.array() != arr) { buf.position(0); buf.put(arr); buf.rewind(); } } protected static void fillShortBuffer(ShortBuffer buf, int i0, int i1, short val) { int n = i1 - i0; short[] temp = new short[n]; Arrays.fill(temp, 0, n, val); buf.position(i0); buf.put(temp, 0, n); buf.rewind(); } protected static IntBuffer allocateDirectIntBuffer(int size) { int bytes = PApplet.max(MIN_DIRECT_BUFFER_SIZE, size) * SIZEOF_INT; return ByteBuffer.allocateDirect(bytes).order(ByteOrder.nativeOrder()).asIntBuffer(); } protected static IntBuffer allocateIntBuffer(int size) { if (USE_DIRECT_BUFFERS) { return allocateDirectIntBuffer(size); } else { return IntBuffer.allocate(size); } } protected static IntBuffer allocateIntBuffer(int[] arr) { if (USE_DIRECT_BUFFERS) { return PGL.allocateDirectIntBuffer(arr.length); } else { return IntBuffer.wrap(arr); } } protected static IntBuffer updateIntBuffer(IntBuffer buf, int[] arr, boolean wrap) { if (USE_DIRECT_BUFFERS) { if (buf == null || buf.capacity() < arr.length) { buf = PGL.allocateDirectIntBuffer(arr.length); } buf.position(0); buf.put(arr); buf.rewind(); } else { if (wrap) { buf = IntBuffer.wrap(arr); } else { if (buf == null || buf.capacity() < arr.length) { buf = IntBuffer.allocate(arr.length); } buf.position(0); buf.put(arr); buf.rewind(); } } return buf; } protected static void getIntArray(IntBuffer buf, int[] arr) { if (!buf.hasArray() || buf.array() != arr) { buf.position(0); buf.get(arr); buf.rewind(); } } protected static void putIntArray(IntBuffer buf, int[] arr) { if (!buf.hasArray() || buf.array() != arr) { buf.position(0); buf.put(arr); buf.rewind(); } } protected static void fillIntBuffer(IntBuffer buf, int i0, int i1, int val) { int n = i1 - i0; int[] temp = new int[n]; Arrays.fill(temp, 0, n, val); buf.position(i0); buf.put(temp, 0, n); buf.rewind(); } protected static FloatBuffer allocateDirectFloatBuffer(int size) { int bytes = PApplet.max(MIN_DIRECT_BUFFER_SIZE, size) * SIZEOF_FLOAT; return ByteBuffer.allocateDirect(bytes).order(ByteOrder.nativeOrder()).asFloatBuffer(); } protected static FloatBuffer allocateFloatBuffer(int size) { if (USE_DIRECT_BUFFERS) { return allocateDirectFloatBuffer(size); } else { return FloatBuffer.allocate(size); } } protected static FloatBuffer allocateFloatBuffer(float[] arr) { if (USE_DIRECT_BUFFERS) { return PGL.allocateDirectFloatBuffer(arr.length); } else { return FloatBuffer.wrap(arr); } } protected static FloatBuffer updateFloatBuffer(FloatBuffer buf, float[] arr, boolean wrap) { if (USE_DIRECT_BUFFERS) { if (buf == null || buf.capacity() < arr.length) { buf = PGL.allocateDirectFloatBuffer(arr.length); } buf.position(0); buf.put(arr); buf.rewind(); } else { if (wrap) { buf = FloatBuffer.wrap(arr); } else { if (buf == null || buf.capacity() < arr.length) { buf = FloatBuffer.allocate(arr.length); } buf.position(0); buf.put(arr); buf.rewind(); } } return buf; } protected static void getFloatArray(FloatBuffer buf, float[] arr) { if (!buf.hasArray() || buf.array() != arr) { buf.position(0); buf.get(arr); buf.rewind(); } } protected static void putFloatArray(FloatBuffer buf, float[] arr) { if (!buf.hasArray() || buf.array() != arr) { buf.position(0); buf.put(arr); buf.rewind(); } } protected static void fillFloatBuffer(FloatBuffer buf, int i0, int i1, float val) { int n = i1 - i0; float[] temp = new float[n]; Arrays.fill(temp, 0, n, val); buf.position(i0); buf.put(temp, 0, n); buf.rewind(); } /////////////////////////////////////////////////////////// // Java specific stuff protected class KeyPoller extends Thread { protected PApplet parent; protected boolean stopRequested; protected boolean[] pressedKeys; protected char[] charCheys; KeyPoller(PApplet parent) { this.parent = parent; stopRequested = false; } @Override public void run() { pressedKeys = new boolean[256]; charCheys = new char[256]; Keyboard.enableRepeatEvents(true); while (true) { if (stopRequested) break; Keyboard.poll(); while (Keyboard.next()) { if (stopRequested) break; long millis = Keyboard.getEventNanoseconds() / 1000000L; char keyChar = Keyboard.getEventCharacter(); int keyCode = Keyboard.getEventKey(); if (keyCode >= pressedKeys.length) continue; int modifiers = 0; if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT)) { modifiers |= Event.SHIFT; } if (Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL)) { modifiers |= Event.CTRL; } if (Keyboard.isKeyDown(Keyboard.KEY_LMETA) || Keyboard.isKeyDown(Keyboard.KEY_RMETA)) { modifiers |= Event.META; } if (Keyboard.isKeyDown(Keyboard.KEY_LMENU) || Keyboard.isKeyDown(Keyboard.KEY_RMENU)) { // LWJGL maps the menu key and the alt key to the same value. modifiers |= Event.ALT; } int keyPCode = LWJGLtoAWTCode(keyCode); if (keyChar == 0) { keyChar = PConstants.CODED; } int action = 0; if (Keyboard.getEventKeyState()) { action = KeyEvent.PRESS; KeyEvent ke = new KeyEvent(null, millis, action, modifiers, keyChar, keyPCode); parent.postEvent(ke); pressedKeys[keyCode] = true; charCheys[keyCode] = keyChar; keyPCode = 0; action = KeyEvent.TYPE; } else if (pressedKeys[keyCode]) { keyChar = charCheys[keyCode]; pressedKeys[keyCode] = false; action = KeyEvent.RELEASE; } KeyEvent ke = new KeyEvent(null, millis, action, modifiers, keyChar, keyPCode); parent.postEvent(ke); } try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } public void requestStop() { stopRequested = true; } } protected class MousePoller extends Thread { protected PApplet parent; protected boolean stopRequested; protected boolean pressed; protected boolean inside; protected long startedClickTime; protected int startedClickButton; MousePoller(PApplet parent) { this.parent = parent; stopRequested = false; } @Override public void run() { while (true) { if (stopRequested) break; Mouse.poll(); while (Mouse.next()) { if (stopRequested) break; long millis = Mouse.getEventNanoseconds() / 1000000L; int modifiers = 0; if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT)) { modifiers |= Event.SHIFT; } if (Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL)) { modifiers |= Event.CTRL; } if (Keyboard.isKeyDown(Keyboard.KEY_LMETA) || Keyboard.isKeyDown(Keyboard.KEY_RMETA)) { modifiers |= Event.META; } if (Keyboard.isKeyDown(Keyboard.KEY_LMENU) || Keyboard.isKeyDown(Keyboard.KEY_RMENU)) { // LWJGL maps the menu key and the alt key to the same value. modifiers |= Event.ALT; } int x = Mouse.getX(); int y = parent.height - Mouse.getY(); int button = 0; if (Mouse.isButtonDown(0)) { button = PConstants.LEFT; } else if (Mouse.isButtonDown(1)) { button = PConstants.RIGHT; } else if (Mouse.isButtonDown(2)) { button = PConstants.CENTER; } int action = 0; if (button != 0) { if (pressed) { action = MouseEvent.DRAG; } else { action = MouseEvent.PRESS; pressed = true; } } else if (pressed) { action = MouseEvent.RELEASE; pressed = false; } else { action = MouseEvent.MOVE; } if (inside) { if (!Mouse.isInsideWindow()) { inside = false; action = MouseEvent.EXIT; } } else { if (Mouse.isInsideWindow()) { inside = true; action = MouseEvent.ENTER; } } int count = 0; if (Mouse.getEventButtonState()) { startedClickTime = millis; startedClickButton = button; } else { if (action == MouseEvent.RELEASE) { boolean clickDetected = millis - startedClickTime < 500; if (clickDetected) { // post a RELEASE event, in addition to the CLICK event. MouseEvent me = new MouseEvent(null, millis, action, modifiers, x, y, button, count); parent.postEvent(me); action = MouseEvent.CLICK; count = 1; } } } MouseEvent me = new MouseEvent(null, millis, action, modifiers, x, y, button, count); parent.postEvent(me); } try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } public void requestStop() { stopRequested = true; } } // To complete later... // http://docs.oracle.com/javase/6/docs/api/java/awt/event/KeyEvent.html // http://processing.org/reference/keyCode.html protected int LWJGLtoAWTCode(int code) { switch (code) { case Keyboard.KEY_0: return java.awt.event.KeyEvent.VK_0; case Keyboard.KEY_1: return java.awt.event.KeyEvent.VK_1; case Keyboard.KEY_2: return java.awt.event.KeyEvent.VK_2; case Keyboard.KEY_3: return java.awt.event.KeyEvent.VK_3; case Keyboard.KEY_4: return java.awt.event.KeyEvent.VK_4; case Keyboard.KEY_5: return java.awt.event.KeyEvent.VK_5; case Keyboard.KEY_6: return java.awt.event.KeyEvent.VK_6; case Keyboard.KEY_7: return java.awt.event.KeyEvent.VK_7; case Keyboard.KEY_8: return java.awt.event.KeyEvent.VK_8; case Keyboard.KEY_9: return java.awt.event.KeyEvent.VK_9; case Keyboard.KEY_A: return java.awt.event.KeyEvent.VK_A; case Keyboard.KEY_B: return java.awt.event.KeyEvent.VK_B; case Keyboard.KEY_C: return java.awt.event.KeyEvent.VK_C; case Keyboard.KEY_D: return java.awt.event.KeyEvent.VK_D; case Keyboard.KEY_E: return java.awt.event.KeyEvent.VK_E; case Keyboard.KEY_F: return java.awt.event.KeyEvent.VK_F; case Keyboard.KEY_G: return java.awt.event.KeyEvent.VK_G; case Keyboard.KEY_H: return java.awt.event.KeyEvent.VK_H; case Keyboard.KEY_I: return java.awt.event.KeyEvent.VK_I; case Keyboard.KEY_J: return java.awt.event.KeyEvent.VK_J; case Keyboard.KEY_K: return java.awt.event.KeyEvent.VK_K; case Keyboard.KEY_L: return java.awt.event.KeyEvent.VK_L; case Keyboard.KEY_M: return java.awt.event.KeyEvent.VK_M; case Keyboard.KEY_N: return java.awt.event.KeyEvent.VK_N; case Keyboard.KEY_O: return java.awt.event.KeyEvent.VK_O; case Keyboard.KEY_P: return java.awt.event.KeyEvent.VK_P; case Keyboard.KEY_Q: return java.awt.event.KeyEvent.VK_Q; case Keyboard.KEY_R: return java.awt.event.KeyEvent.VK_R; case Keyboard.KEY_S: return java.awt.event.KeyEvent.VK_S; case Keyboard.KEY_T: return java.awt.event.KeyEvent.VK_T; case Keyboard.KEY_U: return java.awt.event.KeyEvent.VK_U; case Keyboard.KEY_V: return java.awt.event.KeyEvent.VK_V; case Keyboard.KEY_W: return java.awt.event.KeyEvent.VK_W; case Keyboard.KEY_X: return java.awt.event.KeyEvent.VK_X; case Keyboard.KEY_Y: return java.awt.event.KeyEvent.VK_Y; case Keyboard.KEY_Z: return java.awt.event.KeyEvent.VK_Z; case Keyboard.KEY_ADD: return java.awt.event.KeyEvent.VK_ADD; case Keyboard.KEY_APOSTROPHE: return java.awt.event.KeyEvent.VK_ASTERISK; case Keyboard.KEY_AT: return java.awt.event.KeyEvent.VK_AT; case Keyboard.KEY_BACK: return java.awt.event.KeyEvent.VK_BACK_SPACE; case Keyboard.KEY_BACKSLASH: return java.awt.event.KeyEvent.VK_BACK_SLASH; case Keyboard.KEY_CAPITAL: return java.awt.event.KeyEvent.VK_CAPS_LOCK; case Keyboard.KEY_CIRCUMFLEX: return java.awt.event.KeyEvent.VK_CIRCUMFLEX; case Keyboard.KEY_COLON: return java.awt.event.KeyEvent.VK_COLON; case Keyboard.KEY_COMMA: return java.awt.event.KeyEvent.VK_COMMA; case Keyboard.KEY_CONVERT: return java.awt.event.KeyEvent.VK_CONVERT; case Keyboard.KEY_DECIMAL: return java.awt.event.KeyEvent.VK_DECIMAL; case Keyboard.KEY_DELETE: return java.awt.event.KeyEvent.VK_DELETE; case Keyboard.KEY_DIVIDE: return java.awt.event.KeyEvent.VK_DIVIDE; case Keyboard.KEY_DOWN: return java.awt.event.KeyEvent.VK_DOWN; case Keyboard.KEY_END: return java.awt.event.KeyEvent.VK_END; case Keyboard.KEY_EQUALS: return java.awt.event.KeyEvent.VK_EQUALS; case Keyboard.KEY_ESCAPE: return java.awt.event.KeyEvent.VK_ESCAPE; case Keyboard.KEY_F1: return java.awt.event.KeyEvent.VK_F1; case Keyboard.KEY_F10: return java.awt.event.KeyEvent.VK_F10; case Keyboard.KEY_F11: return java.awt.event.KeyEvent.VK_F11; case Keyboard.KEY_F12: return java.awt.event.KeyEvent.VK_F12; case Keyboard.KEY_F13: return java.awt.event.KeyEvent.VK_F13; case Keyboard.KEY_F14: return java.awt.event.KeyEvent.VK_F14; case Keyboard.KEY_F15: return java.awt.event.KeyEvent.VK_F15; case Keyboard.KEY_F2: return java.awt.event.KeyEvent.VK_F2; case Keyboard.KEY_F3: return java.awt.event.KeyEvent.VK_F3; case Keyboard.KEY_F4: return java.awt.event.KeyEvent.VK_F4; case Keyboard.KEY_F5: return java.awt.event.KeyEvent.VK_F5; case Keyboard.KEY_F6: return java.awt.event.KeyEvent.VK_F6; case Keyboard.KEY_F7: return java.awt.event.KeyEvent.VK_F7; case Keyboard.KEY_F8: return java.awt.event.KeyEvent.VK_F8; case Keyboard.KEY_F9: return java.awt.event.KeyEvent.VK_F9; // case Keyboard.KEY_GRAVE: case Keyboard.KEY_HOME: return java.awt.event.KeyEvent.VK_HOME; case Keyboard.KEY_INSERT: return java.awt.event.KeyEvent.VK_INSERT; case Keyboard.KEY_LBRACKET: return java.awt.event.KeyEvent.VK_BRACELEFT; case Keyboard.KEY_LCONTROL: return java.awt.event.KeyEvent.VK_CONTROL; case Keyboard.KEY_LEFT: return java.awt.event.KeyEvent.VK_LEFT; case Keyboard.KEY_LMENU: return java.awt.event.KeyEvent.VK_ALT; case Keyboard.KEY_LMETA: return java.awt.event.KeyEvent.VK_META; case Keyboard.KEY_LSHIFT: return java.awt.event.KeyEvent.VK_SHIFT; case Keyboard.KEY_MINUS: return java.awt.event.KeyEvent.VK_MINUS; case Keyboard.KEY_MULTIPLY: return java.awt.event.KeyEvent.VK_MULTIPLY; // case Keyboard.KEY_NEXT: case Keyboard.KEY_NUMLOCK: return java.awt.event.KeyEvent.VK_NUM_LOCK; case Keyboard.KEY_NUMPAD0: return java.awt.event.KeyEvent.VK_NUMPAD0; case Keyboard.KEY_NUMPAD1: return java.awt.event.KeyEvent.VK_NUMPAD1; case Keyboard.KEY_NUMPAD2: return java.awt.event.KeyEvent.VK_NUMPAD2; case Keyboard.KEY_NUMPAD3: return java.awt.event.KeyEvent.VK_NUMPAD3; case Keyboard.KEY_NUMPAD4: return java.awt.event.KeyEvent.VK_NUMPAD4; case Keyboard.KEY_NUMPAD5: return java.awt.event.KeyEvent.VK_NUMPAD5; case Keyboard.KEY_NUMPAD6: return java.awt.event.KeyEvent.VK_NUMPAD6; case Keyboard.KEY_NUMPAD7: return java.awt.event.KeyEvent.VK_NUMPAD7; case Keyboard.KEY_NUMPAD8: return java.awt.event.KeyEvent.VK_NUMPAD8; case Keyboard.KEY_NUMPAD9: return java.awt.event.KeyEvent.VK_NUMPAD9; // case Keyboard.KEY_NUMPADCOMMA: // case Keyboard.KEY_NUMPADENTER: // case Keyboard.KEY_NUMPADEQUALS: case Keyboard.KEY_PAUSE: return java.awt.event.KeyEvent.VK_PAUSE; case Keyboard.KEY_PERIOD: return java.awt.event.KeyEvent.VK_PERIOD; // case Keyboard.KEY_POWER: // case Keyboard.KEY_PRIOR: case Keyboard.KEY_RBRACKET: return java.awt.event.KeyEvent.VK_BRACERIGHT; case Keyboard.KEY_RCONTROL: return java.awt.event.KeyEvent.VK_CONTROL; case Keyboard.KEY_RETURN: return java.awt.event.KeyEvent.VK_ENTER; case Keyboard.KEY_RIGHT: return java.awt.event.KeyEvent.VK_RIGHT; // case Keyboard.KEY_RMENU: case Keyboard.KEY_RMETA: return java.awt.event.KeyEvent.VK_META; case Keyboard.KEY_RSHIFT: return java.awt.event.KeyEvent.VK_SHIFT; // case Keyboard.KEY_SCROLL: case Keyboard.KEY_SEMICOLON: return java.awt.event.KeyEvent.VK_SEMICOLON; case Keyboard.KEY_SLASH: return java.awt.event.KeyEvent.VK_SLASH; // case Keyboard.KEY_SLEEP: case Keyboard.KEY_SPACE: return java.awt.event.KeyEvent.VK_SPACE; case Keyboard.KEY_STOP: return java.awt.event.KeyEvent.VK_STOP; case Keyboard.KEY_SUBTRACT: return java.awt.event.KeyEvent.VK_SUBTRACT; case Keyboard.KEY_TAB: return java.awt.event.KeyEvent.VK_TAB; // case Keyboard.KEY_UNDERLINE: case Keyboard.KEY_UP: return java.awt.event.KeyEvent.VK_UP; default: return 0; } } }