From 6aab3afd5839c2fa20e4c0207349cd79dcbc5578 Mon Sep 17 00:00:00 2001 From: codeanticode Date: Fri, 20 Jul 2012 17:07:29 +0000 Subject: [PATCH] add filter(PShader) function --- .../core/src/processing/core/PGraphics.java | 7 +- .../src/processing/opengl/PFontTexture.java | 4 +- android/core/src/processing/opengl/PGL.java | 46 +++- .../src/processing/opengl/PGraphics2D.java | 19 ++ .../src/processing/opengl/PGraphics3D.java | 19 ++ .../processing/opengl/PGraphicsOpenGL.java | 242 ++++++++++++------ .../core/src/processing/opengl/Texture.java | 167 ++++++++---- core/src/processing/core/PApplet.java | 6 + core/src/processing/core/PGraphics.java | 5 + .../src/processing/opengl/PFontTexture.java | 4 +- .../opengl/src/processing/opengl/PGL.java | 13 +- .../src/processing/opengl/PGraphics2D.java | 19 ++ .../src/processing/opengl/PGraphics3D.java | 19 ++ .../processing/opengl/PGraphicsOpenGL.java | 153 +++++++---- 14 files changed, 519 insertions(+), 204 deletions(-) diff --git a/android/core/src/processing/core/PGraphics.java b/android/core/src/processing/core/PGraphics.java index adb8f45ee..a133ee78e 100644 --- a/android/core/src/processing/core/PGraphics.java +++ b/android/core/src/processing/core/PGraphics.java @@ -1333,7 +1333,12 @@ public class PGraphics extends PImage implements PConstants { return null; } - + + public void filter(Object shader) { + showMissingWarning("filter"); + } + + ////////////////////////////////////////////////////////////// // CURVE/BEZIER VERTEX HANDLING diff --git a/android/core/src/processing/opengl/PFontTexture.java b/android/core/src/processing/opengl/PFontTexture.java index e8342b76a..247fdec32 100644 --- a/android/core/src/processing/opengl/PFontTexture.java +++ b/android/core/src/processing/opengl/PFontTexture.java @@ -370,9 +370,7 @@ class PFontTexture implements PConstants { void updateTex() { - textures[texIndex].bind(); - textures[texIndex].setTexels(pixels, 0, crop[0] - 1, crop[1] + crop[3] - 1, crop[2] + 2, -crop[3] + 2); - textures[texIndex].unbind(); + textures[texIndex].setNative(pixels, 0, crop[0] - 1, crop[1] + crop[3] - 1, crop[2] + 2, -crop[3] + 2); } } } \ No newline at end of file diff --git a/android/core/src/processing/opengl/PGL.java b/android/core/src/processing/opengl/PGL.java index c87f96ae5..ca8d33c9c 100644 --- a/android/core/src/processing/opengl/PGL.java +++ b/android/core/src/processing/opengl/PGL.java @@ -451,10 +451,10 @@ public class PGL { GLES20.glGenTextures(2, colorTex, 0); for (int i = 0; i < 2; i++) { GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, colorTex[i]); - GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); - GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST); - GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE); - GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); + 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, texWidth, texHeight, 0, PGL.GL_RGBA, PGL.GL_UNSIGNED_BYTE, null); initTexture(GLES20.GL_TEXTURE_2D, PGL.GL_RGBA, texWidth, texHeight); } @@ -505,14 +505,41 @@ public class PGL { } - public boolean usingPrimaryFBO() { + public boolean primaryIsFboBacked() { return colorFBO[0] != 0; } + public int getFboTexTarget() { + return GLES20.GL_TEXTURE_2D; + } + + + public int getFboTexName() { + return colorTex[0]; + } + + + public int getFboWidth() { + return texWidth; + } + + + public int getFboHeight() { + return texHeight; + } + + public void bindPrimaryColorFBO() { GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, colorFBO[0]); PGraphicsOpenGL.screenFramebuffer.glFbo = colorFBO[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); } @@ -741,11 +768,16 @@ public class PGL { } - public void glTexParameterf(int target, int param, int value) { - GLES20.glTexParameterf(target, param, value); + public void glTexParameteri(int target, int param, int value) { + GLES20.glTexParameteri(target, param, value); } + public void glGetTexParameteriv(int target, int param, int[] values, int offset) { + GLES20.glGetTexParameteriv(target, param, values, offset); + } + + public void glGenerateMipmap(int target) { GLES20.glGenerateMipmap(target); } diff --git a/android/core/src/processing/opengl/PGraphics2D.java b/android/core/src/processing/opengl/PGraphics2D.java index 5a1a048de..0cd86722d 100644 --- a/android/core/src/processing/opengl/PGraphics2D.java +++ b/android/core/src/processing/opengl/PGraphics2D.java @@ -146,6 +146,25 @@ public class PGraphics2D extends PGraphicsOpenGL { } + ////////////////////////////////////////////////////////////// + + // MATRIX MORE! + + + protected void begin2D() { + pushProjection(); + defaultPerspective(); + pushMatrix(); + defaultCamera(); + } + + + protected void end2D() { + popMatrix(); + popProjection(); + } + + ////////////////////////////////////////////////////////////// // SHAPE diff --git a/android/core/src/processing/opengl/PGraphics3D.java b/android/core/src/processing/opengl/PGraphics3D.java index 1744f9b21..e93fdc29b 100644 --- a/android/core/src/processing/opengl/PGraphics3D.java +++ b/android/core/src/processing/opengl/PGraphics3D.java @@ -67,6 +67,25 @@ public class PGraphics3D extends PGraphicsOpenGL { protected void defaultCamera() { camera(); } + + + ////////////////////////////////////////////////////////////// + + // MATRIX MORE! + + + protected void begin2D() { + pushProjection(); + ortho(-width/2, +width/2, -height/2, +height/2, -1, +1); + pushMatrix(); + camera(width/2, height/2); + } + + + protected void end2D() { + popMatrix(); + popProjection(); + } ////////////////////////////////////////////////////////////// diff --git a/android/core/src/processing/opengl/PGraphicsOpenGL.java b/android/core/src/processing/opengl/PGraphicsOpenGL.java index 5459d6966..83a0eaeff 100644 --- a/android/core/src/processing/opengl/PGraphicsOpenGL.java +++ b/android/core/src/processing/opengl/PGraphicsOpenGL.java @@ -362,13 +362,17 @@ public class PGraphicsOpenGL extends PGraphics { /** Used to create a temporary copy of the color buffer of this * rendering surface when applying a filter */ protected Texture textureCopy; - + protected PImage imageCopy; + /** IntBuffer wrapping the pixels array. */ protected IntBuffer pixelBuffer; /** Array to store pixels in OpenGL format. */ - protected int[] rgbaPixels; + protected int[] nativePixels; + /** IntBuffer wrapping the native pixels array. */ + protected IntBuffer nativePixelBuffer; + /** Flag to indicate if the user is manipulating the * pixels array through the set()/get() methods */ protected boolean setgetPixels; @@ -1796,7 +1800,7 @@ public class PGraphicsOpenGL extends PGraphics { pgl.glDrawBuffer(PGL.GL_BACK); } offscreenNotCurrent = false; - } else if (pgl.usingPrimaryFBO()) { + } else if (pgl.primaryIsFboBacked()) { if (op == OP_READ) { // We read from the color FBO, but the multisample FBO is currently bound, so: offscreenNotCurrent = true; @@ -3690,6 +3694,14 @@ public class PGraphicsOpenGL extends PGraphics { n30, n31, n32, n33); } + + protected void begin2D() { + } + + + protected void end2D() { + } + ////////////////////////////////////////////////////////////// @@ -4078,8 +4090,8 @@ public class PGraphicsOpenGL extends PGraphics { protected void defaultCamera() { camera(); } - + ////////////////////////////////////////////////////////////// // PROJECTION @@ -4921,14 +4933,7 @@ public class PGraphicsOpenGL extends PGraphics { allocatePixels(); if (!setgetPixels) { - readPixels(); - - if (primarySurface) { - // Copy pixels to the texture associated to the primary surface - // so both are in sync. - loadTextureImpl(POINT, false); - pixelsToTexture(); - } + readPixels(); } if (needEndDraw) { @@ -4970,19 +4975,20 @@ public class PGraphicsOpenGL extends PGraphics { int i0 = y * width + x; int len = w * h; - if (rgbaPixels == null || rgbaPixels.length < len) { - rgbaPixels = new int[len]; + if (nativePixels == null || nativePixels.length < len) { + nativePixels = new int[len]; + nativePixelBuffer = IntBuffer.wrap(nativePixels); } - PApplet.arrayCopy(pixels, i0, rgbaPixels, 0, len); - PGL.javaToNativeARGB(rgbaPixels, w, h); + PApplet.arrayCopy(pixels, i0, nativePixels, 0, len); + PGL.javaToNativeARGB(nativePixels, w, h); // Copying pixel buffer to screen texture... if (primarySurface) { loadTextureImpl(POINT, false); // (first making sure that the screen texture is valid). } pgl.copyToTexture(texture.glTarget, texture.glFormat, texture.glName, - x, y, w, h, IntBuffer.wrap(rgbaPixels)); + x, y, w, h, IntBuffer.wrap(nativePixels)); if (primarySurface || offscreenMultisample) { // ...and drawing the texture to screen... but only @@ -5004,7 +5010,7 @@ public class PGraphicsOpenGL extends PGraphics { public int get(int x, int y) { - loadPixels(); + loadPixels(); setgetPixels = true; return super.get(x, y); } @@ -5040,14 +5046,78 @@ public class PGraphicsOpenGL extends PGraphics { // Copies the contents of the color buffer into the pixels // array, and then the pixels array into the screen texture. public void loadTexture() { + boolean needEndDraw = false; + if (!drawing) { + beginDraw(); + needEndDraw = true; + } + + flush(); // To make sure the color buffer is updated. + if (primarySurface) { loadTextureImpl(Texture.POINT, false); - loadPixels(); - pixelsToTexture(); + + if (pgl.primaryIsFboBacked()) { + pgl.bindPrimaryColorFBO(); + // Copy the contents of the FBO used by the primary surface into texture, this copy + // operation is very fast because it is resolved in the GPU. + texture.set(pgl.getFboTexTarget(), pgl.getFboTexName(), pgl.getFboWidth(), pgl.getFboHeight(), width, height); + pgl.bindPrimaryMultiFBO(); + } else { + // Here we go the slow route: we first copy the contents of the color buffer into a pixels array (but we keep it + // in native format) and then copy this array into the texture. + if (nativePixels == null || nativePixels.length < width * height) { + nativePixels = new int[width * height]; + nativePixelBuffer = IntBuffer.wrap(nativePixels); + } + + beginPixelsOp(OP_READ); + pgl.glReadPixels(0, 0, width, height, PGL.GL_RGBA, PGL.GL_UNSIGNED_BYTE, nativePixelBuffer); + endPixelsOp(); + + texture.setNative(nativePixels, 0, 0, width, height); + } + } else { + // We need to copy the contents of the multisampled buffer to the + // color buffer, so the later is up-to-date with the last drawing. + if (offscreenMultisample) { + offscreenFramebufferMultisample.copy(offscreenFramebuffer); + } + + // Make the offscreen color buffer opaque so it doesn't show + // the background when drawn on the main surface. + if (offscreenMultisample) { + pushFramebuffer(); + setFramebuffer(offscreenFramebuffer); + } + pgl.glColorMask(false, false, false, true); + pgl.glClearColor(0, 0, 0, 1); + pgl.glClear(PGL.GL_COLOR_BUFFER_BIT); + pgl.glColorMask(true, true, true, true); + if (offscreenMultisample) { + popFramebuffer(); + } } + + if (needEndDraw) { + endDraw(); + } } + // Just marks the whole texture as updated + public void updateTexture() { + texture.updateTexels(); + } + + + // Marks the specified rectanglular subregion in the texture as + // updated. + public void updateTexture(int x, int y, int w, int h) { + texture.updateTexels(x, y, w, h); + } + + // Draws wherever it is in the screen texture right now to the display. public void updateDisplay() { flush(); @@ -5065,15 +5135,17 @@ public class PGraphicsOpenGL extends PGraphics { } if (texture == null || texture != img.getCache(pgPrimary)) { - Texture.Parameters params; - if (primarySurface) { - params = new Texture.Parameters(ARGB, Texture.POINT, false); - } else { - params = new Texture.Parameters(ARGB, Texture.BILINEAR, false); - } - - texture = addTexture(img, params); - + Texture tex = (Texture)img.getCache(pgPrimary); + Texture.Parameters params = tex != null ? tex.getParameters() : null; + if (tex == null || tex.contextIsOutdated() || !validSurfaceTex(tex)) { + if (primarySurface) { + params = new Texture.Parameters(ARGB, Texture.POINT, false); + } else { + params = new Texture.Parameters(ARGB, Texture.BILINEAR, false); + } + tex = addTexture(img, params); + } + texture = tex; texture.setFlippedY(true); this.setCache(pgPrimary, texture); this.setParams(pgPrimary, params); @@ -5111,18 +5183,18 @@ public class PGraphicsOpenGL extends PGraphics { texture.glWidth, texture.glHeight, x, y, x + w, y + h); } - - - protected void pixelsToTexture() { - texture.set(pixels); - } - - - protected void textureToPixels() { - texture.get(pixels); - } + protected boolean validSurfaceTex(Texture tex) { + Texture.Parameters params = tex.getParameters(); + if (primarySurface) { + return params.sampling == Texture.POINT && !params.mipmaps; + } else { + return params.sampling == Texture.BILINEAR && !params.mipmaps; + } + } + + ////////////////////////////////////////////////////////////// // IMAGE CONVERSION @@ -5206,48 +5278,58 @@ public class PGraphicsOpenGL extends PGraphics { return; } - PolyTexShader texShader = (PolyTexShader)shader; - -// if (shader instanceof FilterShader) { -// -// } - - if (primarySurface) { - - - } else { - if (textureCopy == null) { - Texture.Parameters params = new Texture.Parameters(ARGB, Texture.BILINEAR, false); - textureCopy = new Texture(parent, width, height, params); - textureCopy.setFlippedY(true); - } - - flush(); - - // Disable writing to the depth buffer, so that after applying the filter we can - // still use the depth information to properly add geometry to the scene. - - offscreenFramebuffer.setColorBuffer(textureCopy); - offscreenFramebuffer.clear(); - - // Disable depth test so the texture overwrites everything else. - - beginPGL(); - pgl.drawTexture(texture.glTarget, texture.glName, - texture.glWidth, texture.glHeight, - 0, 0, width, height); - endPGL(); + loadTexture(); - offscreenFramebuffer.setColorBuffer(texture); - offscreenFramebuffer.clear(); - - beginPGL(); - pgl.drawTextureCustom(textureCopy.glTarget, textureCopy.glName, - textureCopy.glWidth, textureCopy.glHeight, - 0, 0, width, height, texShader.glProgram); - endPGL(); - } - + if (textureCopy == null || textureCopy.width != width || textureCopy.height != height) { + Texture.Parameters params = new Texture.Parameters(ARGB, Texture.POINT, false); + textureCopy = new Texture(parent, width, height, params); + textureCopy.setFlippedY(true); + imageCopy = wrapTexture(textureCopy); + } + textureCopy.set(texture.glTarget, texture.glName, texture.glWidth, texture.glHeight, width, height); + + // Disable writing to the depth buffer, so that after applying the filter we can + // still use the depth information to keep adding geometry to the scene. + pgl.glDepthMask(false); + // Also disabling depth testing so the texture is drawn on top of everything that + // has been drawn before. + pgl.glDisable(PGL.GL_DEPTH_TEST); + + PolyTexShader prevTexShader = polyTexShader; + polyTexShader = (PolyTexShader) shader; + + boolean prevLights = lights; + lights = false; + int prevTextureMode = textureMode; + textureMode = NORMAL; + boolean prevStroke = stroke; + stroke = false; + + // Drawing a textured quad in 2D, covering the entire screen, + // with the filter shader applied to it: + begin2D(); + beginShape(QUADS); + texture(imageCopy); + vertex(0, 0, 0, 0); + vertex(width, 0, 1, 0); + vertex(width, height, 1, 1); + vertex(0, height, 0, 1); + endShape(); + end2D(); + + // Restoring previous configuration. + stroke = prevStroke; + lights = prevLights; + textureMode = prevTextureMode; + + polyTexShader = prevTexShader; + + if (!hints[DISABLE_DEPTH_TEST]) { + pgl.glEnable(PGL.GL_DEPTH_TEST); + } + if (!hints[DISABLE_DEPTH_MASK]) { + pgl.glDepthMask(true); + } } diff --git a/android/core/src/processing/opengl/Texture.java b/android/core/src/processing/opengl/Texture.java index b4751c1a6..0039f4e66 100644 --- a/android/core/src/processing/opengl/Texture.java +++ b/android/core/src/processing/opengl/Texture.java @@ -89,6 +89,7 @@ public class Texture implements PConstants { protected boolean usingMipmaps; protected float maxTexcoordU; protected float maxTexcoordV; + protected boolean bound; protected boolean flippedX; protected boolean flippedY; @@ -241,15 +242,25 @@ public class Texture implements PConstants { public void set(Texture tex) { - copyTexels(tex, 0, 0, tex.width, tex.height, true); + copyTexture(tex, 0, 0, tex.width, tex.height, true); } public void set(Texture tex, int x, int y, int w, int h) { - copyTexels(tex, x, y, w, h, true); + copyTexture(tex, x, y, w, h, true); } + public void set(int texTarget, int texName, int texWidth, int texHeight, int w, int h) { + copyTexture(texTarget, texName, texWidth, texHeight, 0, 0, w, h, true); + } + + + public void set(int texTarget, int texName, int texWidth, int texHeight, int target, int tex, int x, int y, int w, int h) { + copyTexture(texTarget, texName, texWidth, texHeight, x, y, w, h, true); + } + + public void set(int[] pixels) { set(pixels, 0, 0, width, height, ARGB); } @@ -276,15 +287,14 @@ public class Texture implements PConstants { return; } - pgl.enableTexturing(glTarget); - pgl.glBindTexture(glTarget, glName); + bind(); if (usingMipmaps) { if (PGraphicsOpenGL.autoMipmapGenSupported) { // Automatic mipmap generation. int[] rgbaPixels = new int[w * h]; convertToRGBA(pixels, rgbaPixels, format, w, h); - setTexels(rgbaPixels, x, y, w, h); + setNative(rgbaPixels, x, y, w, h); pgl.glGenerateMipmap(glTarget); rgbaPixels = null; } else { @@ -344,23 +354,53 @@ public class Texture implements PConstants { int[] rgbaPixels = new int[w * h]; convertToRGBA(pixels, rgbaPixels, format, w, h); - setTexels(rgbaPixels, x, y, w, h); + setNative(rgbaPixels, x, y, w, h); rgbaPixels = null; } } else { int[] rgbaPixels = new int[w * h]; convertToRGBA(pixels, rgbaPixels, format, w, h); - setTexels(rgbaPixels, x, y, w, h); + setNative(rgbaPixels, x, y, w, h); rgbaPixels = null; } - pgl.glBindTexture(glTarget, 0); - pgl.disableTexturing(glTarget); + unbind(); updateTexels(x, y, w, h); } + //////////////////////////////////////////////////////////// + + // Native set methods + + + public void setNative(int[] pix, int x, int y, int w, int h) { + setNative(pix, 0, x, y, w, h); + } + + + public void setNative(int[] pix, int level, int x, int y, int w, int h) { + bind(); + pgl.glTexSubImage2D(glTarget, level, x, y, w, h, PGL.GL_RGBA, PGL.GL_UNSIGNED_BYTE, IntBuffer.wrap(pix)); + unbind(); + updateTexels(x, y, w, h); + } + + + public void setNative(IntBuffer buffer, int x, int y, int w, int h) { + setNative(buffer, 0, x, y, w, h); + } + + + public void setNative(IntBuffer buffer, int level, int x, int y, int w, int h) { + bind(); + pgl.glTexSubImage2D(glTarget, level, x, y, w, h, PGL.GL_RGBA, PGL.GL_UNSIGNED_BYTE, buffer); + unbind(); + updateTexels(x, y, w, h); + } + + //////////////////////////////////////////////////////////// // Get methods @@ -425,14 +465,24 @@ public class Texture implements PConstants { public void put(Texture tex) { - copyTexels(tex, 0, 0, tex.width, tex.height, false); + copyTexture(tex, 0, 0, tex.width, tex.height, false); } public void put(Texture tex, int x, int y, int w, int h) { - copyTexels(tex, x, y, w, h, false); + copyTexture(tex, x, y, w, h, false); } + + public void put(int texTarget, int texName, int texWidth, int texHeight, int w, int h) { + copyTexture(texTarget, texName, texWidth, texHeight, 0, 0, w, h, false); + } + + + public void put(int texTarget, int texName, int texWidth, int texHeight, int target, int tex, int x, int y, int w, int h) { + copyTexture(texTarget, texName, texWidth, texHeight, x, y, w, h, false); + } + //////////////////////////////////////////////////////////// @@ -505,18 +555,29 @@ public class Texture implements PConstants { // Bind/unbind - public void bind() { - pgl.enableTexturing(glTarget); - pgl.glBindTexture(glTarget, glName); + public void bind() { + if (!bound) { + pgl.enableTexturing(glTarget); + pgl.glBindTexture(glTarget, glName); + bound = true; + } } public void unbind() { - pgl.enableTexturing(glTarget); - pgl.glBindTexture(glTarget, 0); + if (bound) { + pgl.enableTexturing(glTarget); + pgl.glBindTexture(glTarget, 0); + bound = false; + } } + public boolean bound() { + return bound; + } + + ////////////////////////////////////////////////////////////// // Modified flag @@ -636,7 +697,7 @@ public class Texture implements PConstants { try { data = bufferCache.remove(0); } catch (NoSuchElementException ex) { - PGraphics.showWarning("PTexture: don't have pixel data to copy to texture"); + PGraphics.showWarning("Don't have pixel data to copy to texture"); } if (data != null) { @@ -644,7 +705,7 @@ public class Texture implements PConstants { init(data.w, data.h); } bind(); - setTexels(data.rgbBuf, 0, 0, width, height); + setNative(data.rgbBuf, 0, 0, width, height); unbind(); data.dispose(); @@ -661,7 +722,7 @@ public class Texture implements PConstants { try { data = bufferCache.remove(0); } catch (NoSuchElementException ex) { - PGraphics.showWarning("PTexture: don't have pixel data to copy to texture"); + PGraphics.showWarning("Don't have pixel data to copy to texture"); } if (data != null) { @@ -669,7 +730,7 @@ public class Texture implements PConstants { init(data.w, data.h); } bind(); - setTexels(data.rgbBuf, 0, 0, width, height); + setNative(data.rgbBuf, 0, 0, width, height); unbind(); data.rgbBuf.get(pixels); @@ -688,7 +749,7 @@ public class Texture implements PConstants { try { disposeBufferMethod = bufferSource.getClass().getMethod("disposeBuffer", new Class[] { Object.class }); } catch (Exception e) { - throw new RuntimeException("PTexture: provided source object doesn't have a disposeBuffer method."); + throw new RuntimeException("Provided source object doesn't have a disposeBuffer method."); } } @@ -974,10 +1035,10 @@ public class Texture implements PConstants { glName = pg.createTextureObject(context.code()); pgl.glBindTexture(glTarget, glName); - pgl.glTexParameterf(glTarget, PGL.GL_TEXTURE_MIN_FILTER, glMinFilter); - pgl.glTexParameterf(glTarget, PGL.GL_TEXTURE_MAG_FILTER, glMagFilter); - pgl.glTexParameterf(glTarget, PGL.GL_TEXTURE_WRAP_S, glWrapS); - pgl.glTexParameterf(glTarget, PGL.GL_TEXTURE_WRAP_T, glWrapT); + pgl.glTexParameteri(glTarget, PGL.GL_TEXTURE_MIN_FILTER, glMinFilter); + pgl.glTexParameteri(glTarget, PGL.GL_TEXTURE_MAG_FILTER, glMagFilter); + pgl.glTexParameteri(glTarget, PGL.GL_TEXTURE_WRAP_S, glWrapS); + pgl.glTexParameteri(glTarget, PGL.GL_TEXTURE_WRAP_T, glWrapT); // First, we use glTexImage2D to set the full size of the texture (glW/glH might be diff // from w/h in the case that the GPU doesn't support NPOT textures) @@ -988,6 +1049,7 @@ public class Texture implements PConstants { pgl.glBindTexture(glTarget, 0); pgl.disableTexturing(glTarget); + bound = false; } @@ -1024,9 +1086,9 @@ public class Texture implements PConstants { // Copies source texture tex into this. - protected void copyTexels(Texture tex, int x, int y, int w, int h, boolean scale) { + protected void copyTexture(Texture tex, int x, int y, int w, int h, boolean scale) { if (tex == null) { - throw new RuntimeException("PTexture: source texture is null"); + throw new RuntimeException("Source texture is null"); } if (tempFbo == null) { @@ -1044,39 +1106,48 @@ public class Texture implements PConstants { // Rendering tex into "this", and scaling the source rectangle // to cover the entire destination region. pgl.drawTexture(tex.glTarget, tex.glName, tex.glWidth, tex.glHeight, - x, y, w, h, 0, 0, width, height); + x, y, w, h, 0, 0, width, height); } else { // Rendering tex into "this" but without scaling so the contents // of the source texture fall in the corresponding texels of the // destination. pgl.drawTexture(tex.glTarget, tex.glName, tex.glWidth, tex.glHeight, - x, y, w, h, x, y, w, h); + x, y, w, h, x, y, w, h); } pg.popFramebuffer(); updateTexels(x, y, w, h); } - protected void setTexels(int[] pix, int x, int y, int w, int h) { - setTexels(pix, 0, x, y, w, h); - } - - - protected void setTexels(int[] pix, int level, int x, int y, int w, int h) { - pgl.glTexSubImage2D(glTarget, level, x, y, w, h, PGL.GL_RGBA, PGL.GL_UNSIGNED_BYTE, IntBuffer.wrap(pix)); - updateTexels(x, y, w, h); - } - - - protected void setTexels(IntBuffer buffer, int x, int y, int w, int h) { - setTexels(buffer, 0, x, y, w, h); - } - - - protected void setTexels(IntBuffer buffer, int level, int x, int y, int w, int h) { - pgl.glTexSubImage2D(glTarget, level, x, y, w, h, PGL.GL_RGBA, PGL.GL_UNSIGNED_BYTE, buffer); - updateTexels(x, y, w, h); + // Copies source texture tex into this. + protected void copyTexture(int texTarget, int texName, int texWidth, int texHeight, int x, int y, int w, int h, boolean scale) { + if (tempFbo == null) { + tempFbo = new FrameBuffer(parent, glWidth, glHeight); + } + + // This texture is the color (destination) buffer of the FBO. + tempFbo.setColorBuffer(this); + tempFbo.disableDepthTest(); + + // FBO copy: + pg.pushFramebuffer(); + pg.setFramebuffer(tempFbo); + if (scale) { + // Rendering tex into "this", and scaling the source rectangle + // to cover the entire destination region. + pgl.drawTexture(texTarget, texName, texWidth, texHeight, + x, y, w, h, 0, 0, width, height); + + } else { + // Rendering tex into "this" but without scaling so the contents + // of the source texture fall in the corresponding texels of the + // destination. + pgl.drawTexture(texTarget, texName, texWidth, texHeight, + x, y, w, h, x, y, w, h); + } + pg.popFramebuffer(); + updateTexels(x, y, w, h); } diff --git a/core/src/processing/core/PApplet.java b/core/src/processing/core/PApplet.java index 441d4dd69..e01f54667 100644 --- a/core/src/processing/core/PApplet.java +++ b/core/src/processing/core/PApplet.java @@ -10165,6 +10165,12 @@ public class PApplet extends Applet } + public void filter(Object shader) { + if (recorder != null) recorder.filter(shader); + g.filter(shader); + } + + public void bezierVertex(float x2, float y2, float x3, float y3, float x4, float y4) { diff --git a/core/src/processing/core/PGraphics.java b/core/src/processing/core/PGraphics.java index e6c930250..16de33615 100644 --- a/core/src/processing/core/PGraphics.java +++ b/core/src/processing/core/PGraphics.java @@ -1536,6 +1536,11 @@ public class PGraphics extends PImage implements PConstants { } + public void filter(Object shader) { + showMissingWarning("filter"); + } + + ////////////////////////////////////////////////////////////// // CURVE/BEZIER VERTEX HANDLING diff --git a/java/libraries/opengl/src/processing/opengl/PFontTexture.java b/java/libraries/opengl/src/processing/opengl/PFontTexture.java index f9e350889..247fdec32 100644 --- a/java/libraries/opengl/src/processing/opengl/PFontTexture.java +++ b/java/libraries/opengl/src/processing/opengl/PFontTexture.java @@ -370,9 +370,7 @@ class PFontTexture implements PConstants { void updateTex() { - textures[texIndex].bind(); - textures[texIndex].setNative(pixels, 0, crop[0] - 1, crop[1] + crop[3] - 1, crop[2] + 2, -crop[3] + 2); - textures[texIndex].unbind(); + textures[texIndex].setNative(pixels, 0, crop[0] - 1, crop[1] + crop[3] - 1, crop[2] + 2, -crop[3] + 2); } } } \ No newline at end of file diff --git a/java/libraries/opengl/src/processing/opengl/PGL.java b/java/libraries/opengl/src/processing/opengl/PGL.java index 7d88248e5..3bc95fee8 100644 --- a/java/libraries/opengl/src/processing/opengl/PGL.java +++ b/java/libraries/opengl/src/processing/opengl/PGL.java @@ -800,6 +800,13 @@ public class PGL { gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, glColorFboID[0]); PGraphicsOpenGL.screenFramebuffer.glFbo = glColorFboID[0]; + + // Make the color buffer opaque so it doesn't show + // the background when drawn on top of another surface. + gl.glColorMask(false, false, false, true); + gl.glClearColor(0, 0, 0, 1); + gl.glClear(GL.GL_COLOR_BUFFER_BIT); + gl.glColorMask(true, true, true, true); } @@ -1823,12 +1830,6 @@ public class PGL { glDepthMask(depthMask[0]); } } - - - public void drawTextureCustom(int target, int id, int width, int height, - int X0, int Y0, int X1, int Y1, int program) { - // ... - } public void drawTextureRect(int id, int width, int height, diff --git a/java/libraries/opengl/src/processing/opengl/PGraphics2D.java b/java/libraries/opengl/src/processing/opengl/PGraphics2D.java index 5a1a048de..0cd86722d 100644 --- a/java/libraries/opengl/src/processing/opengl/PGraphics2D.java +++ b/java/libraries/opengl/src/processing/opengl/PGraphics2D.java @@ -146,6 +146,25 @@ public class PGraphics2D extends PGraphicsOpenGL { } + ////////////////////////////////////////////////////////////// + + // MATRIX MORE! + + + protected void begin2D() { + pushProjection(); + defaultPerspective(); + pushMatrix(); + defaultCamera(); + } + + + protected void end2D() { + popMatrix(); + popProjection(); + } + + ////////////////////////////////////////////////////////////// // SHAPE diff --git a/java/libraries/opengl/src/processing/opengl/PGraphics3D.java b/java/libraries/opengl/src/processing/opengl/PGraphics3D.java index 1744f9b21..e93fdc29b 100644 --- a/java/libraries/opengl/src/processing/opengl/PGraphics3D.java +++ b/java/libraries/opengl/src/processing/opengl/PGraphics3D.java @@ -67,6 +67,25 @@ public class PGraphics3D extends PGraphicsOpenGL { protected void defaultCamera() { camera(); } + + + ////////////////////////////////////////////////////////////// + + // MATRIX MORE! + + + protected void begin2D() { + pushProjection(); + ortho(-width/2, +width/2, -height/2, +height/2, -1, +1); + pushMatrix(); + camera(width/2, height/2); + } + + + protected void end2D() { + popMatrix(); + popProjection(); + } ////////////////////////////////////////////////////////////// diff --git a/java/libraries/opengl/src/processing/opengl/PGraphicsOpenGL.java b/java/libraries/opengl/src/processing/opengl/PGraphicsOpenGL.java index 304c04b97..83a0eaeff 100644 --- a/java/libraries/opengl/src/processing/opengl/PGraphicsOpenGL.java +++ b/java/libraries/opengl/src/processing/opengl/PGraphicsOpenGL.java @@ -362,7 +362,8 @@ public class PGraphicsOpenGL extends PGraphics { /** Used to create a temporary copy of the color buffer of this * rendering surface when applying a filter */ protected Texture textureCopy; - + protected PImage imageCopy; + /** IntBuffer wrapping the pixels array. */ protected IntBuffer pixelBuffer; @@ -3693,6 +3694,14 @@ public class PGraphicsOpenGL extends PGraphics { n30, n31, n32, n33); } + + protected void begin2D() { + } + + + protected void end2D() { + } + ////////////////////////////////////////////////////////////// @@ -4081,8 +4090,8 @@ public class PGraphicsOpenGL extends PGraphics { protected void defaultCamera() { camera(); } - + ////////////////////////////////////////////////////////////// // PROJECTION @@ -5037,21 +5046,23 @@ public class PGraphicsOpenGL extends PGraphics { // Copies the contents of the color buffer into the pixels // array, and then the pixels array into the screen texture. public void loadTexture() { + boolean needEndDraw = false; + if (!drawing) { + beginDraw(); + needEndDraw = true; + } + + flush(); // To make sure the color buffer is updated. + if (primarySurface) { - boolean needEndDraw = false; - if (!drawing) { - beginDraw(); - needEndDraw = true; - } - loadTextureImpl(Texture.POINT, false); - flush(); // To make sure the color buffer is updated. - - if (pgl.primaryIsFboBacked()) { + if (pgl.primaryIsFboBacked()) { + pgl.bindPrimaryColorFBO(); // Copy the contents of the FBO used by the primary surface into texture, this copy - // operation is very fast because it is resolved in the GPU. - texture.set(pgl.getFboTexTarget(), pgl.getFboTexName(), pgl.getFboWidth(), pgl.getFboHeight(), width, height); + // operation is very fast because it is resolved in the GPU. + texture.set(pgl.getFboTexTarget(), pgl.getFboTexName(), pgl.getFboWidth(), pgl.getFboHeight(), width, height); + pgl.bindPrimaryMultiFBO(); } else { // Here we go the slow route: we first copy the contents of the color buffer into a pixels array (but we keep it // in native format) and then copy this array into the texture. @@ -5066,11 +5077,31 @@ public class PGraphicsOpenGL extends PGraphics { texture.setNative(nativePixels, 0, 0, width, height); } + } else { + // We need to copy the contents of the multisampled buffer to the + // color buffer, so the later is up-to-date with the last drawing. + if (offscreenMultisample) { + offscreenFramebufferMultisample.copy(offscreenFramebuffer); + } - if (needEndDraw) { - endDraw(); + // Make the offscreen color buffer opaque so it doesn't show + // the background when drawn on the main surface. + if (offscreenMultisample) { + pushFramebuffer(); + setFramebuffer(offscreenFramebuffer); + } + pgl.glColorMask(false, false, false, true); + pgl.glClearColor(0, 0, 0, 1); + pgl.glClear(PGL.GL_COLOR_BUFFER_BIT); + pgl.glColorMask(true, true, true, true); + if (offscreenMultisample) { + popFramebuffer(); } } + + if (needEndDraw) { + endDraw(); + } } @@ -5247,48 +5278,58 @@ public class PGraphicsOpenGL extends PGraphics { return; } - PolyTexShader texShader = (PolyTexShader)shader; - -// if (shader instanceof FilterShader) { -// -// } - - if (primarySurface) { - - - } else { - if (textureCopy == null) { - Texture.Parameters params = new Texture.Parameters(ARGB, Texture.BILINEAR, false); - textureCopy = new Texture(parent, width, height, params); - textureCopy.setFlippedY(true); - } - - flush(); - - // Disable writing to the depth buffer, so that after applying the filter we can - // still use the depth information to properly add geometry to the scene. - - offscreenFramebuffer.setColorBuffer(textureCopy); - offscreenFramebuffer.clear(); - - // Disable depth test so the texture overwrites everything else. - - beginPGL(); - pgl.drawTexture(texture.glTarget, texture.glName, - texture.glWidth, texture.glHeight, - 0, 0, width, height); - endPGL(); + loadTexture(); - offscreenFramebuffer.setColorBuffer(texture); - offscreenFramebuffer.clear(); - - beginPGL(); - pgl.drawTextureCustom(textureCopy.glTarget, textureCopy.glName, - textureCopy.glWidth, textureCopy.glHeight, - 0, 0, width, height, texShader.glProgram); - endPGL(); - } - + if (textureCopy == null || textureCopy.width != width || textureCopy.height != height) { + Texture.Parameters params = new Texture.Parameters(ARGB, Texture.POINT, false); + textureCopy = new Texture(parent, width, height, params); + textureCopy.setFlippedY(true); + imageCopy = wrapTexture(textureCopy); + } + textureCopy.set(texture.glTarget, texture.glName, texture.glWidth, texture.glHeight, width, height); + + // Disable writing to the depth buffer, so that after applying the filter we can + // still use the depth information to keep adding geometry to the scene. + pgl.glDepthMask(false); + // Also disabling depth testing so the texture is drawn on top of everything that + // has been drawn before. + pgl.glDisable(PGL.GL_DEPTH_TEST); + + PolyTexShader prevTexShader = polyTexShader; + polyTexShader = (PolyTexShader) shader; + + boolean prevLights = lights; + lights = false; + int prevTextureMode = textureMode; + textureMode = NORMAL; + boolean prevStroke = stroke; + stroke = false; + + // Drawing a textured quad in 2D, covering the entire screen, + // with the filter shader applied to it: + begin2D(); + beginShape(QUADS); + texture(imageCopy); + vertex(0, 0, 0, 0); + vertex(width, 0, 1, 0); + vertex(width, height, 1, 1); + vertex(0, height, 0, 1); + endShape(); + end2D(); + + // Restoring previous configuration. + stroke = prevStroke; + lights = prevLights; + textureMode = prevTextureMode; + + polyTexShader = prevTexShader; + + if (!hints[DISABLE_DEPTH_TEST]) { + pgl.glEnable(PGL.GL_DEPTH_TEST); + } + if (!hints[DISABLE_DEPTH_MASK]) { + pgl.glDepthMask(true); + } }