diff --git a/core/src/processing/core/PApplet.java b/core/src/processing/core/PApplet.java index 945b91b92..3d613c66a 100755 --- a/core/src/processing/core/PApplet.java +++ b/core/src/processing/core/PApplet.java @@ -6379,7 +6379,7 @@ public class PApplet extends Applet /** - * @webref input:files + * @webref output:files * @param table the Table object to save to a file * @param filename the filename to which the Table should be saved * @see Table @@ -14196,7 +14196,9 @@ public class PApplet extends Applet * ( end auto-generated ) * * @webref color:setting - * @see PGraphics#stroke(float, float, float, float) + * @see PGraphics#stroke(int, float) + * @see PGraphics#fill(float, float, float, float) + * @see PGraphics#noFill() */ public void noStroke() { if (recorder != null) recorder.noStroke(); @@ -14228,7 +14230,9 @@ public class PApplet extends Applet * * @param rgb color value in hexadecimal notation * @see PGraphics#noStroke() + * @see PGraphics#strokeWeight(float) * @see PGraphics#fill(int, float) + * @see PGraphics#noFill() * @see PGraphics#tint(int, float) * @see PGraphics#background(float, float, float, float) * @see PGraphics#colorMode(int, float, float, float, float) @@ -14392,6 +14396,8 @@ public class PApplet extends Applet * @webref color:setting * @usage web_application * @see PGraphics#fill(float, float, float, float) + * @see PGraphics#stroke(int, float) + * @see PGraphics#noStroke() */ public void noFill() { if (recorder != null) recorder.noFill(); @@ -14429,6 +14435,7 @@ public class PApplet extends Applet * @param rgb color variable or hex value * @see PGraphics#noFill() * @see PGraphics#stroke(int, float) + * @see PGraphics#noStroke() * @see PGraphics#tint(int, float) * @see PGraphics#background(float, float, float, float) * @see PGraphics#colorMode(int, float, float, float, float) diff --git a/core/src/processing/core/PGraphics.java b/core/src/processing/core/PGraphics.java index 8ec72c89d..2213965ba 100644 --- a/core/src/processing/core/PGraphics.java +++ b/core/src/processing/core/PGraphics.java @@ -5817,6 +5817,8 @@ public class PGraphics extends PImage implements PConstants { ellipseMode(s.ellipseMode); shapeMode(s.shapeMode); + blendMode(s.blendMode); + if (s.tint) { tint(s.tintColor); } else { @@ -5895,6 +5897,8 @@ public class PGraphics extends PImage implements PConstants { s.ellipseMode = ellipseMode; s.shapeMode = shapeMode; + s.blendMode = blendMode; + s.colorMode = colorMode; s.colorModeX = colorModeX; s.colorModeY = colorModeY; @@ -6031,7 +6035,9 @@ public class PGraphics extends PImage implements PConstants { * ( end auto-generated ) * * @webref color:setting - * @see PGraphics#stroke(float, float, float, float) + * @see PGraphics#stroke(int, float) + * @see PGraphics#fill(float, float, float, float) + * @see PGraphics#noFill() */ public void noStroke() { stroke = false; @@ -6062,7 +6068,9 @@ public class PGraphics extends PImage implements PConstants { * * @param rgb color value in hexadecimal notation * @see PGraphics#noStroke() + * @see PGraphics#strokeWeight(float) * @see PGraphics#fill(int, float) + * @see PGraphics#noFill() * @see PGraphics#tint(int, float) * @see PGraphics#background(float, float, float, float) * @see PGraphics#colorMode(int, float, float, float, float) @@ -6266,6 +6274,8 @@ public class PGraphics extends PImage implements PConstants { * @webref color:setting * @usage web_application * @see PGraphics#fill(float, float, float, float) + * @see PGraphics#stroke(int, float) + * @see PGraphics#noStroke() */ public void noFill() { fill = false; @@ -6302,6 +6312,7 @@ public class PGraphics extends PImage implements PConstants { * @param rgb color variable or hex value * @see PGraphics#noFill() * @see PGraphics#stroke(int, float) + * @see PGraphics#noStroke() * @see PGraphics#tint(int, float) * @see PGraphics#background(float, float, float, float) * @see PGraphics#colorMode(int, float, float, float, float) diff --git a/core/src/processing/core/PImage.java b/core/src/processing/core/PImage.java index e3994c91e..430d67228 100644 --- a/core/src/processing/core/PImage.java +++ b/core/src/processing/core/PImage.java @@ -477,7 +477,7 @@ public class PImage implements PConstants, Cloneable { if (pixels == null || pixels.length != width*height) { pixels = new int[width*height]; } - isLoaded(); + setLoaded(); } diff --git a/core/src/processing/core/PStyle.java b/core/src/processing/core/PStyle.java index ccea03a42..7b27695cf 100644 --- a/core/src/processing/core/PStyle.java +++ b/core/src/processing/core/PStyle.java @@ -30,6 +30,8 @@ public class PStyle implements PConstants { public int ellipseMode; public int shapeMode; + public int blendMode; + public int colorMode; public float colorModeX; public float colorModeY; diff --git a/core/src/processing/opengl/FontTexture.java b/core/src/processing/opengl/FontTexture.java index 2a9fbadeb..8c1c66f5d 100644 --- a/core/src/processing/opengl/FontTexture.java +++ b/core/src/processing/opengl/FontTexture.java @@ -63,8 +63,9 @@ class FontTexture implements PConstants { protected TextureInfo[] glyphTexinfos; protected HashMap texinfoMap; + public FontTexture(PGraphicsOpenGL pg, PFont font, boolean is3D) { - pgl = PGraphicsOpenGL.pgl; + pgl = pg.pgl; this.is3D = is3D; initTexture(pg, font); @@ -130,15 +131,15 @@ class FontTexture implements PConstants { if (is3D) { // Bilinear sampling ensures that the texture doesn't look pixelated // either when it is magnified or minified... - tex = new Texture(w, h, new Texture.Parameters(ARGB, Texture.BILINEAR, - false)); + tex = new Texture(pg, w, h, + new Texture.Parameters(ARGB, Texture.BILINEAR, false)); } else { // ...however, the effect of bilinear sampling is to add some blurriness // to the text in its original size. In 2D, we assume that text will be // shown at its original size, so linear sampling is chosen instead (which // only affects minimized text). - tex = new Texture(w, h, new Texture.Parameters(ARGB, Texture.LINEAR, - false)); + tex = new Texture(pg, w, h, + new Texture.Parameters(ARGB, Texture.LINEAR, false)); } if (textures == null) { diff --git a/core/src/processing/opengl/FrameBuffer.java b/core/src/processing/opengl/FrameBuffer.java index 8821861c9..e70ac1173 100644 --- a/core/src/processing/opengl/FrameBuffer.java +++ b/core/src/processing/opengl/FrameBuffer.java @@ -40,6 +40,7 @@ import java.nio.IntBuffer; */ public class FrameBuffer implements PConstants { + protected PGraphicsOpenGL pg; protected PGL pgl; protected int context; // The context that created this framebuffer. @@ -67,16 +68,17 @@ public class FrameBuffer implements PConstants { protected IntBuffer pixelBuffer; - FrameBuffer() { - pgl = PGraphicsOpenGL.pgl; + FrameBuffer(PGraphicsOpenGL pg) { + this.pg = pg; + pgl = pg.pgl; context = pgl.createEmptyContext(); } - FrameBuffer(int w, int h, int samples, int colorBuffers, - int depthBits, int stencilBits, boolean packedDepthStencil, - boolean screen) { - this(); + FrameBuffer(PGraphicsOpenGL pg, int w, int h, int samples, int colorBuffers, + int depthBits, int stencilBits, boolean packedDepthStencil, + boolean screen) { + this(pg); glFbo = 0; glDepth = 0; @@ -136,13 +138,13 @@ public class FrameBuffer implements PConstants { } - FrameBuffer(int w, int h) { - this(w, h, 1, 1, 0, 0, false, false); + FrameBuffer(PGraphicsOpenGL pg, int w, int h) { + this(pg, w, h, 1, 1, 0, 0, false, false); } - FrameBuffer(int w, int h, boolean screen) { - this(w, h, 1, 1, 0, 0, false, screen); + FrameBuffer(PGraphicsOpenGL pg, int w, int h, boolean screen) { + this(pg, w, h, 1, 1, 0, 0, false, screen); } @@ -172,15 +174,15 @@ public class FrameBuffer implements PConstants { } public void clear() { - PGraphicsOpenGL.pushFramebuffer(); - PGraphicsOpenGL.setFramebuffer(this); + pg.pushFramebuffer(); + pg.setFramebuffer(this); pgl.clearDepth(1); pgl.clearStencil(0); pgl.clearColor(0, 0, 0, 0); pgl.clear(PGL.DEPTH_BUFFER_BIT | PGL.STENCIL_BUFFER_BIT | PGL.COLOR_BUFFER_BIT); - PGraphicsOpenGL.popFramebuffer(); + pg.popFramebuffer(); } public void copy(FrameBuffer dest, FrameBuffer current) { @@ -201,7 +203,7 @@ public class FrameBuffer implements PConstants { noDepth = true; } - public void finish(PGraphicsOpenGL pg) { + public void finish() { if (noDepth) { // No need to clear depth buffer because depth testing was disabled. if (pg.getHint(ENABLE_DEPTH_TEST)) { @@ -271,8 +273,8 @@ public class FrameBuffer implements PConstants { colorBufferTex[i] = textures[i]; } - PGraphicsOpenGL.pushFramebuffer(); - PGraphicsOpenGL.setFramebuffer(this); + pg.pushFramebuffer(); + pg.setFramebuffer(this); // Making sure nothing is attached. for (int i = 0; i < numColorBuffers; i++) { @@ -288,7 +290,7 @@ public class FrameBuffer implements PConstants { pgl.validateFramebuffer(); - PGraphicsOpenGL.popFramebuffer(); + pg.popFramebuffer(); } @@ -300,8 +302,8 @@ public class FrameBuffer implements PConstants { colorBufferTex[i1] = tmp; } - PGraphicsOpenGL.pushFramebuffer(); - PGraphicsOpenGL.setFramebuffer(this); + pg.pushFramebuffer(); + pg.setFramebuffer(this); for (int i = 0; i < numColorBuffers; i++) { pgl.framebufferTexture2D(PGL.FRAMEBUFFER, PGL.COLOR_ATTACHMENT0 + i, colorBufferTex[i].glTarget, @@ -309,7 +311,7 @@ public class FrameBuffer implements PConstants { } pgl.validateFramebuffer(); - PGraphicsOpenGL.popFramebuffer(); + pg.popFramebuffer(); } @@ -345,7 +347,7 @@ public class FrameBuffer implements PConstants { glFbo = 0; } else { //create the FBO object... - glFbo = PGraphicsOpenGL.createFrameBufferObject(context); + glFbo = PGraphicsOpenGL.createFrameBufferObject(context, pgl); // ... and then create the rest of the stuff. if (multisample) { @@ -420,17 +422,17 @@ public class FrameBuffer implements PConstants { protected void createColorBufferMultisample() { if (screenFb) return; - PGraphicsOpenGL.pushFramebuffer(); - PGraphicsOpenGL.setFramebuffer(this); + pg.pushFramebuffer(); + pg.setFramebuffer(this); - glMultisample = PGraphicsOpenGL.createRenderBufferObject(context); + glMultisample = PGraphicsOpenGL.createRenderBufferObject(context, pgl); pgl.bindRenderbuffer(PGL.RENDERBUFFER, glMultisample); pgl.renderbufferStorageMultisample(PGL.RENDERBUFFER, nsamples, PGL.RGBA8, width, height); pgl.framebufferRenderbuffer(PGL.FRAMEBUFFER, PGL.COLOR_ATTACHMENT0, PGL.RENDERBUFFER, glMultisample); - PGraphicsOpenGL.popFramebuffer(); + pg.popFramebuffer(); } @@ -441,10 +443,10 @@ public class FrameBuffer implements PConstants { throw new RuntimeException("PFramebuffer: size undefined."); } - PGraphicsOpenGL.pushFramebuffer(); - PGraphicsOpenGL.setFramebuffer(this); + pg.pushFramebuffer(); + pg.setFramebuffer(this); - glDepthStencil = PGraphicsOpenGL.createRenderBufferObject(context); + glDepthStencil = PGraphicsOpenGL.createRenderBufferObject(context, pgl); pgl.bindRenderbuffer(PGL.RENDERBUFFER, glDepthStencil); if (multisample) { @@ -460,7 +462,7 @@ public class FrameBuffer implements PConstants { pgl.framebufferRenderbuffer(PGL.FRAMEBUFFER, PGL.STENCIL_ATTACHMENT, PGL.RENDERBUFFER, glDepthStencil); - PGraphicsOpenGL.popFramebuffer(); + pg.popFramebuffer(); } @@ -471,10 +473,10 @@ public class FrameBuffer implements PConstants { throw new RuntimeException("PFramebuffer: size undefined."); } - PGraphicsOpenGL.pushFramebuffer(); - PGraphicsOpenGL.setFramebuffer(this); + pg.pushFramebuffer(); + pg.setFramebuffer(this); - glDepth = PGraphicsOpenGL.createRenderBufferObject(context); + glDepth = PGraphicsOpenGL.createRenderBufferObject(context, pgl); pgl.bindRenderbuffer(PGL.RENDERBUFFER, glDepth); int glConst = PGL.DEPTH_COMPONENT16; @@ -496,7 +498,7 @@ public class FrameBuffer implements PConstants { pgl.framebufferRenderbuffer(PGL.FRAMEBUFFER, PGL.DEPTH_ATTACHMENT, PGL.RENDERBUFFER, glDepth); - PGraphicsOpenGL.popFramebuffer(); + pg.popFramebuffer(); } @@ -507,10 +509,10 @@ public class FrameBuffer implements PConstants { throw new RuntimeException("PFramebuffer: size undefined."); } - PGraphicsOpenGL.pushFramebuffer(); - PGraphicsOpenGL.setFramebuffer(this); + pg.pushFramebuffer(); + pg.setFramebuffer(this); - glStencil = PGraphicsOpenGL.createRenderBufferObject(context); + glStencil = PGraphicsOpenGL.createRenderBufferObject(context, pgl); pgl.bindRenderbuffer(PGL.RENDERBUFFER, glStencil); int glConst = PGL.STENCIL_INDEX1; @@ -531,7 +533,7 @@ public class FrameBuffer implements PConstants { pgl.framebufferRenderbuffer(PGL.FRAMEBUFFER, PGL.STENCIL_ATTACHMENT, PGL.RENDERBUFFER, glStencil); - PGraphicsOpenGL.popFramebuffer(); + pg.popFramebuffer(); } diff --git a/core/src/processing/opengl/PGL.java b/core/src/processing/opengl/PGL.java index f1e43fcd1..96c4fcb7d 100644 --- a/core/src/processing/opengl/PGL.java +++ b/core/src/processing/opengl/PGL.java @@ -52,10 +52,13 @@ public abstract class PGL { protected PGraphicsOpenGL pg; /** OpenGL thread */ - protected static Thread glThread; + protected Thread glThread; /** ID of the GL context associated to the surface **/ - protected static int glContext; + protected int glContext; + + /** true if this is the GL interface for a primary surface PGraphics */ + public boolean primaryPGL; // ........................................................ @@ -131,21 +134,21 @@ public abstract class PGL { // FBO layer - protected static boolean fboLayerRequested = false; - 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 boolean fboLayerRequested = false; + protected boolean fboLayerCreated = false; + protected boolean fboLayerInUse = false; + protected boolean firstFrame = true; + protected int reqNumSamples; + protected int numSamples; + protected IntBuffer glColorFbo; + protected IntBuffer glMultiFbo; + protected IntBuffer glColorBuf; + protected IntBuffer glColorTex; + protected IntBuffer glDepthStencil; + protected IntBuffer glDepth; + protected IntBuffer glStencil; + protected int fboWidth, fboHeight; + protected int backTex, frontTex; /** Flags used to handle the creation of a separate front texture */ protected boolean usingFrontTex = false; @@ -155,33 +158,34 @@ public abstract class PGL { // 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 texGeoVBO; - protected static int tex2DVertLoc; - protected static int tex2DTCoordLoc; - protected static int tex2DSamplerLoc; + protected boolean loadedTex2DShader = false; + protected int tex2DShaderProgram; + protected int tex2DVertShader; + protected int tex2DFragShader; + protected int tex2DShaderContext; + protected int tex2DVertLoc; + protected int tex2DTCoordLoc; + protected int tex2DSamplerLoc; - 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 int texRectSamplerLoc; + protected boolean loadedTexRectShader = false; + protected int texRectShaderProgram; + protected int texRectVertShader; + protected int texRectFragShader; + protected int texRectShaderContext; + protected int texRectVertLoc; + protected int texRectTCoordLoc; + protected int texRectSamplerLoc; - protected static float[] texCoords = { + protected int texGeoVBO; + + protected float[] texCoords = { // X, Y, U, V -1.0f, -1.0f, 0.0f, 0.0f, +1.0f, -1.0f, 1.0f, 0.0f, -1.0f, +1.0f, 0.0f, 1.0f, +1.0f, +1.0f, 1.0f, 1.0f }; - protected static FloatBuffer texData; + protected FloatBuffer texData; protected static final String SHADER_PREPROCESSOR_DIRECTIVE = "#ifdef GL_ES\n" + @@ -218,12 +222,12 @@ public abstract class PGL { }; /** Which texturing targets are enabled */ - protected static boolean[] texturingTargets = { false, false }; + protected boolean[] texturingTargets = { false, false }; /** Used to keep track of which textures are bound to each target */ - protected static int maxTexUnits; - protected static int activeTexUnit = 0; - protected static int[][] boundTextures; + protected int maxTexUnits; + protected int activeTexUnit = 0; + protected int[][] boundTextures; // ........................................................ @@ -270,6 +274,13 @@ public abstract class PGL { protected static final String TEXUNIT_ERROR = "Number of texture units not supported by this hardware (or driver)" + WIKI; + protected static final String NONPRIMARY_ERROR = + "The renderer is trying to call a PGL function that can only be called on a primary PGL. " + + "This is most likely due to a bug in the renderer's code, please report it with an " + + "issue on Processing's github page https://github.com/processing/processing/issues?state=open " + + "if using any of the built-in OpenGL renderers. If you are using a contributed " + + "library, contact the library's developers."; + // ........................................................ // Constants @@ -334,6 +345,18 @@ public abstract class PGL { } + public void setPrimary(boolean primary) { + primaryPGL = primary; + } + + + /** + * Return the native canvas the OpenGL context associated to this PGL object + * is rendering to (if any). + */ + public abstract Object getCanvas(); + + protected abstract void setFps(float fps); @@ -385,7 +408,7 @@ public abstract class PGL { } - protected boolean isFBOBacked() { + protected boolean isFBOBacked() {; return fboLayerInUse; } @@ -430,7 +453,7 @@ public abstract class PGL { protected Texture wrapBackTexture(Texture texture) { if (texture == null) { - texture = new Texture(); + texture = new Texture(pg); texture.init(pg.width, pg.height, glColorTex.get(backTex), TEXTURE_2D, RGBA, fboWidth, fboHeight, NEAREST, NEAREST, @@ -447,7 +470,7 @@ public abstract class PGL { protected Texture wrapFrontTexture(Texture texture) { if (texture == null) { - texture = new Texture(); + texture = new Texture(pg); texture.init(pg.width, pg.height, glColorTex.get(frontTex), TEXTURE_2D, RGBA, fboWidth, fboHeight, NEAREST, NEAREST, @@ -497,18 +520,119 @@ public abstract class PGL { } - protected int qualityToSamples(int quality) { - if (quality <= 1) { - return 1; + /////////////////////////////////////////////////////////// + + // Frame rendering + + + protected void beginDraw(boolean clear0) { + if (needFBOLayer(clear0)) { + if (!fboLayerCreated) createFBOLayer(); + + 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, pg.width, pg.height, + 0, 0, pg.width, pg.height, + 0, 0, pg.width, pg.height); + } + + fboLayerInUse = true; } else { - // Number of samples is always an even number: - int n = 2 * (quality / 2); - return n; + fboLayerInUse = false; + } + + if (firstFrame) { + firstFrame = false; + } + + if (!USE_FBOLAYER_BY_DEFAULT) { + // 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 + // request it again, then the rendering won't render to the FBO layer if + // not needed by the condif, since it is slower than simple onscreen + // rendering. + fboLayerRequested = false; } } - protected void createFBOLayer() { + 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, pg.width, pg.height, + 0, 0, pg.width, pg.height, + 0, 0, pg.width, pg.height); + + // Swapping front and back textures. + int temp = frontTex; + frontTex = backTex; + backTex = temp; + } + } + + + protected abstract void getGL(PGL pgl); + + + protected abstract boolean canDraw(); + + + protected abstract void requestFocus(); + + + protected abstract void requestDraw(); + + + protected abstract void swapBuffers(); + + + protected boolean threadIsCurrent() { + return Thread.currentThread() == glThread; + } + + + protected void beginGL() { } + + + protected void endGL() { } + + + private boolean needFBOLayer(boolean clear0) { + return !clear0 || fboLayerRequested || 1 < numSamples; + } + + + private void createFBOLayer() { String ext = getString(EXTENSIONS); if (-1 < ext.indexOf("texture_non_power_of_two")) { fboWidth = pg.width; @@ -649,115 +773,6 @@ public abstract class PGL { } - /////////////////////////////////////////////////////////// - - // Frame rendering - - - protected void beginDraw(boolean clear0) { - if (needFBOLayer(clear0)) { - if (!fboLayerCreated) createFBOLayer(); - - 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, pg.width, pg.height, - 0, 0, pg.width, pg.height, - 0, 0, pg.width, pg.height); - } - - fboLayerInUse = true; - } else { - fboLayerInUse = false; - } - - if (firstFrame) { - firstFrame = false; - } - - if (!USE_FBOLAYER_BY_DEFAULT) { - // 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 - // request it again, then the rendering won't render to the FBO layer if - // not needed by the condif, since it is slower than simple onscreen - // rendering. - fboLayerRequested = 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, pg.width, pg.height, - 0, 0, pg.width, pg.height, - 0, 0, pg.width, pg.height); - - // Swapping front and back textures. - int temp = frontTex; - frontTex = backTex; - backTex = temp; - } - } - - - protected abstract boolean canDraw(); - - - protected abstract void requestFocus(); - - - protected abstract void requestDraw(); - - - protected abstract void swapBuffers(); - - - protected boolean threadIsCurrent() { - return Thread.currentThread() == glThread; - } - - - protected boolean needFBOLayer(boolean clear0) { - return !clear0 || fboLayerRequested || 1 < numSamples; - } - - - protected void beginGL() { } - - - protected void endGL() { } - - /////////////////////////////////////////////////////////// // Context interface @@ -836,7 +851,7 @@ public abstract class PGL { int initColor) { int[] glcolor = new int[16 * 16]; Arrays.fill(glcolor, javaToNativeARGB(initColor)); - IntBuffer texels = PGL.allocateDirectIntBuffer(16 * 16); + IntBuffer texels = allocateDirectIntBuffer(16 * 16); texels.put(glcolor); texels.rewind(); for (int y = 0; y < height; y += 16) { @@ -895,41 +910,47 @@ public abstract class PGL { } - protected void initTex2DShader() { - if (!loadedTex2DShader || tex2DShaderContext != glContext) { + protected PGL initTex2DShader() { + PGL ppgl = primaryPGL ? this : pg.getPrimaryPGL(); + + if (!ppgl.loadedTex2DShader || ppgl.tex2DShaderContext != ppgl.glContext) { String vertSource = PApplet.join(texVertShaderSource, "\n"); String fragSource = PApplet.join(tex2DFragShaderSource, "\n"); - tex2DVertShader = createShader(VERTEX_SHADER, vertSource); - tex2DFragShader = createShader(FRAGMENT_SHADER, fragSource); - if (0 < tex2DVertShader && 0 < tex2DFragShader) { - tex2DShaderProgram = createProgram(tex2DVertShader, tex2DFragShader); + ppgl.tex2DVertShader = createShader(VERTEX_SHADER, vertSource); + ppgl.tex2DFragShader = createShader(FRAGMENT_SHADER, fragSource); + if (0 < ppgl.tex2DVertShader && 0 < ppgl.tex2DFragShader) { + ppgl.tex2DShaderProgram = createProgram(ppgl.tex2DVertShader, ppgl.tex2DFragShader); } - if (0 < tex2DShaderProgram) { - tex2DVertLoc = getAttribLocation(tex2DShaderProgram, "position"); - tex2DTCoordLoc = getAttribLocation(tex2DShaderProgram, "texCoord"); - tex2DSamplerLoc = getUniformLocation(tex2DShaderProgram, "texMap"); + if (0 < ppgl.tex2DShaderProgram) { + ppgl.tex2DVertLoc = getAttribLocation(ppgl.tex2DShaderProgram, "position"); + ppgl.tex2DTCoordLoc = getAttribLocation(ppgl.tex2DShaderProgram, "texCoord"); + ppgl.tex2DSamplerLoc = getUniformLocation(ppgl.tex2DShaderProgram, "texMap"); } - loadedTex2DShader = true; - tex2DShaderContext = glContext; + ppgl.loadedTex2DShader = true; + ppgl.tex2DShaderContext = ppgl.glContext; - genBuffers(1, intBuffer); - texGeoVBO = intBuffer.get(0); - bindBuffer(ARRAY_BUFFER, texGeoVBO); - bufferData(ARRAY_BUFFER, 16 * SIZEOF_FLOAT, null, STATIC_DRAW); + if (ppgl.texGeoVBO == 0) { + genBuffers(1, intBuffer); + ppgl.texGeoVBO = intBuffer.get(0); + bindBuffer(ARRAY_BUFFER, ppgl.texGeoVBO); + bufferData(ARRAY_BUFFER, 16 * SIZEOF_FLOAT, null, STATIC_DRAW); + } } if (texData == null) { texData = allocateDirectFloatBuffer(texCoords.length); } + + return ppgl; } protected void drawTexture2D(int id, int texW, int texH, int scrW, int scrH, int texX0, int texY0, int texX1, int texY1, int scrX0, int scrY0, int scrX1, int scrY1) { - initTex2DShader(); + PGL ppgl = initTex2DShader(); - if (0 < tex2DShaderProgram) { + if (0 < ppgl.tex2DShaderProgram) { // The texture overwrites anything drawn earlier. boolean depthTest = getDepthTest(); disable(DEPTH_TEST); @@ -946,10 +967,10 @@ public abstract class PGL { getIntegerv(VIEWPORT, viewBuffer); viewport(0, 0, scrW, scrH); - useProgram(tex2DShaderProgram); + useProgram(ppgl.tex2DShaderProgram); - enableVertexAttribArray(tex2DVertLoc); - enableVertexAttribArray(tex2DTCoordLoc); + enableVertexAttribArray(ppgl.tex2DVertLoc); + enableVertexAttribArray(ppgl.tex2DTCoordLoc); // Vertex coordinates of the textured quad are specified // in normalized screen space (-1, 1): @@ -984,14 +1005,14 @@ public abstract class PGL { enabledTex = true; } bindTexture(TEXTURE_2D, id); - uniform1i(tex2DSamplerLoc, 0); + uniform1i(ppgl.tex2DSamplerLoc, 0); texData.position(0); - bindBuffer(PGL.ARRAY_BUFFER, texGeoVBO); - bufferData(PGL.ARRAY_BUFFER, 16 * SIZEOF_FLOAT, texData, PGL.STATIC_DRAW); + bindBuffer(ARRAY_BUFFER, ppgl.texGeoVBO); + bufferData(ARRAY_BUFFER, 16 * SIZEOF_FLOAT, texData, STATIC_DRAW); - vertexAttribPointer(tex2DVertLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, 0); - vertexAttribPointer(tex2DTCoordLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, 2 * SIZEOF_FLOAT); + vertexAttribPointer(ppgl.tex2DVertLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, 0); + vertexAttribPointer(ppgl.tex2DTCoordLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, 2 * SIZEOF_FLOAT); drawArrays(TRIANGLE_STRIP, 0, 4); @@ -1002,8 +1023,8 @@ public abstract class PGL { disableTexturing(TEXTURE_2D); } - disableVertexAttribArray(tex2DVertLoc); - disableVertexAttribArray(tex2DTCoordLoc); + disableVertexAttribArray(ppgl.tex2DVertLoc); + disableVertexAttribArray(ppgl.tex2DTCoordLoc); useProgram(0); @@ -1015,47 +1036,53 @@ public abstract class PGL { depthMask(depthMask); viewport(viewBuffer.get(0), viewBuffer.get(1), - viewBuffer.get(2),viewBuffer.get(3)); + viewBuffer.get(2), viewBuffer.get(3)); } } - protected void initTexRectShader() { - if (!loadedTexRectShader || texRectShaderContext != glContext) { + protected PGL initTexRectShader() { + PGL ppgl = primaryPGL ? this : pg.getPrimaryPGL(); + + if (!ppgl.loadedTexRectShader || ppgl.texRectShaderContext != ppgl.glContext) { String vertSource = PApplet.join(texVertShaderSource, "\n"); String fragSource = PApplet.join(texRectFragShaderSource, "\n"); - texRectVertShader = createShader(VERTEX_SHADER, vertSource); - texRectFragShader = createShader(FRAGMENT_SHADER, fragSource); - if (0 < texRectVertShader && 0 < texRectFragShader) { - texRectShaderProgram = createProgram(texRectVertShader, - texRectFragShader); + ppgl.texRectVertShader = createShader(VERTEX_SHADER, vertSource); + ppgl.texRectFragShader = createShader(FRAGMENT_SHADER, fragSource); + if (0 < ppgl.texRectVertShader && 0 < ppgl.texRectFragShader) { + ppgl.texRectShaderProgram = createProgram(ppgl.texRectVertShader, + ppgl.texRectFragShader); } - if (0 < texRectShaderProgram) { - texRectVertLoc = getAttribLocation(texRectShaderProgram, "position"); - texRectTCoordLoc = getAttribLocation(texRectShaderProgram, "texCoord"); - texRectSamplerLoc = getUniformLocation(texRectShaderProgram, "texMap"); + if (0 < ppgl.texRectShaderProgram) { + ppgl.texRectVertLoc = getAttribLocation(ppgl.texRectShaderProgram, "position"); + ppgl.texRectTCoordLoc = getAttribLocation(ppgl.texRectShaderProgram, "texCoord"); + ppgl.texRectSamplerLoc = getUniformLocation(ppgl.texRectShaderProgram, "texMap"); } - loadedTexRectShader = true; - texRectShaderContext = glContext; + ppgl.loadedTexRectShader = true; + ppgl.texRectShaderContext = ppgl.glContext; - genBuffers(1, intBuffer); - texGeoVBO = intBuffer.get(0); - bindBuffer(ARRAY_BUFFER, texGeoVBO); - bufferData(ARRAY_BUFFER, 16 * SIZEOF_FLOAT, null, STATIC_DRAW); + if (ppgl.texGeoVBO == 0) { + genBuffers(1, intBuffer); + ppgl.texGeoVBO = intBuffer.get(0); + bindBuffer(ARRAY_BUFFER, ppgl.texGeoVBO); + bufferData(ARRAY_BUFFER, 16 * SIZEOF_FLOAT, null, STATIC_DRAW); + } } + + return ppgl; } protected void drawTextureRect(int id, int texW, int texH, int scrW, int scrH, int texX0, int texY0, int texX1, int texY1, int scrX0, int scrY0, int scrX1, int scrY1) { - initTexRectShader(); + PGL ppgl = initTexRectShader(); if (texData == null) { texData = allocateDirectFloatBuffer(texCoords.length); } - if (0 < texRectShaderProgram) { + if (0 < ppgl.texRectShaderProgram) { // The texture overwrites anything drawn earlier. boolean depthTest = getDepthTest(); disable(DEPTH_TEST); @@ -1072,10 +1099,10 @@ public abstract class PGL { getIntegerv(VIEWPORT, viewBuffer); viewport(0, 0, scrW, scrH); - useProgram(texRectShaderProgram); + useProgram(ppgl.texRectShaderProgram); - enableVertexAttribArray(texRectVertLoc); - enableVertexAttribArray(texRectTCoordLoc); + enableVertexAttribArray(ppgl.texRectVertLoc); + enableVertexAttribArray(ppgl.texRectTCoordLoc); // Vertex coordinates of the textured quad are specified // in normalized screen space (-1, 1): @@ -1110,14 +1137,14 @@ public abstract class PGL { enabledTex = true; } bindTexture(TEXTURE_RECTANGLE, id); - uniform1i(texRectSamplerLoc, 0); + uniform1i(ppgl.texRectSamplerLoc, 0); texData.position(0); - bindBuffer(PGL.ARRAY_BUFFER, texGeoVBO); - bufferData(PGL.ARRAY_BUFFER, 16 * SIZEOF_FLOAT, texData, PGL.STATIC_DRAW); + bindBuffer(ARRAY_BUFFER, ppgl.texGeoVBO); + bufferData(ARRAY_BUFFER, 16 * SIZEOF_FLOAT, texData, STATIC_DRAW); - vertexAttribPointer(texRectVertLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, 0); - vertexAttribPointer(texRectTCoordLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, 2 * SIZEOF_FLOAT); + vertexAttribPointer(ppgl.texRectVertLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, 0); + vertexAttribPointer(ppgl.texRectTCoordLoc, 2, FLOAT, false, 4 * SIZEOF_FLOAT, 2 * SIZEOF_FLOAT); drawArrays(TRIANGLE_STRIP, 0, 4); @@ -1128,8 +1155,8 @@ public abstract class PGL { disableTexturing(TEXTURE_RECTANGLE); } - disableVertexAttribArray(texRectVertLoc); - disableVertexAttribArray(texRectTCoordLoc); + disableVertexAttribArray(ppgl.texRectVertLoc); + disableVertexAttribArray(ppgl.texRectTCoordLoc); useProgram(0); @@ -1141,7 +1168,7 @@ public abstract class PGL { depthMask(depthMask); viewport(viewBuffer.get(0), viewBuffer.get(1), - viewBuffer.get(2),viewBuffer.get(3)); + viewBuffer.get(2), viewBuffer.get(3)); } } @@ -1193,7 +1220,7 @@ public abstract class PGL { * endian) to Java ARGB. */ protected static int nativeToJavaARGB(int color) { - if (PGL.BIG_ENDIAN) { // RGBA to ARGB + if (BIG_ENDIAN) { // RGBA to ARGB return (color >>> 8) | ((color << 24) & 0xFF000000); // equivalent to // ((color >> 8) & 0x00FFFFFF) | ((color << 24) & 0xFF000000) @@ -1425,6 +1452,17 @@ public abstract class PGL { } + protected static 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; + } + } + + protected String[] loadVertexShader(String filename) { return pg.parent.loadStrings(filename); } @@ -1475,18 +1513,45 @@ public abstract class PGL { } - protected String[] convertFragmentSource(String[] fragSrc0, - int version0, int version1) { + protected static String[] convertFragmentSource(String[] fragSrc0, + int version0, int version1) { + if (version0 == 120 && version1 == 150) { + String[] fragSrc = new String[fragSrc0.length + 2]; + fragSrc[0] = "#version 150"; + fragSrc[1] = "out vec4 fragColor;"; + for (int i = 0; i < fragSrc0.length; i++) { + String line = fragSrc0[i]; + line = line.replace("varying", "in"); + line = line.replace("attribute", "in"); + line = line.replace("gl_FragColor", "fragColor"); + line = line.replace("texture", "texMap"); + line = line.replace("texMap2D(", "texture("); + line = line.replace("texMap2DRect(", "texture("); + fragSrc[i + 2] = line; + } + return fragSrc; + } return fragSrc0; } - protected String[] convertVertexSource(String[] vertSrc0, - int version0, int version1) { + + protected static String[] convertVertexSource(String[] vertSrc0, + int version0, int version1) { + if (version0 == 120 && version1 == 150) { + String[] vertSrc = new String[vertSrc0.length + 1]; + vertSrc[0] = "#version 150"; + for (int i = 0; i < vertSrc0.length; i++) { + String line = vertSrc0[i]; + line = line.replace("attribute", "in"); + line = line.replace("varying", "out"); + vertSrc[i + 1] = line; + } + return vertSrc; + } return vertSrc0; } - protected int createShader(int shaderType, String source) { int shader = createShader(shaderType); if (shader != 0) { @@ -1706,7 +1771,7 @@ public abstract class PGL { protected static ByteBuffer allocateByteBuffer(byte[] arr) { if (USE_DIRECT_BUFFERS) { - return PGL.allocateDirectByteBuffer(arr.length); + return allocateDirectByteBuffer(arr.length); } else { return ByteBuffer.wrap(arr); } @@ -1717,7 +1782,7 @@ public abstract class PGL { boolean wrap) { if (USE_DIRECT_BUFFERS) { if (buf == null || buf.capacity() < arr.length) { - buf = PGL.allocateDirectByteBuffer(arr.length); + buf = allocateDirectByteBuffer(arr.length); } buf.position(0); buf.put(arr); @@ -1795,7 +1860,7 @@ public abstract class PGL { protected static ShortBuffer allocateShortBuffer(short[] arr) { if (USE_DIRECT_BUFFERS) { - return PGL.allocateDirectShortBuffer(arr.length); + return allocateDirectShortBuffer(arr.length); } else { return ShortBuffer.wrap(arr); } @@ -1806,7 +1871,7 @@ public abstract class PGL { boolean wrap) { if (USE_DIRECT_BUFFERS) { if (buf == null || buf.capacity() < arr.length) { - buf = PGL.allocateDirectShortBuffer(arr.length); + buf = allocateDirectShortBuffer(arr.length); } buf.position(0); buf.put(arr); @@ -1884,7 +1949,7 @@ public abstract class PGL { protected static IntBuffer allocateIntBuffer(int[] arr) { if (USE_DIRECT_BUFFERS) { - return PGL.allocateDirectIntBuffer(arr.length); + return allocateDirectIntBuffer(arr.length); } else { return IntBuffer.wrap(arr); } @@ -1895,7 +1960,7 @@ public abstract class PGL { boolean wrap) { if (USE_DIRECT_BUFFERS) { if (buf == null || buf.capacity() < arr.length) { - buf = PGL.allocateDirectIntBuffer(arr.length); + buf = allocateDirectIntBuffer(arr.length); } buf.position(0); buf.put(arr); @@ -1972,7 +2037,7 @@ public abstract class PGL { protected static FloatBuffer allocateFloatBuffer(float[] arr) { if (USE_DIRECT_BUFFERS) { - return PGL.allocateDirectFloatBuffer(arr.length); + return allocateDirectFloatBuffer(arr.length); } else { return FloatBuffer.wrap(arr); } @@ -1983,7 +2048,7 @@ public abstract class PGL { boolean wrap) { if (USE_DIRECT_BUFFERS) { if (buf == null || buf.capacity() < arr.length) { - buf = PGL.allocateDirectFloatBuffer(arr.length); + buf = allocateDirectFloatBuffer(arr.length); } buf.position(0); buf.put(arr); diff --git a/core/src/processing/opengl/PGraphics2D.java b/core/src/processing/opengl/PGraphics2D.java index 9c1761cba..7229e561d 100644 --- a/core/src/processing/opengl/PGraphics2D.java +++ b/core/src/processing/opengl/PGraphics2D.java @@ -119,6 +119,10 @@ public class PGraphics2D extends PGraphicsOpenGL { @Override protected void defaultPerspective() { + // The camera part of the modelview is simply the identity matrix, so in + // order to the ortho projection to be consistent with this, it needs to be + // set as follows, because ortho() will shift the viewing rectangle at + // (width/2, height/2) and will also apply the axis inversion along Y: super.ortho(width/2f, (3f/2f) * width, -height/2f, height/2f, -1, +1); } @@ -180,12 +184,6 @@ public class PGraphics2D extends PGraphicsOpenGL { popProjection(); } -// @Override -// public void resetMatrix() { -// super.resetMatrix(); -// defaultCamera(); -// } - ////////////////////////////////////////////////////////////// @@ -267,7 +265,7 @@ public class PGraphics2D extends PGraphicsOpenGL { } if (svg != null) { - PShapeOpenGL p2d = PShapeOpenGL.createShape2D(pg.parent, svg); + PShapeOpenGL p2d = PShapeOpenGL.createShape2D((PGraphicsOpenGL)pg, svg); return p2d; } else { return null; @@ -282,7 +280,7 @@ public class PGraphics2D extends PGraphicsOpenGL { @Override public PShape createShape(PShape source) { - return PShapeOpenGL.createShape2D(parent, source); + return PShapeOpenGL.createShape2D(this, source); } @@ -294,31 +292,31 @@ public class PGraphics2D extends PGraphicsOpenGL { @Override public PShape createShape(int type) { - return createShapeImpl(parent, type); + return createShapeImpl(this, type); } @Override public PShape createShape(int kind, float... p) { - return createShapeImpl(parent, kind, p); + return createShapeImpl(this, kind, p); } - static protected PShapeOpenGL createShapeImpl(PApplet parent, int type) { + static protected PShapeOpenGL createShapeImpl(PGraphicsOpenGL pg, int type) { PShapeOpenGL shape = null; if (type == PConstants.GROUP) { - shape = new PShapeOpenGL(parent, PConstants.GROUP); + shape = new PShapeOpenGL(pg, PConstants.GROUP); } else if (type == PShape.PATH) { - shape = new PShapeOpenGL(parent, PShape.PATH); + shape = new PShapeOpenGL(pg, PShape.PATH); } else if (type == PShape.GEOMETRY) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); + shape = new PShapeOpenGL(pg, PShape.GEOMETRY); } shape.is3D(false); return shape; } - static protected PShapeOpenGL createShapeImpl(PApplet parent, + static protected PShapeOpenGL createShapeImpl(PGraphicsOpenGL pg, int kind, float... p) { PShapeOpenGL shape = null; int len = p.length; @@ -328,49 +326,49 @@ public class PGraphics2D extends PGraphicsOpenGL { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(POINT); } else if (kind == LINE) { if (len != 4) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(LINE); } else if (kind == TRIANGLE) { if (len != 6) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(TRIANGLE); } else if (kind == QUAD) { if (len != 8) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(QUAD); } else if (kind == RECT) { if (len != 4 && len != 5 && len != 8 && len != 9) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(RECT); } else if (kind == ELLIPSE) { if (len != 4 && len != 5) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(ELLIPSE); } else if (kind == ARC) { if (len != 6 && len != 7) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(ARC); } else if (kind == BOX) { showWarning("Primitive not supported in 2D"); diff --git a/core/src/processing/opengl/PGraphics3D.java b/core/src/processing/opengl/PGraphics3D.java index 43aa52251..77edcc9f0 100644 --- a/core/src/processing/opengl/PGraphics3D.java +++ b/core/src/processing/opengl/PGraphics3D.java @@ -87,7 +87,20 @@ public class PGraphics3D extends PGraphicsOpenGL { pushProjection(); ortho(0, width, 0, height, -1, +1); pushMatrix(); - camera(width/2, height/2); + + // Set camera for 2D rendering, it simply centers at (width/2, height/2) + float centerX = width/2; + float centerY = height/2; + modelview.reset(); + modelview.translate(-centerX, -centerY); + + modelviewInv.set(modelview); + modelviewInv.invert(); + + camera.set(modelview); + cameraInv.set(modelviewInv); + + updateProjmodelview(); } @@ -131,7 +144,7 @@ public class PGraphics3D extends PGraphicsOpenGL { if (obj != null) { int prevTextureMode = pg.textureMode; pg.textureMode = NORMAL; - PShapeOpenGL p3d = PShapeOpenGL.createShape3D(pg.parent, obj); + PShapeOpenGL p3d = PShapeOpenGL.createShape3D((PGraphicsOpenGL)pg, obj); pg.textureMode = prevTextureMode; return p3d; } else { @@ -147,7 +160,7 @@ public class PGraphics3D extends PGraphicsOpenGL { @Override public PShape createShape(PShape source) { - return PShapeOpenGL.createShape3D(parent, source); + return PShapeOpenGL.createShape3D(this, source); } @@ -159,31 +172,31 @@ public class PGraphics3D extends PGraphicsOpenGL { @Override public PShape createShape(int type) { - return createShapeImpl(parent, type); + return createShapeImpl(this, type); } @Override public PShape createShape(int kind, float... p) { - return createShapeImpl(parent, kind, p); + return createShapeImpl(this, kind, p); } - static protected PShapeOpenGL createShapeImpl(PApplet parent, int type) { + static protected PShapeOpenGL createShapeImpl(PGraphicsOpenGL pg, int type) { PShapeOpenGL shape = null; if (type == PConstants.GROUP) { - shape = new PShapeOpenGL(parent, PConstants.GROUP); + shape = new PShapeOpenGL(pg, PConstants.GROUP); } else if (type == PShape.PATH) { - shape = new PShapeOpenGL(parent, PShape.PATH); + shape = new PShapeOpenGL(pg, PShape.PATH); } else if (type == PShape.GEOMETRY) { - shape = new PShapeOpenGL(parent, PShape.GEOMETRY); + shape = new PShapeOpenGL(pg, PShape.GEOMETRY); } shape.is3D(true); return shape; } - static protected PShapeOpenGL createShapeImpl(PApplet parent, + static protected PShapeOpenGL createShapeImpl(PGraphicsOpenGL pg, int kind, float... p) { PShapeOpenGL shape = null; int len = p.length; @@ -193,63 +206,63 @@ public class PGraphics3D extends PGraphicsOpenGL { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(POINT); } else if (kind == LINE) { if (len != 4 && len != 6) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(LINE); } else if (kind == TRIANGLE) { if (len != 6) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(TRIANGLE); } else if (kind == QUAD) { if (len != 8) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(QUAD); } else if (kind == RECT) { if (len != 4 && len != 5 && len != 8 && len != 9) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(RECT); } else if (kind == ELLIPSE) { if (len != 4 && len != 5) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(ELLIPSE); } else if (kind == ARC) { if (len != 6 && len != 7) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(ARC); } else if (kind == BOX) { if (len != 1 && len != 3) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(BOX); } else if (kind == SPHERE) { if (len < 1 || 3 < len) { showWarning("Wrong number of parameters"); return null; } - shape = new PShapeOpenGL(parent, PShape.PRIMITIVE); + shape = new PShapeOpenGL(pg, PShape.PRIMITIVE); shape.setKind(SPHERE); } else { showWarning("Unrecognized primitive type"); diff --git a/core/src/processing/opengl/PGraphicsOpenGL.java b/core/src/processing/opengl/PGraphicsOpenGL.java index f613fc583..b3a0450fb 100644 --- a/core/src/processing/opengl/PGraphicsOpenGL.java +++ b/core/src/processing/opengl/PGraphicsOpenGL.java @@ -34,81 +34,13 @@ import java.util.*; */ public class PGraphicsOpenGL extends PGraphics { /** Interface between Processing and OpenGL */ - public static PGL pgl; - - /** The main PApplet renderer. */ - protected static PGraphicsOpenGL pgPrimary = null; + public PGL pgl; /** The renderer currently in use. */ - protected static PGraphicsOpenGL pgCurrent = null; + protected PGraphicsOpenGL currentPG; /** Font cache for texture objects. */ - protected WeakHashMap fontMap = - new WeakHashMap(); - - // ........................................................ - - static final String OPENGL_THREAD_ERROR = - "Cannot run the OpenGL renderer outside the main thread, change your code" + - "\nso the drawing calls are all inside the main thread, " + - "\nor use the default renderer instead."; - static final String BLEND_DRIVER_ERROR = - "blendMode(%1$s) is not supported by this hardware (or driver)"; - static final String BLEND_RENDERER_ERROR = - "blendMode(%1$s) is not supported by this renderer"; - static final String ALREADY_DRAWING_ERROR = - "Already called beginDraw()"; - static final String NO_BEGIN_DRAW_ERROR = - "Cannot call endDraw() before beginDraw()"; - static final String NESTED_DRAW_ERROR = - "Already called drawing on another PGraphicsOpenGL object"; - static final String ALREADY_BEGAN_CONTOUR_ERROR = - "Already called beginContour()"; - static final String NO_BEGIN_CONTOUR_ERROR = - "Need to call beginContour() first"; - static final String UNSUPPORTED_SMOOTH_LEVEL_ERROR = - "Smooth level %1$s is not available. Using %2$s instead"; - static final String UNSUPPORTED_SMOOTH_ERROR = - "Smooth is not supported by this hardware (or driver)"; - static final String TOO_MANY_SMOOTH_CALLS_ERROR = - "The smooth/noSmooth functions are being called too often.\n" + - "This results in screen flickering, so they will be disabled\n" + - "for the rest of the sketch's execution"; - static final String UNSUPPORTED_SHAPE_FORMAT_ERROR = - "Unsupported shape format"; - static final String MISSING_UV_TEXCOORDS_ERROR = - "No uv texture coordinates supplied with vertex() call"; - static final String INVALID_FILTER_SHADER_ERROR = - "Your shader needs to be of TEXTURE type to be used as a filter"; - static final String INCONSISTENT_SHADER_TYPES = - "The vertex and fragment shaders have different types"; - static final String WRONG_SHADER_TYPE_ERROR = - "shader() called with a wrong shader"; - static final String SHADER_NEED_LIGHT_ATTRIBS = - "The provided shader needs light attributes (ambient, diffuse, etc.), but " + - "the current scene is unlit, so the default shader will be used instead"; - static final String MISSING_FRAGMENT_SHADER = - "The fragment shader is missing, cannot create shader object"; - static final String MISSING_VERTEX_SHADER = - "The vertex shader is missing, cannot create shader object"; - static final String UNKNOWN_SHADER_KIND_ERROR = - "Unknown shader kind"; - static final String NO_TEXLIGHT_SHADER_ERROR = - "Your shader needs to be of TEXLIGHT type " + - "to render this geometry properly, using default shader instead."; - static final String NO_LIGHT_SHADER_ERROR = - "Your shader needs to be of LIGHT type " + - "to render this geometry properly, using default shader instead."; - static final String NO_TEXTURE_SHADER_ERROR = - "Your shader needs to be of TEXTURE type " + - "to render this geometry properly, using default shader instead."; - static final String NO_COLOR_SHADER_ERROR = - "Your shader needs to be of COLOR type " + - "to render this geometry properly, using default shader instead."; - static final String TOO_LONG_STROKE_PATH_ERROR = - "Stroke path is too long, some bevel triangles won't be added"; - static final String TESSELLATION_ERROR = - "Tessellation Error: %1$s"; + protected WeakHashMap fontMap; // ........................................................ @@ -162,8 +94,8 @@ public class PGraphicsOpenGL extends PGraphics { protected boolean pointBuffersCreated = false; protected int pointBuffersContext; - protected static final int INIT_VERTEX_BUFFER_SIZE = 256; - protected static final int INIT_INDEX_BUFFER_SIZE = 512; + static protected final int INIT_VERTEX_BUFFER_SIZE = 256; + static protected final int INIT_INDEX_BUFFER_SIZE = 512; // ........................................................ @@ -195,7 +127,7 @@ public class PGraphicsOpenGL extends PGraphics { // ........................................................ - // GL objects: + // GL resources: static protected HashMap glTextureObjects = new HashMap(); @@ -236,17 +168,16 @@ public class PGraphicsOpenGL extends PGraphics { PGraphicsOpenGL.class.getResource("PointVert.glsl"); static protected URL defPointShaderFragURL = PGraphicsOpenGL.class.getResource("PointFrag.glsl"); - - static protected PShader defColorShader; - static protected PShader defTextureShader; - static protected PShader defLightShader; - static protected PShader defTexlightShader; - static protected PShader defLineShader; - static protected PShader defPointShader; - static protected URL maskShaderFragURL = PGraphicsOpenGL.class.getResource("MaskFrag.glsl"); - static protected PShader maskShader; + + protected PShader defColorShader; + protected PShader defTextureShader; + protected PShader defLightShader; + protected PShader defTexlightShader; + protected PShader defLineShader; + protected PShader defPointShader; + protected PShader maskShader; protected PShader polyShader; protected PShader lineShader; @@ -258,8 +189,8 @@ public class PGraphicsOpenGL extends PGraphics { protected InGeometry inGeo; protected TessGeometry tessGeo; - static protected Tessellator tessellator; protected TexCache texCache; + static protected Tessellator tessellator; // ........................................................ @@ -407,11 +338,11 @@ public class PGraphicsOpenGL extends PGraphics { static protected final int FB_STACK_DEPTH = 16; - static protected int fbStackDepth; - static protected FrameBuffer[] fbStack = new FrameBuffer[FB_STACK_DEPTH]; - static protected FrameBuffer drawFramebuffer; - static protected FrameBuffer readFramebuffer; - static protected FrameBuffer currentFramebuffer; + protected int fbStackDepth; + protected FrameBuffer[] fbStack; + protected FrameBuffer drawFramebuffer; + protected FrameBuffer readFramebuffer; + protected FrameBuffer currentFramebuffer; // ....................................................... @@ -487,7 +418,6 @@ public class PGraphicsOpenGL extends PGraphics { protected boolean openContour = false; protected boolean breakShape = false; protected boolean defaultEdges = false; - protected PImage textureImage0; static protected final int EDGE_MIDDLE = 0; static protected final int EDGE_START = 1; @@ -519,15 +449,73 @@ public class PGraphicsOpenGL extends PGraphics { static protected IntBuffer intBuffer; static protected FloatBuffer floatBuffer; + // ........................................................ + + // Error strings: + + static final String OPENGL_THREAD_ERROR = + "Cannot run the OpenGL renderer outside the main thread, change your code" + + "\nso the drawing calls are all inside the main thread, " + + "\nor use the default renderer instead."; + static final String BLEND_DRIVER_ERROR = + "blendMode(%1$s) is not supported by this hardware (or driver)"; + static final String BLEND_RENDERER_ERROR = + "blendMode(%1$s) is not supported by this renderer"; + static final String ALREADY_BEGAN_CONTOUR_ERROR = + "Already called beginContour()"; + static final String NO_BEGIN_CONTOUR_ERROR = + "Need to call beginContour() first"; + static final String UNSUPPORTED_SMOOTH_LEVEL_ERROR = + "Smooth level %1$s is not available. Using %2$s instead"; + static final String UNSUPPORTED_SMOOTH_ERROR = + "Smooth is not supported by this hardware (or driver)"; + static final String TOO_MANY_SMOOTH_CALLS_ERROR = + "The smooth/noSmooth functions are being called too often.\n" + + "This results in screen flickering, so they will be disabled\n" + + "for the rest of the sketch's execution"; + static final String UNSUPPORTED_SHAPE_FORMAT_ERROR = + "Unsupported shape format"; + static final String MISSING_UV_TEXCOORDS_ERROR = + "No uv texture coordinates supplied with vertex() call"; + static final String INVALID_FILTER_SHADER_ERROR = + "Your shader cannot be used as a filter because is of type POINT or LINES"; + static final String INCONSISTENT_SHADER_TYPES = + "The vertex and fragment shaders have different types"; + static final String WRONG_SHADER_TYPE_ERROR = + "shader() called with a wrong shader"; + static final String SHADER_NEED_LIGHT_ATTRIBS = + "The provided shader needs light attributes (ambient, diffuse, etc.), but " + + "the current scene is unlit, so the default shader will be used instead"; + static final String MISSING_FRAGMENT_SHADER = + "The fragment shader is missing, cannot create shader object"; + static final String MISSING_VERTEX_SHADER = + "The vertex shader is missing, cannot create shader object"; + static final String UNKNOWN_SHADER_KIND_ERROR = + "Unknown shader kind"; + static final String NO_TEXLIGHT_SHADER_ERROR = + "Your shader needs to be of TEXLIGHT type " + + "to render this geometry properly, using default shader instead."; + static final String NO_LIGHT_SHADER_ERROR = + "Your shader needs to be of LIGHT type " + + "to render this geometry properly, using default shader instead."; + static final String NO_TEXTURE_SHADER_ERROR = + "Your shader needs to be of TEXTURE type " + + "to render this geometry properly, using default shader instead."; + static final String NO_COLOR_SHADER_ERROR = + "Your shader needs to be of COLOR type " + + "to render this geometry properly, using default shader instead."; + static final String TOO_LONG_STROKE_PATH_ERROR = + "Stroke path is too long, some bevel triangles won't be added"; + static final String TESSELLATION_ERROR = + "Tessellation Error: %1$s"; + ////////////////////////////////////////////////////////////// // INIT/ALLOCATE/FINISH public PGraphicsOpenGL() { - if (pgl == null) { - pgl = createPGL(this); - } + pgl = createPGL(this); if (tessellator == null) { tessellator = new Tessellator(); @@ -542,7 +530,7 @@ public class PGraphicsOpenGL extends PGraphics { inGeo = newInGeometry(this, IMMEDIATE); tessGeo = newTessGeometry(this, IMMEDIATE); - texCache = newTexCache(); + texCache = newTexCache(this); initialized = false; } @@ -551,7 +539,12 @@ public class PGraphicsOpenGL extends PGraphics { @Override public void setPrimary(boolean primary) { super.setPrimary(primary); + pgl.setPrimary(primary); format = ARGB; + if (primary) { + fbStack = new FrameBuffer[FB_STACK_DEPTH]; + fontMap = new WeakHashMap(); + } } @@ -652,12 +645,13 @@ public class PGraphicsOpenGL extends PGraphics { } } - deleteFinalizedGLResources(); + deleteFinalizedGLResources(pgl); - if (primarySurface) pgl.deleteSurface(); + if (primarySurface) { + pgl.deleteSurface(); + } } -// @Override @Override protected void finalize() throws Throwable { try { @@ -686,21 +680,44 @@ public class PGraphicsOpenGL extends PGraphics { } + ////////////////////////////////////////////////////////////// + + // IMAGE METADATA FOR THIS RENDERER + + + @Override + public void setCache(PImage image, Object storage) { + getPrimaryPG().cacheMap.put(image, storage); + } + + + @Override + public Object getCache(PImage image) { + return getPrimaryPG().cacheMap.get(image); + } + + + @Override + public void removeCache(PImage image) { + getPrimaryPG().cacheMap.remove(image); + } + + ////////////////////////////////////////////////////////////// protected void setFontTexture(PFont font, FontTexture fontTexture) { - fontMap.put(font, fontTexture); + getPrimaryPG().fontMap.put(font, fontTexture); } protected FontTexture getFontTexture(PFont font) { - return fontMap.get(font); + return getPrimaryPG().fontMap.get(font); } protected void removeFontTexture(PFont font) { - fontMap.remove(font); + getPrimaryPG().fontMap.remove(font); } @@ -736,8 +753,8 @@ public class PGraphicsOpenGL extends PGraphics { // Texture Objects ----------------------------------------------------------- - protected static int createTextureObject(int context) { - deleteFinalizedTextureObjects(); + protected static int createTextureObject(int context, PGL pgl) { + deleteFinalizedTextureObjects(pgl); pgl.genTextures(1, intBuffer); int id = intBuffer.get(0); @@ -750,7 +767,7 @@ public class PGraphicsOpenGL extends PGraphics { return id; } - protected static void deleteTextureObject(int id, int context) { + protected static void deleteTextureObject(int id, int context, PGL pgl) { GLResource res = new GLResource(id, context); if (glTextureObjects.containsKey(res)) { intBuffer.put(0, id); @@ -759,7 +776,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteAllTextureObjects() { + protected static void deleteAllTextureObjects(PGL pgl) { for (GLResource res : glTextureObjects.keySet()) { intBuffer.put(0, res.id); if (pgl.threadIsCurrent()) pgl.deleteTextures(1, intBuffer); @@ -775,7 +792,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteFinalizedTextureObjects() { + protected static void deleteFinalizedTextureObjects(PGL pgl) { Set finalized = new HashSet(); for (GLResource res : glTextureObjects.keySet()) { @@ -800,8 +817,8 @@ public class PGraphicsOpenGL extends PGraphics { // Vertex Buffer Objects ----------------------------------------------------- - protected static int createVertexBufferObject(int context) { - deleteFinalizedVertexBufferObjects(); + protected static int createVertexBufferObject(int context, PGL pgl) { + deleteFinalizedVertexBufferObjects(pgl); pgl.genBuffers(1, intBuffer); int id = intBuffer.get(0); @@ -814,7 +831,7 @@ public class PGraphicsOpenGL extends PGraphics { return id; } - protected static void deleteVertexBufferObject(int id, int context) { + protected static void deleteVertexBufferObject(int id, int context, PGL pgl) { GLResource res = new GLResource(id, context); if (glVertexBuffers.containsKey(res)) { intBuffer.put(0, id); @@ -823,7 +840,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteAllVertexBufferObjects() { + protected static void deleteAllVertexBufferObjects(PGL pgl) { for (GLResource res : glVertexBuffers.keySet()) { intBuffer.put(0, res.id); if (pgl.threadIsCurrent()) pgl.deleteBuffers(1, intBuffer); @@ -839,7 +856,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteFinalizedVertexBufferObjects() { + protected static void deleteFinalizedVertexBufferObjects(PGL pgl) { Set finalized = new HashSet(); for (GLResource res : glVertexBuffers.keySet()) { @@ -864,8 +881,8 @@ public class PGraphicsOpenGL extends PGraphics { // FrameBuffer Objects ------------------------------------------------------- - protected static int createFrameBufferObject(int context) { - deleteFinalizedFrameBufferObjects(); + protected static int createFrameBufferObject(int context, PGL pgl) { + deleteFinalizedFrameBufferObjects(pgl); pgl.genFramebuffers(1, intBuffer); int id = intBuffer.get(0); @@ -878,7 +895,7 @@ public class PGraphicsOpenGL extends PGraphics { return id; } - protected static void deleteFrameBufferObject(int id, int context) { + protected static void deleteFrameBufferObject(int id, int context, PGL pgl) { GLResource res = new GLResource(id, context); if (glFrameBuffers.containsKey(res)) { intBuffer.put(0, id); @@ -887,7 +904,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteAllFrameBufferObjects() { + protected static void deleteAllFrameBufferObjects(PGL pgl) { for (GLResource res : glFrameBuffers.keySet()) { intBuffer.put(0, res.id); if (pgl.threadIsCurrent()) pgl.deleteFramebuffers(1, intBuffer); @@ -903,7 +920,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteFinalizedFrameBufferObjects() { + protected static void deleteFinalizedFrameBufferObjects(PGL pgl) { Set finalized = new HashSet(); for (GLResource res : glFrameBuffers.keySet()) { @@ -930,8 +947,8 @@ public class PGraphicsOpenGL extends PGraphics { // RenderBuffer Objects ------------------------------------------------------ - protected static int createRenderBufferObject(int context) { - deleteFinalizedRenderBufferObjects(); + protected static int createRenderBufferObject(int context, PGL pgl) { + deleteFinalizedRenderBufferObjects(pgl); pgl.genRenderbuffers(1, intBuffer); int id = intBuffer.get(0); @@ -944,7 +961,7 @@ public class PGraphicsOpenGL extends PGraphics { return id; } - protected static void deleteRenderBufferObject(int id, int context) { + protected static void deleteRenderBufferObject(int id, int context, PGL pgl) { GLResource res = new GLResource(id, context); if (glRenderBuffers.containsKey(res)) { intBuffer.put(0, id); @@ -953,7 +970,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteAllRenderBufferObjects() { + protected static void deleteAllRenderBufferObjects(PGL pgl) { for (GLResource res : glRenderBuffers.keySet()) { intBuffer.put(0, res.id); if (pgl.threadIsCurrent()) pgl.deleteRenderbuffers(1, intBuffer); @@ -969,7 +986,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteFinalizedRenderBufferObjects() { + protected static void deleteFinalizedRenderBufferObjects(PGL pgl) { Set finalized = new HashSet(); for (GLResource res : glRenderBuffers.keySet()) { @@ -994,8 +1011,8 @@ public class PGraphicsOpenGL extends PGraphics { // GLSL Program Objects ------------------------------------------------------ - protected static int createGLSLProgramObject(int context) { - deleteFinalizedGLSLProgramObjects(); + protected static int createGLSLProgramObject(int context, PGL pgl) { + deleteFinalizedGLSLProgramObjects(pgl); int id = pgl.createProgram(); @@ -1007,7 +1024,7 @@ public class PGraphicsOpenGL extends PGraphics { return id; } - protected static void deleteGLSLProgramObject(int id, int context) { + protected static void deleteGLSLProgramObject(int id, int context, PGL pgl) { GLResource res = new GLResource(id, context); if (glslPrograms.containsKey(res)) { if (pgl.threadIsCurrent()) pgl.deleteProgram(res.id); @@ -1015,7 +1032,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteAllGLSLProgramObjects() { + protected static void deleteAllGLSLProgramObjects(PGL pgl) { for (GLResource res : glslPrograms.keySet()) { if (pgl.threadIsCurrent()) pgl.deleteProgram(res.id); } @@ -1030,7 +1047,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteFinalizedGLSLProgramObjects() { + protected static void deleteFinalizedGLSLProgramObjects(PGL pgl) { Set finalized = new HashSet(); for (GLResource res : glslPrograms.keySet()) { @@ -1054,8 +1071,8 @@ public class PGraphicsOpenGL extends PGraphics { // GLSL Vertex Shader Objects ------------------------------------------------ - protected static int createGLSLVertShaderObject(int context) { - deleteFinalizedGLSLVertShaderObjects(); + protected static int createGLSLVertShaderObject(int context, PGL pgl) { + deleteFinalizedGLSLVertShaderObjects(pgl); int id = pgl.createShader(PGL.VERTEX_SHADER); @@ -1067,7 +1084,7 @@ public class PGraphicsOpenGL extends PGraphics { return id; } - protected static void deleteGLSLVertShaderObject(int id, int context) { + protected static void deleteGLSLVertShaderObject(int id, int context, PGL pgl) { GLResource res = new GLResource(id, context); if (glslVertexShaders.containsKey(res)) { if (pgl.threadIsCurrent()) pgl.deleteShader(res.id); @@ -1075,7 +1092,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteAllGLSLVertShaderObjects() { + protected static void deleteAllGLSLVertShaderObjects(PGL pgl) { for (GLResource res : glslVertexShaders.keySet()) { if (pgl.threadIsCurrent()) pgl.deleteShader(res.id); } @@ -1091,7 +1108,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteFinalizedGLSLVertShaderObjects() { + protected static void deleteFinalizedGLSLVertShaderObjects(PGL pgl) { Set finalized = new HashSet(); for (GLResource res : glslVertexShaders.keySet()) { @@ -1115,8 +1132,8 @@ public class PGraphicsOpenGL extends PGraphics { // GLSL Fragment Shader Objects ---------------------------------------------- - protected static int createGLSLFragShaderObject(int context) { - deleteFinalizedGLSLFragShaderObjects(); + protected static int createGLSLFragShaderObject(int context, PGL pgl) { + deleteFinalizedGLSLFragShaderObjects(pgl); int id = pgl.createShader(PGL.FRAGMENT_SHADER); @@ -1128,7 +1145,7 @@ public class PGraphicsOpenGL extends PGraphics { return id; } - protected static void deleteGLSLFragShaderObject(int id, int context) { + protected static void deleteGLSLFragShaderObject(int id, int context, PGL pgl) { GLResource res = new GLResource(id, context); if (glslFragmentShaders.containsKey(res)) { if (pgl.threadIsCurrent()) pgl.deleteShader(res.id); @@ -1136,7 +1153,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteAllGLSLFragShaderObjects() { + protected static void deleteAllGLSLFragShaderObjects(PGL pgl) { for (GLResource res : glslFragmentShaders.keySet()) { if (pgl.threadIsCurrent()) pgl.deleteShader(res.id); } @@ -1152,7 +1169,7 @@ public class PGraphicsOpenGL extends PGraphics { } } - protected static void deleteFinalizedGLSLFragShaderObjects() { + protected static void deleteFinalizedGLSLFragShaderObjects(PGL pgl) { Set finalized = new HashSet(); for (GLResource res : glslFragmentShaders.keySet()) { @@ -1176,14 +1193,14 @@ public class PGraphicsOpenGL extends PGraphics { // All OpenGL resources ------------------------------------------------------ - protected static void deleteFinalizedGLResources() { - deleteFinalizedTextureObjects(); - deleteFinalizedVertexBufferObjects(); - deleteFinalizedFrameBufferObjects(); - deleteFinalizedRenderBufferObjects(); - deleteFinalizedGLSLProgramObjects(); - deleteFinalizedGLSLVertShaderObjects(); - deleteFinalizedGLSLFragShaderObjects(); + protected static void deleteFinalizedGLResources(PGL pgl) { + deleteFinalizedTextureObjects(pgl); + deleteFinalizedVertexBufferObjects(pgl); + deleteFinalizedFrameBufferObjects(pgl); + deleteFinalizedRenderBufferObjects(pgl); + deleteFinalizedGLSLProgramObjects(pgl); + deleteFinalizedGLSLVertShaderObjects(pgl); + deleteFinalizedGLSLFragShaderObjects(pgl); } @@ -1192,37 +1209,45 @@ public class PGraphicsOpenGL extends PGraphics { // FRAMEBUFFERS - protected static void pushFramebuffer() { - if (fbStackDepth == FB_STACK_DEPTH) { + protected void pushFramebuffer() { + PGraphicsOpenGL ppg = getPrimaryPG(); + if (ppg.fbStackDepth == FB_STACK_DEPTH) { throw new RuntimeException("Too many pushFramebuffer calls"); } - fbStack[fbStackDepth] = currentFramebuffer; - fbStackDepth++; + ppg.fbStack[ppg.fbStackDepth] = ppg.currentFramebuffer; + ppg.fbStackDepth++; } - protected static void setFramebuffer(FrameBuffer fbo) { - if (currentFramebuffer != fbo) { - currentFramebuffer = fbo; - currentFramebuffer.bind(); + protected void setFramebuffer(FrameBuffer fbo) { + PGraphicsOpenGL ppg = getPrimaryPG(); + if (ppg.currentFramebuffer != fbo) { + ppg.currentFramebuffer = fbo; + if (ppg.currentFramebuffer != null) ppg.currentFramebuffer.bind(); } } - protected static void popFramebuffer() { - if (fbStackDepth == 0) { + protected void popFramebuffer() { + PGraphicsOpenGL ppg = getPrimaryPG(); + if (ppg.fbStackDepth == 0) { throw new RuntimeException("popFramebuffer call is unbalanced."); } - fbStackDepth--; - FrameBuffer fbo = fbStack[fbStackDepth]; - if (currentFramebuffer != fbo) { - currentFramebuffer.finish(pgPrimary); - currentFramebuffer = fbo; - currentFramebuffer.bind(); + ppg.fbStackDepth--; + FrameBuffer fbo = ppg.fbStack[ppg.fbStackDepth]; + if (ppg.currentFramebuffer != fbo) { + ppg.currentFramebuffer.finish(); + ppg.currentFramebuffer = fbo; + if (ppg.currentFramebuffer != null) ppg.currentFramebuffer.bind(); } } + protected FrameBuffer getCurrentFB() { + return getPrimaryPG().currentFramebuffer; + } + + ////////////////////////////////////////////////////////////// // FRAME RENDERING @@ -1236,41 +1261,41 @@ public class PGraphicsOpenGL extends PGraphics { int sizei = INIT_VERTEX_BUFFER_SIZE * PGL.SIZEOF_INT; int sizex = INIT_INDEX_BUFFER_SIZE * PGL.SIZEOF_INDEX; - glPolyVertex = createVertexBufferObject(polyBuffersContext); + glPolyVertex = createVertexBufferObject(polyBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyVertex); pgl.bufferData(PGL.ARRAY_BUFFER, 3 * sizef, null, PGL.STATIC_DRAW); - glPolyColor = createVertexBufferObject(polyBuffersContext); + glPolyColor = createVertexBufferObject(polyBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyColor); pgl.bufferData(PGL.ARRAY_BUFFER, sizei, null, PGL.STATIC_DRAW); - glPolyNormal = createVertexBufferObject(polyBuffersContext); + glPolyNormal = createVertexBufferObject(polyBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyNormal); pgl.bufferData(PGL.ARRAY_BUFFER, 3 * sizef, null, PGL.STATIC_DRAW); - glPolyTexcoord = createVertexBufferObject(polyBuffersContext); + glPolyTexcoord = createVertexBufferObject(polyBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyTexcoord); pgl.bufferData(PGL.ARRAY_BUFFER, 2 * sizef, null, PGL.STATIC_DRAW); - glPolyAmbient = createVertexBufferObject(polyBuffersContext); + glPolyAmbient = createVertexBufferObject(polyBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyAmbient); pgl.bufferData(PGL.ARRAY_BUFFER, sizei, null, PGL.STATIC_DRAW); - glPolySpecular = createVertexBufferObject(polyBuffersContext); + glPolySpecular = createVertexBufferObject(polyBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolySpecular); pgl.bufferData(PGL.ARRAY_BUFFER, sizei, null, PGL.STATIC_DRAW); - glPolyEmissive = createVertexBufferObject(polyBuffersContext); + glPolyEmissive = createVertexBufferObject(polyBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyEmissive); pgl.bufferData(PGL.ARRAY_BUFFER, sizei, null, PGL.STATIC_DRAW); - glPolyShininess = createVertexBufferObject(polyBuffersContext); + glPolyShininess = createVertexBufferObject(polyBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyShininess); pgl.bufferData(PGL.ARRAY_BUFFER, sizef, null, PGL.STATIC_DRAW); pgl.bindBuffer(PGL.ARRAY_BUFFER, 0); - glPolyIndex = createVertexBufferObject(polyBuffersContext); + glPolyIndex = createVertexBufferObject(polyBuffersContext, pgl); pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, glPolyIndex); pgl.bufferData(PGL.ELEMENT_ARRAY_BUFFER, sizex, null, PGL.STATIC_DRAW); @@ -1355,31 +1380,31 @@ public class PGraphicsOpenGL extends PGraphics { protected void deletePolyBuffers() { if (polyBuffersCreated) { - deleteVertexBufferObject(glPolyVertex, polyBuffersContext); + deleteVertexBufferObject(glPolyVertex, polyBuffersContext, pgl); glPolyVertex = 0; - deleteVertexBufferObject(glPolyColor, polyBuffersContext); + deleteVertexBufferObject(glPolyColor, polyBuffersContext, pgl); glPolyColor = 0; - deleteVertexBufferObject(glPolyNormal, polyBuffersContext); + deleteVertexBufferObject(glPolyNormal, polyBuffersContext, pgl); glPolyNormal = 0; - deleteVertexBufferObject(glPolyTexcoord, polyBuffersContext); + deleteVertexBufferObject(glPolyTexcoord, polyBuffersContext, pgl); glPolyTexcoord = 0; - deleteVertexBufferObject(glPolyAmbient, polyBuffersContext); + deleteVertexBufferObject(glPolyAmbient, polyBuffersContext, pgl); glPolyAmbient = 0; - deleteVertexBufferObject(glPolySpecular, polyBuffersContext); + deleteVertexBufferObject(glPolySpecular, polyBuffersContext, pgl); glPolySpecular = 0; - deleteVertexBufferObject(glPolyEmissive, polyBuffersContext); + deleteVertexBufferObject(glPolyEmissive, polyBuffersContext, pgl); glPolyEmissive = 0; - deleteVertexBufferObject(glPolyShininess, polyBuffersContext); + deleteVertexBufferObject(glPolyShininess, polyBuffersContext, pgl); glPolyShininess = 0; - deleteVertexBufferObject(glPolyIndex, polyBuffersContext); + deleteVertexBufferObject(glPolyIndex, polyBuffersContext, pgl); glPolyIndex = 0; polyBuffersCreated = false; @@ -1395,22 +1420,22 @@ public class PGraphicsOpenGL extends PGraphics { int sizei = INIT_VERTEX_BUFFER_SIZE * PGL.SIZEOF_INT; int sizex = INIT_INDEX_BUFFER_SIZE * PGL.SIZEOF_INDEX; - glLineVertex = createVertexBufferObject(lineBuffersContext); + glLineVertex = createVertexBufferObject(lineBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glLineVertex); pgl.bufferData(PGL.ARRAY_BUFFER, 3 * sizef, null, PGL.STATIC_DRAW); - glLineColor = createVertexBufferObject(lineBuffersContext); + glLineColor = createVertexBufferObject(lineBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glLineColor); pgl.bufferData(PGL.ARRAY_BUFFER, sizei, null, PGL.STATIC_DRAW); - glLineAttrib = createVertexBufferObject(lineBuffersContext); + glLineAttrib = createVertexBufferObject(lineBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glLineAttrib); pgl.bufferData(PGL.ARRAY_BUFFER, 4 * sizef, null, PGL.STATIC_DRAW); pgl.bindBuffer(PGL.ARRAY_BUFFER, 0); - glLineIndex = createVertexBufferObject(lineBuffersContext); + glLineIndex = createVertexBufferObject(lineBuffersContext, pgl); pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, glLineIndex); pgl.bufferData(PGL.ELEMENT_ARRAY_BUFFER, sizex, null, PGL.STATIC_DRAW); @@ -1464,16 +1489,16 @@ public class PGraphicsOpenGL extends PGraphics { protected void deleteLineBuffers() { if (lineBuffersCreated) { - deleteVertexBufferObject(glLineVertex, lineBuffersContext); + deleteVertexBufferObject(glLineVertex, lineBuffersContext, pgl); glLineVertex = 0; - deleteVertexBufferObject(glLineColor, lineBuffersContext); + deleteVertexBufferObject(glLineColor, lineBuffersContext, pgl); glLineColor = 0; - deleteVertexBufferObject(glLineAttrib, lineBuffersContext); + deleteVertexBufferObject(glLineAttrib, lineBuffersContext, pgl); glLineAttrib = 0; - deleteVertexBufferObject(glLineIndex, lineBuffersContext); + deleteVertexBufferObject(glLineIndex, lineBuffersContext, pgl); glLineIndex = 0; lineBuffersCreated = false; @@ -1489,21 +1514,21 @@ public class PGraphicsOpenGL extends PGraphics { int sizei = INIT_VERTEX_BUFFER_SIZE * PGL.SIZEOF_INT; int sizex = INIT_INDEX_BUFFER_SIZE * PGL.SIZEOF_INDEX; - glPointVertex = createVertexBufferObject(pointBuffersContext); + glPointVertex = createVertexBufferObject(pointBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPointVertex); pgl.bufferData(PGL.ARRAY_BUFFER, 3 * sizef, null, PGL.STATIC_DRAW); - glPointColor = createVertexBufferObject(pointBuffersContext); + glPointColor = createVertexBufferObject(pointBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPointColor); pgl.bufferData(PGL.ARRAY_BUFFER, sizei, null, PGL.STATIC_DRAW); - glPointAttrib = createVertexBufferObject(pointBuffersContext); + glPointAttrib = createVertexBufferObject(pointBuffersContext, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPointAttrib); pgl.bufferData(PGL.ARRAY_BUFFER, 2 * sizef, null, PGL.STATIC_DRAW); pgl.bindBuffer(PGL.ARRAY_BUFFER, 0); - glPointIndex = createVertexBufferObject(pointBuffersContext); + glPointIndex = createVertexBufferObject(pointBuffersContext, pgl); pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, glPointIndex); pgl.bufferData(PGL.ELEMENT_ARRAY_BUFFER, sizex, null, PGL.STATIC_DRAW); @@ -1557,16 +1582,16 @@ public class PGraphicsOpenGL extends PGraphics { protected void deletePointBuffers() { if (pointBuffersCreated) { - deleteVertexBufferObject(glPointVertex, pointBuffersContext); + deleteVertexBufferObject(glPointVertex, pointBuffersContext, pgl); glPointVertex = 0; - deleteVertexBufferObject(glPointColor, pointBuffersContext); + deleteVertexBufferObject(glPointColor, pointBuffersContext, pgl); glPointColor = 0; - deleteVertexBufferObject(glPointAttrib, pointBuffersContext); + deleteVertexBufferObject(glPointAttrib, pointBuffersContext, pgl); glPointAttrib = 0; - deleteVertexBufferObject(glPointIndex, pointBuffersContext); + deleteVertexBufferObject(glPointIndex, pointBuffersContext, pgl); glPointIndex = 0; pointBuffersCreated = false; @@ -1605,6 +1630,13 @@ public class PGraphicsOpenGL extends PGraphics { @Override public void beginDraw() { + if (primarySurface) { + setCurrentPG(this); + } else { + pgl.getGL(getPrimaryPGL()); + getPrimaryPG().setCurrentPG(this); + } + report("top beginDraw()"); if (!checkGLThread()) { @@ -1612,23 +1644,14 @@ public class PGraphicsOpenGL extends PGraphics { } if (drawing) { - PGraphics.showWarning(ALREADY_DRAWING_ERROR); return; } - if (pgCurrent != null && !pgCurrent.primarySurface && - !this.primarySurface) { - // It seems that the user is trying to start another beginDraw()/endDraw() - // block for an offscreen surface, still drawing on another one. - PGraphics.showWarning(NESTED_DRAW_ERROR); - return; - } - - if (!primarySurface && pgPrimary.texCache.containsTexture(this)) { + if (!primarySurface && getPrimaryPG().texCache.containsTexture(this)) { // This offscreen surface is being used as a texture earlier in draw, - // so we should update the rendering up to this point since it will + // so we should update the rendering up to this point since it will be // modified. - pgPrimary.flush(); + getPrimaryPG().flush(); } if (!glParamsRead) { @@ -1643,7 +1666,6 @@ public class PGraphicsOpenGL extends PGraphics { } setDrawDefaults(); // TODO: look at using checkSettings() instead... - pgCurrent = this; drawing = true; report("bot beginDraw()"); @@ -1655,7 +1677,6 @@ public class PGraphicsOpenGL extends PGraphics { report("top endDraw()"); if (!drawing) { - PGraphics.showWarning(NO_BEGIN_DRAW_ERROR); return; } @@ -1663,7 +1684,7 @@ public class PGraphicsOpenGL extends PGraphics { flush(); if (PGL.SAVE_SURFACE_TO_PIXELS_HACK && - (!pgPrimary.initialized || parent.frameCount == 0)) { + (!getPrimaryPG().initialized || parent.frameCount == 0)) { // Smooth was disabled/enabled at some point during drawing. We save // the current contents of the back buffer (because the buffers haven't // been swapped yet) to the pixels array. The frameCount == 0 condition @@ -1679,12 +1700,10 @@ public class PGraphicsOpenGL extends PGraphics { endOffscreenDraw(); } - if (pgCurrent == pgPrimary) { - // Done with the main surface - pgCurrent = null; + if (primarySurface) { + setCurrentPG(null); } else { - // Done with an offscreen surface, going back to onscreen drawing. - pgCurrent = pgPrimary; + getPrimaryPG().setCurrentPG(getPrimaryPG()); } drawing = false; @@ -1698,6 +1717,31 @@ public class PGraphicsOpenGL extends PGraphics { } + protected PGraphicsOpenGL getPrimaryPG() { + if (primarySurface) { + return this; + } else { + return (PGraphicsOpenGL)parent.g; + } + } + + protected void setCurrentPG(PGraphicsOpenGL pg) { + currentPG = pg; + } + + protected PGraphicsOpenGL getCurrentPG() { + return currentPG; + } + + protected PGL getPrimaryPGL() { + if (primarySurface) { + return pgl; + } else { + return ((PGraphicsOpenGL)parent.g).pgl; + } + } + + @Override public PGL beginPGL() { flush(); @@ -1761,16 +1805,19 @@ public class PGraphicsOpenGL extends PGraphics { pgl.depthMask(true); } - currentFramebuffer.bind(); - pgl.drawBuffer(currentFramebuffer.getDefaultDrawBuffer()); + FrameBuffer fb = getCurrentFB(); + if (fb != null) { + getCurrentFB().bind(); + pgl.drawBuffer(getCurrentFB().getDefaultDrawBuffer()); + } } public void beginReadPixels() { - pgCurrent.beginPixelsOp(OP_READ); + beginPixelsOp(OP_READ); } public void endReadPixels() { - pgCurrent.endPixelsOp(); + endPixelsOp(); } protected void beginPixelsOp(int op) { @@ -1795,7 +1842,7 @@ public class PGraphicsOpenGL extends PGraphics { if (op == OP_READ) { if (offscreenMultisample) { // Making sure the offscreen FBO is up-to-date - multisampleFramebuffer.copy(offscreenFramebuffer, currentFramebuffer); + multisampleFramebuffer.copy(offscreenFramebuffer, getCurrentFB()); } // We always read the screen pixels from the color FBO. pixfb = offscreenFramebuffer; @@ -1808,7 +1855,7 @@ public class PGraphicsOpenGL extends PGraphics { } // Set the framebuffer where the pixel operation shall be carried out. - if (pixfb != currentFramebuffer) { + if (pixfb != getCurrentFB()) { pushFramebuffer(); setFramebuffer(pixfb); pixOpChangedFB = true; @@ -1816,9 +1863,9 @@ public class PGraphicsOpenGL extends PGraphics { // We read from/write to the draw buffer. if (op == OP_READ) { - pgl.readBuffer(currentFramebuffer.getDefaultDrawBuffer()); + pgl.readBuffer(getCurrentFB().getDefaultDrawBuffer()); } else if (op == OP_WRITE) { - pgl.drawBuffer(currentFramebuffer.getDefaultDrawBuffer()); + pgl.drawBuffer(getCurrentFB().getDefaultDrawBuffer()); } pixelsOp = op; @@ -1833,8 +1880,8 @@ public class PGraphicsOpenGL extends PGraphics { } // Restoring default read/draw buffer configuration. - pgl.readBuffer(currentFramebuffer.getDefaultReadBuffer()); - pgl.drawBuffer(currentFramebuffer.getDefaultDrawBuffer()); + pgl.readBuffer(getCurrentFB().getDefaultReadBuffer()); + pgl.drawBuffer(getCurrentFB().getDefaultDrawBuffer()); pixelsOp = OP_NONE; } @@ -2049,7 +2096,6 @@ public class PGraphicsOpenGL extends PGraphics { breakShape = false; defaultEdges = true; - textureImage0 = textureImage; // The superclass method is called to avoid an early flush. super.noTexture(); @@ -2226,13 +2272,13 @@ public class PGraphicsOpenGL extends PGraphics { tessellator.setInGeometry(inGeo); tessellator.setTessGeometry(tessGeo); tessellator.setFill(fill || textureImage != null); - tessellator.setTexCache(texCache, textureImage0, textureImage); + tessellator.setTexCache(texCache, textureImage); tessellator.setStroke(stroke); tessellator.setStrokeColor(strokeColor); tessellator.setStrokeWeight(strokeWeight); tessellator.setStrokeCap(strokeCap); tessellator.setStrokeJoin(strokeJoin); - tessellator.setRenderer(pgCurrent); + tessellator.setRenderer(this); tessellator.setTransform(modelview); tessellator.set3D(is3D()); @@ -2280,7 +2326,7 @@ public class PGraphicsOpenGL extends PGraphics { tessellator.setStrokeWeight(strokeWeight); tessellator.setStrokeCap(strokeCap); tessellator.setStrokeJoin(strokeJoin); - tessellator.setTexCache(texCache, textureImage0, textureImage); + tessellator.setTexCache(texCache, textureImage); tessellator.setTransform(modelview); tessellator.set3D(is3D()); @@ -2429,16 +2475,218 @@ public class PGraphicsOpenGL extends PGraphics { } + class Triangle { + int i0, i1, i2; + PImage tex; + float dist; + Triangle(int i0, int i1, int i2, PImage tex, float dist) { + this.i0 = i0; + this.i1 = i1; + this.i2 = i2; + this.tex = tex; + this.dist = dist; + } + } + // Adapted from Ben Van Citters code: + // http://openprocessing.org/sketch/100912 + Triangle[] sortedPolyTriangles = null; + int sortedTriangleCount = 0; + void sortTriangles() { + if (sortedPolyTriangles == null) { + sortedPolyTriangles = new Triangle[512]; + } + + float[] vertices = tessGeo.polyVertices; + short[] indices = tessGeo.polyIndices; + float[] src0 = {0, 0, 0, 0}; + float[] src1 = {0, 0, 0, 0}; + float[] src2 = {0, 0, 0, 0}; + float[] pt0 = {0, 0, 0, 0}; + float[] pt1 = {0, 0, 0, 0}; + float[] pt2 = {0, 0, 0, 0}; + + sortedTriangleCount = 0; + for (int i = 0; i < texCache.size; i++) { + PImage textureImage = texCache.getTextureImage(i); + int first = texCache.firstCache[i]; + int last = texCache.lastCache[i]; + IndexCache cache = tessGeo.polyIndexCache; + for (int n = first; n <= last; n++) { + int ioffset = n == first ? texCache.firstIndex[i] : + cache.indexOffset[n]; + int icount = n == last ? texCache.lastIndex[i] - ioffset + 1 : + cache.indexOffset[n] + cache.indexCount[n] - + ioffset; + int voffset = cache.vertexOffset[n]; + for (int tr = ioffset / 3; tr < (ioffset + icount) / 3; tr++) { + if (sortedPolyTriangles.length == sortedTriangleCount) { + // expand array + int newSize = sortedTriangleCount << 1; + Triangle[] temp = new Triangle[newSize]; + PApplet.arrayCopy(sortedPolyTriangles, 0, temp, 0, newSize); + sortedPolyTriangles = temp; + } + + int i0 = voffset + indices[3 * tr + 0]; + int i1 = voffset + indices[3 * tr + 1]; + int i2 = voffset + indices[3 * tr + 2]; + PApplet.arrayCopy(vertices, 4 * i0, src0, 0, 4); + PApplet.arrayCopy(vertices, 4 * i1, src1, 0, 4); + PApplet.arrayCopy(vertices, 4 * i2, src2, 0, 4); + modelview.mult(src0, pt0); + modelview.mult(src1, pt1); + modelview.mult(src2, pt2); + // add all three verts together and divide... could use another determination + // of the 'depth' of the triangle such as min or max vert dist... + float[] pos = new float[]{(pt0[X] + pt1[X] + pt2[X]) /3, + (pt0[Y] + pt1[Y] + pt2[Y]) /3, + (pt0[Z] + pt1[Z] + pt2[Z]) /3}; + + // pt0, pt1 and pt2 are in eye coordinates since they have been + // multiplied by the modelview matrix. + float d = PApplet.dist(0f, 0f, 0f, pos[0], pos[1], pos[2]); + + Triangle tri = new Triangle(i0, i1, i2, textureImage, d); + sortedPolyTriangles[sortedTriangleCount] = tri; + sortedTriangleCount++; + } + } + } + quickSortTris(0, sortedTriangleCount - 1); + } + + // an 'in-place' implementation of quick I whipped together late at night + // based off of the algorithm found on wikipedia: http://en.wikipedia.org/wiki/Quicksort + private void quickSortTris(int leftI, int rightI) { + if (leftI < rightI) { + int pivotIndex = (leftI + rightI)/2; + int newPivotIndex = partition(leftI,rightI,pivotIndex); + quickSortTris(leftI, newPivotIndex-1); + quickSortTris(newPivotIndex+1, rightI); + } + } + + //part of quicksort + private int partition(int leftIndex, int rightIndex, int pivotIndex) { + float pivotVal = sortedPolyTriangles[pivotIndex].dist; + swapTris(pivotIndex,rightIndex); + int storeIndex = leftIndex; + for(int i = leftIndex; i < rightIndex; i++) + { + if(sortedPolyTriangles[i].dist > pivotVal) + { + swapTris(i,storeIndex); + storeIndex++; + } + } + swapTris(rightIndex,storeIndex); + return storeIndex; + } + + //part of quicksort + private void swapTris(int a, int b) { + Triangle tmp = sortedPolyTriangles[a]; + sortedPolyTriangles[a] = sortedPolyTriangles[b]; + sortedPolyTriangles[b] = tmp; + } + + + void rawPolys() { raw.colorMode(RGB); raw.noStroke(); raw.beginShape(TRIANGLES); + sortTriangles(); + float[] vertices = tessGeo.polyVertices; int[] color = tessGeo.polyColors; float[] uv = tessGeo.polyTexCoords; short[] indices = tessGeo.polyIndices; + + sortTriangles(); + for (int i = 0; i < sortedTriangleCount; i++) { + Triangle tri = sortedPolyTriangles[i]; + int i0 = tri.i0; + int i1 = tri.i1; + int i2 = tri.i2; + PImage tex = tri.tex; + + float[] pt0 = {0, 0, 0, 0}; + float[] pt1 = {0, 0, 0, 0}; + float[] pt2 = {0, 0, 0, 0}; + int argb0 = PGL.nativeToJavaARGB(color[i0]); + int argb1 = PGL.nativeToJavaARGB(color[i1]); + int argb2 = PGL.nativeToJavaARGB(color[i2]); + + if (flushMode == FLUSH_CONTINUOUSLY) { + float[] src0 = {0, 0, 0, 0}; + float[] src1 = {0, 0, 0, 0}; + float[] src2 = {0, 0, 0, 0}; + PApplet.arrayCopy(vertices, 4 * i0, src0, 0, 4); + PApplet.arrayCopy(vertices, 4 * i1, src1, 0, 4); + PApplet.arrayCopy(vertices, 4 * i2, src2, 0, 4); + modelview.mult(src0, pt0); + modelview.mult(src1, pt1); + modelview.mult(src2, pt2); + } else { + PApplet.arrayCopy(vertices, 4 * i0, pt0, 0, 4); + PApplet.arrayCopy(vertices, 4 * i1, pt1, 0, 4); + PApplet.arrayCopy(vertices, 4 * i2, pt2, 0, 4); + } + + if (tex != null) { + raw.texture(tex); + if (raw.is3D()) { + raw.fill(argb0); + raw.vertex(pt0[X], pt0[Y], pt0[Z], uv[2 * i0 + 0], uv[2 * i0 + 1]); + raw.fill(argb1); + raw.vertex(pt1[X], pt1[Y], pt1[Z], uv[2 * i1 + 0], uv[2 * i1 + 1]); + raw.fill(argb2); + raw.vertex(pt2[X], pt2[Y], pt2[Z], uv[2 * i2 + 0], uv[2 * i2 + 1]); + } else if (raw.is2D()) { + float sx0 = screenXImpl(pt0[0], pt0[1], pt0[2], pt0[3]); + float sy0 = screenYImpl(pt0[0], pt0[1], pt0[2], pt0[3]); + float sx1 = screenXImpl(pt1[0], pt1[1], pt1[2], pt1[3]); + float sy1 = screenYImpl(pt1[0], pt1[1], pt1[2], pt1[3]); + float sx2 = screenXImpl(pt2[0], pt2[1], pt2[2], pt2[3]); + float sy2 = screenYImpl(pt2[0], pt2[1], pt2[2], pt2[3]); + raw.fill(argb0); + raw.vertex(sx0, sy0, uv[2 * i0 + 0], uv[2 * i0 + 1]); + raw.fill(argb1); + raw.vertex(sx1, sy1, uv[2 * i1 + 0], uv[2 * i1 + 1]); + raw.fill(argb1); + raw.vertex(sx2, sy2, uv[2 * i2 + 0], uv[2 * i2 + 1]); + } + } else { + if (raw.is3D()) { + raw.fill(argb0); + raw.vertex(pt0[X], pt0[Y], pt0[Z]); + raw.fill(argb1); + raw.vertex(pt1[X], pt1[Y], pt1[Z]); + raw.fill(argb2); + raw.vertex(pt2[X], pt2[Y], pt2[Z]); + } else if (raw.is2D()) { + float sx0 = screenXImpl(pt0[0], pt0[1], pt0[2], pt0[3]); + float sy0 = screenYImpl(pt0[0], pt0[1], pt0[2], pt0[3]); + float sx1 = screenXImpl(pt1[0], pt1[1], pt1[2], pt1[3]); + float sy1 = screenYImpl(pt1[0], pt1[1], pt1[2], pt1[3]); + float sx2 = screenXImpl(pt2[0], pt2[1], pt2[2], pt2[3]); + float sy2 = screenYImpl(pt2[0], pt2[1], pt2[2], pt2[3]); + raw.fill(argb0); + raw.vertex(sx0, sy0); + raw.fill(argb1); + raw.vertex(sx1, sy1); + raw.fill(argb2); + raw.vertex(sx2, sy2); + } + } + + } + + +/* for (int i = 0; i < texCache.size; i++) { PImage textureImage = texCache.getTextureImage(i); @@ -2530,6 +2778,7 @@ public class PGraphicsOpenGL extends PGraphics { } } } +*/ raw.endShape(); } @@ -2916,7 +3165,7 @@ public class PGraphicsOpenGL extends PGraphics { ambientColor, specularColor, emissiveColor, shininess); inGeo.setNormal(normalX, normalY, normalZ); inGeo.addRect(x1, y1, x2, y2, tl, tr, br, bl, stroke); - endShape(); + endShape(CLOSE); } @@ -3305,11 +3554,11 @@ public class PGraphicsOpenGL extends PGraphics { protected void textLineImpl(char buffer[], int start, int stop, float x, float y) { if (textMode == MODEL) { - textTex = pgPrimary.getFontTexture(textFont); + textTex = getFontTexture(textFont); if (textTex == null || textTex.contextIsOutdated()) { - textTex = new FontTexture(pgPrimary, textFont, is3D()); - pgPrimary.setFontTexture(textFont, textTex); + textTex = new FontTexture(this, textFont, is3D()); + setFontTexture(textFont, textTex); } textTex.begin(); @@ -3368,7 +3617,7 @@ public class PGraphicsOpenGL extends PGraphics { if (tinfo == null) { // Adding new glyph to the font texture. - tinfo = textTex.addToTexture(pgPrimary, glyph); + tinfo = textTex.addToTexture(this, glyph); } float high = glyph.height / (float) textFont.getSize(); @@ -4191,21 +4440,6 @@ public class PGraphicsOpenGL extends PGraphics { } - // Sets a camera for 2D rendering, which only involves centering - public void camera(float centerX, float centerY) { - modelview.reset(); - modelview.translate(-centerX, -centerY); - - modelviewInv.set(modelview); - modelviewInv.invert(); - - camera.set(modelview); - cameraInv.set(modelviewInv); - - updateProjmodelview(); - } - - /** * Print the current camera matrix. */ @@ -4254,6 +4488,9 @@ public class PGraphicsOpenGL extends PGraphics { public void ortho(float left, float right, float bottom, float top, float near, float far) { + // Translating the origin to (widht/2, height/2) since the matrix math + // below assumes the center of the screen to be (0, 0), but in Processing + // it is (w/2, h/2). left -= width/2f; right -= width/2f; bottom -= height/2f; @@ -5054,6 +5291,13 @@ public class PGraphicsOpenGL extends PGraphics { pgl.depthMask(true); } + // Code to use instead in order to fix + // https://github.com/processing/processing/issues/2296 +// if (!hints[DISABLE_DEPTH_MASK]) { +// pgl.clearDepth(1); +// pgl.clear(PGL.DEPTH_BUFFER_BIT); +// } + pgl.clearColor(backgroundR, backgroundG, backgroundB, backgroundA); pgl.clear(PGL.COLOR_BUFFER_BIT); if (0 < parent.frameCount) { @@ -5106,7 +5350,7 @@ public class PGraphicsOpenGL extends PGraphics { * Don't use this inside glBegin/glEnd otherwise it'll * throw an GL_INVALID_OPERATION error. */ - public void report(String where) { + protected void report(String where) { if (!hints[DISABLE_OPENGL_ERRORS]) { int err = pgl.getError(); if (err != 0) { @@ -5132,16 +5376,6 @@ public class PGraphicsOpenGL extends PGraphics { } - - ////////////////////////////////////////////////////////////// - - // PIMAGE METHODS - - // getImage - // setCache, getCache, removeCache - // isModified, setModified - - ////////////////////////////////////////////////////////////// // LOAD/UPDATE PIXELS @@ -5267,11 +5501,12 @@ public class PGraphicsOpenGL extends PGraphics { // non-multisampled FBO, texture is actually the color buffer used by the // color FBO, so with the copy operation we should be done updating the // (off)screen buffer. - // First, copy the pixels to the texture. We don't need to invert the // pixel copy because the texture will be drawn inverted. + int tw = PApplet.min(texture.glWidth - x, w); + int th = PApplet.min(texture.glHeight - y, h); pgl.copyToTexture(texture.glTarget, texture.glFormat, texture.glName, - x, y, w, h, nativePixelBuffer); + x, y, tw, th, nativePixelBuffer); beginPixelsOp(OP_WRITE); drawTexture(x, y, w, h); endPixelsOp(); @@ -5386,7 +5621,7 @@ public class PGraphicsOpenGL extends PGraphics { } else if (offscreenMultisample) { // 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. - multisampleFramebuffer.copy(offscreenFramebuffer, currentFramebuffer); + multisampleFramebuffer.copy(offscreenFramebuffer, getCurrentFB()); } if (needEndDraw) { @@ -5422,16 +5657,16 @@ public class PGraphicsOpenGL extends PGraphics { if (texture == null || texture.contextIsOutdated()) { Texture.Parameters params = new Texture.Parameters(ARGB, sampling, mipmap); - texture = new Texture(width, height, params); + texture = new Texture(this, width, height, params); texture.invertedY(true); texture.colorBuffer(true); - pgPrimary.setCache(this, texture); + setCache(this, texture); } } protected void createPTexture() { - ptexture = new Texture(width, height, texture.getParameters()); + ptexture = new Texture(this, width, height, texture.getParameters()); ptexture.invertedY(true); ptexture.colorBuffer(true); } @@ -5503,12 +5738,13 @@ public class PGraphicsOpenGL extends PGraphics { "the same size as the applet."); } - if (maskShader == null) { - maskShader = new PShader(parent, defTextureShaderVertURL, - maskShaderFragURL); + PGraphicsOpenGL ppg = getPrimaryPG(); + if (ppg.maskShader == null) { + ppg.maskShader = new PShader(parent, defTextureShaderVertURL, + maskShaderFragURL); } - maskShader.set("mask", alpha); - filter(maskShader); + ppg.maskShader.set("mask", alpha); + filter(ppg.maskShader); } @@ -5546,7 +5782,7 @@ public class PGraphicsOpenGL extends PGraphics { @Override public void filter(PShader shader) { - if (!shader.isPolyShader() || !shader.supportsTexturing()) { + if (!shader.isPolyShader()) { PGraphics.showWarning(INVALID_FILTER_SHADER_ERROR); return; } @@ -5560,7 +5796,7 @@ public class PGraphicsOpenGL extends PGraphics { loadTexture(); if (filterTexture == null || filterTexture.contextIsOutdated()) { - filterTexture = new Texture(texture.width, texture.height, + filterTexture = new Texture(this, texture.width, texture.height, texture.getParameters()); filterTexture.invertedY(true); filterImage = wrapTexture(filterTexture); @@ -5631,7 +5867,7 @@ public class PGraphicsOpenGL extends PGraphics { if (primarySurface) pgl.requestFBOLayer(); loadTexture(); if (filterTexture == null || filterTexture.contextIsOutdated()) { - filterTexture = new Texture(texture.width, texture.height, + filterTexture = new Texture(this, texture.width, texture.height, texture.getParameters()); filterTexture.invertedY(true); filterImage = wrapTexture(filterTexture); @@ -5654,12 +5890,33 @@ public class PGraphicsOpenGL extends PGraphics { flush(); // make sure that the screen contents are up to date. Texture tex = getTexture(src); + boolean invX = tex.invertedX(); + boolean invY = tex.invertedY(); + int scrX0, scrX1; + int scrY0, scrY1; + if (invX) { + scrX0 = dx + dw; + scrX1 = dx; + } else { + scrX0 = dx; + scrX1 = dx + dw; + } + if (invY) { + scrY0 = height - (dy + dh); + scrY1 = height - dy; + } else { + // Because drawTexture uses bottom-to-top orientation of Y axis. + scrY0 = height - dy; + scrY1 = height - (dy + dh); + } + pgl.drawTexture(tex.glTarget, tex.glName, tex.glWidth, tex.glHeight, width, height, sx, tex.height - (sy + sh), sx + sw, tex.height - sy, - dx, height - (dy + dh), - dx + dw, height - dy); + scrX0, scrY0, + scrX1, scrY1); + if (needEndDraw) { endDraw(); @@ -5790,7 +6047,15 @@ public class PGraphicsOpenGL extends PGraphics { * off the screen (or offscreen drawing surface). */ public Texture getTexture() { - loadTexture(); + return getTexture(true); + } + + + /** + * Not an approved function either, don't use it. + */ + public Texture getTexture(boolean load) { + if (load) loadTexture(); return texture; } @@ -5828,7 +6093,7 @@ public class PGraphicsOpenGL extends PGraphics { return null; } - Texture tex = (Texture)pgPrimary.getCache(img); + Texture tex = (Texture)getCache(img); if (tex == null || tex.contextIsOutdated()) { tex = addTexture(img); if (tex != null) { @@ -5881,8 +6146,8 @@ public class PGraphicsOpenGL extends PGraphics { if (img.parent == null) { img.parent = parent; } - Texture tex = new Texture(img.width, img.height, params); - pgPrimary.setCache(img, tex); + Texture tex = new Texture(this, img.width, img.height, params); + setCache(img, tex); return tex; } @@ -5916,7 +6181,7 @@ public class PGraphicsOpenGL extends PGraphics { img.width = tex.width; img.height = tex.height; img.format = ARGB; - pgPrimary.setCache(img, tex); + setCache(img, tex); return img; } @@ -5978,10 +6243,9 @@ public class PGraphicsOpenGL extends PGraphics { protected void initPrimary() { pgl.initSurface(quality); if (texture != null) { - pgPrimary.removeCache(this); + removeCache(this); texture = ptexture = null; } - pgPrimary = this; initialized = true; } @@ -5990,11 +6254,11 @@ public class PGraphicsOpenGL extends PGraphics { pgl.beginDraw(clearColorBuffer); if (drawFramebuffer == null) { - drawFramebuffer = new FrameBuffer(width, height, true); + drawFramebuffer = new FrameBuffer(this, width, height, true); } drawFramebuffer.setFBO(pgl.getDrawFramebuffer()); if (readFramebuffer == null) { - readFramebuffer = new FrameBuffer(width, height, true); + readFramebuffer = new FrameBuffer(this, width, height, true); } readFramebuffer.setFBO(pgl.getReadFramebuffer()); if (currentFramebuffer == null) { @@ -6015,7 +6279,7 @@ public class PGraphicsOpenGL extends PGraphics { protected void initOffscreen() { // Getting the context and capabilities from the main renderer. - loadTextureImpl(Texture.BILINEAR, false); + loadTextureImpl(textureSampling, false); // In case of reinitialization (for example, when the smooth level // is changed), we make sure that all the OpenGL resources associated @@ -6031,7 +6295,7 @@ public class PGraphicsOpenGL extends PGraphics { packedDepthStencilSupported; if (PGraphicsOpenGL.fboMultisampleSupported && 1 < quality) { multisampleFramebuffer = - new FrameBuffer(texture.glWidth, texture.glHeight, quality, 0, + new FrameBuffer(this, texture.glWidth, texture.glHeight, quality, 0, depthBits, stencilBits, packed, false); multisampleFramebuffer.clear(); @@ -6041,13 +6305,13 @@ public class PGraphicsOpenGL extends PGraphics { // to doesn't need depth and stencil buffers since they are part of the // multisampled framebuffer. offscreenFramebuffer = - new FrameBuffer(texture.glWidth, texture.glHeight, 1, 1, 0, 0, + new FrameBuffer(this, texture.glWidth, texture.glHeight, 1, 1, 0, 0, false, false); } else { quality = 0; offscreenFramebuffer = - new FrameBuffer(texture.glWidth, texture.glHeight, 1, 1, + new FrameBuffer(this, texture.glWidth, texture.glHeight, 1, 1, depthBits, stencilBits, packed, false); offscreenMultisample = false; } @@ -6109,13 +6373,13 @@ public class PGraphicsOpenGL extends PGraphics { // pgl.colorMask(true, true, true, true); if (offscreenMultisample) { - multisampleFramebuffer.copy(offscreenFramebuffer, currentFramebuffer); + multisampleFramebuffer.copy(offscreenFramebuffer, getCurrentFB()); } popFramebuffer(); texture.updateTexels(); // Mark all texels in screen texture as modified. - pgPrimary.restoreGL(); + getPrimaryPG().restoreGL(); } @@ -6168,7 +6432,7 @@ public class PGraphicsOpenGL extends PGraphics { pgl.disable(PGL.POLYGON_SMOOTH); if (sized) { - //reapplySettings(); +// reapplySettings(); // To avoid having garbage in the screen after a resize, // in the case background is not called in draw(). @@ -6390,6 +6654,7 @@ public class PGraphicsOpenGL extends PGraphics { protected PShader getPolyShader(boolean lit, boolean tex) { PShader shader; + PGraphicsOpenGL ppg = getPrimaryPG(); boolean useDefault = polyShader == null; if (polyShader != null) { polyShader.setRenderer(this); @@ -6399,23 +6664,23 @@ public class PGraphicsOpenGL extends PGraphics { if (lit) { if (tex) { if (useDefault || !polyShader.checkPolyType(PShader.TEXLIGHT)) { - if (defTexlightShader == null) { + if (ppg.defTexlightShader == null) { String[] vertSource = pgl.loadVertexShader(defTexlightShaderVertURL, 120); String[] fragSource = pgl.loadFragmentShader(defTextureShaderFragURL, 120); - defTexlightShader = new PShader(parent, vertSource, fragSource); + ppg.defTexlightShader = new PShader(parent, vertSource, fragSource); } - shader = defTexlightShader; + shader = ppg.defTexlightShader; } else { shader = polyShader; } } else { if (useDefault || !polyShader.checkPolyType(PShader.LIGHT)) { - if (defLightShader == null) { + if (ppg.defLightShader == null) { String[] vertSource = pgl.loadVertexShader(defLightShaderVertURL, 120); String[] fragSource = pgl.loadFragmentShader(defColorShaderFragURL, 120); - defLightShader = new PShader(parent, vertSource, fragSource); + ppg.defLightShader = new PShader(parent, vertSource, fragSource); } - shader = defLightShader; + shader = ppg.defLightShader; } else { shader = polyShader; } @@ -6428,23 +6693,23 @@ public class PGraphicsOpenGL extends PGraphics { if (tex) { if (useDefault || !polyShader.checkPolyType(PShader.TEXTURE)) { - if (defTextureShader == null) { + if (ppg.defTextureShader == null) { String[] vertSource = pgl.loadVertexShader(defTextureShaderVertURL, 120); String[] fragSource = pgl.loadFragmentShader(defTextureShaderFragURL, 120); - defTextureShader = new PShader(parent, vertSource, fragSource); + ppg.defTextureShader = new PShader(parent, vertSource, fragSource); } - shader = defTextureShader; + shader = ppg.defTextureShader; } else { shader = polyShader; } } else { if (useDefault || !polyShader.checkPolyType(PShader.COLOR)) { - if (defColorShader == null) { + if (ppg.defColorShader == null) { String[] vertSource = pgl.loadVertexShader(defColorShaderVertURL, 120); String[] fragSource = pgl.loadFragmentShader(defColorShaderFragURL, 120); - defColorShader = new PShader(parent, vertSource, fragSource); + ppg.defColorShader = new PShader(parent, vertSource, fragSource); } - shader = defColorShader; + shader = ppg.defColorShader; } else { shader = polyShader; } @@ -6461,13 +6726,14 @@ public class PGraphicsOpenGL extends PGraphics { protected PShader getLineShader() { PShader shader; + PGraphicsOpenGL ppg = getPrimaryPG(); if (lineShader == null) { - if (defLineShader == null) { + if (ppg.defLineShader == null) { String[] vertSource = pgl.loadVertexShader(defLineShaderVertURL, 120); String[] fragSource = pgl.loadFragmentShader(defLineShaderFragURL, 120); - defLineShader = new PShader(parent, vertSource, fragSource); + ppg.defLineShader = new PShader(parent, vertSource, fragSource); } - shader = defLineShader; + shader = ppg.defLineShader; } else { shader = lineShader; } @@ -6480,13 +6746,14 @@ public class PGraphicsOpenGL extends PGraphics { protected PShader getPointShader() { PShader shader; + PGraphicsOpenGL ppg = getPrimaryPG(); if (pointShader == null) { - if (defPointShader == null) { + if (ppg.defPointShader == null) { String[] vertSource = pgl.loadVertexShader(defPointShaderVertURL, 120); String[] fragSource = pgl.loadFragmentShader(defPointShaderFragURL, 120); - defPointShader = new PShader(parent, vertSource, fragSource); + ppg.defPointShader = new PShader(parent, vertSource, fragSource); } - shader = defPointShader; + shader = ppg.defPointShader; } else { shader = pointShader; } @@ -6524,14 +6791,15 @@ public class PGraphicsOpenGL extends PGraphics { } - static protected TexCache newTexCache() { - return new TexCache(); + static protected TexCache newTexCache(PGraphicsOpenGL pg) { + return new TexCache(pg); } // Holds an array of textures and the range of vertex // indices each texture applies to. static protected class TexCache { + PGraphicsOpenGL pg; int size; PImage[] textures; int[] firstIndex; @@ -6540,7 +6808,8 @@ public class PGraphicsOpenGL extends PGraphics { int[] lastCache; boolean hasTextures; - TexCache() { + TexCache(PGraphicsOpenGL pg) { + this.pg = pg; allocate(); } @@ -6576,7 +6845,7 @@ public class PGraphicsOpenGL extends PGraphics { Texture tex = null; if (img != null) { - tex = pgPrimary.getTexture(img); + tex = pg.getTexture(img); } return tex; @@ -7738,13 +8007,10 @@ public class PGraphicsOpenGL extends PGraphics { int startLUT = (int) (0.5f + (start / TWO_PI) * SINCOS_LENGTH); int stopLUT = (int) (0.5f + (stop / TWO_PI) * SINCOS_LENGTH); - if (fill) { - addVertex(centerX, centerY, VERTEX, true); - } + int idx0 = addVertex(centerX, centerY, VERTEX, true); int increment = 1; // what's a good algorithm? stopLUT - startLUT; - int idx0, pidx, idx; - idx0 = pidx = idx = 0; + int pidx = 0, idx = 0; for (int i = startLUT; i < stopLUT; i += increment) { int ii = i % SINCOS_LENGTH; // modulo won't make the value positive @@ -7758,11 +8024,10 @@ public class PGraphicsOpenGL extends PGraphics { addEdge(pidx, idx, i == startLUT, false); } else if (startLUT < i) { addEdge(pidx, idx, i == startLUT + 1, arcMode == 0 && - i == stopLUT - 1); + i == stopLUT - 1); } } - if (startLUT == i) idx0 = idx; pidx = idx; } // draw last point explicitly for accuracy @@ -9445,8 +9710,6 @@ public class PGraphicsOpenGL extends PGraphics { int beginPath; public Tessellator() { - callback = new TessellatorCallback(); - gluTess = pgl.createTessellator(callback); rawIndices = new int[512]; accurate2DStrokes = true; transform = null; @@ -9454,6 +9717,13 @@ public class PGraphicsOpenGL extends PGraphics { is3D = true; } + void initGluTess() { + if (gluTess == null) { + callback = new TessellatorCallback(); + gluTess = pg.pgl.createTessellator(callback); + } + } + void setInGeometry(InGeometry in) { this.in = in; @@ -9473,10 +9743,8 @@ public class PGraphicsOpenGL extends PGraphics { this.fill = fill; } - void setTexCache(TexCache texCache, PImage prevTexImage, - PImage newTexImage) { + void setTexCache(TexCache texCache, PImage newTexImage) { this.texCache = texCache; - this.prevTexImage = prevTexImage; this.newTexImage = newTexImage; } @@ -10941,7 +11209,6 @@ public class PGraphicsOpenGL extends PGraphics { } void beginNoTex() { - prevTexImage = newTexImage; newTexImage = null; setFirstTexIndex(tess.polyIndexCount, tess.polyIndexCache.size - 1); } @@ -10971,6 +11238,7 @@ public class PGraphicsOpenGL extends PGraphics { texCache.setLastIndex(lastIndex, lastCache); } } + prevTexImage = newTexImage; } // ----------------------------------------------------------------- @@ -10985,6 +11253,7 @@ public class PGraphicsOpenGL extends PGraphics { if (3 <= nInVert) { firstPolyIndexCache = -1; + initGluTess(); boolean clamp = clampPolygon(); callback.init(in.renderMode == RETAINED, false, calcNormals, clamp); @@ -11514,8 +11783,8 @@ public class PGraphicsOpenGL extends PGraphics { // Based on the opengl stroke hack described here: // http://wiki.processing.org/w/Stroke_attributes_in_OpenGL public void tessellateLinePath(LinePath path) { + initGluTess(); boolean clamp = clampLinePath(); - callback.init(in.renderMode == RETAINED, true, false, clamp); int cap = strokeCap == ROUND ? LinePath.CAP_ROUND : @@ -11747,7 +12016,7 @@ public class PGraphicsOpenGL extends PGraphics { } public void error(int errnum) { - String estring = pgl.tessError(errnum); + String estring = pg.pgl.tessError(errnum); PGraphics.showWarning(TESSELLATION_ERROR, estring); } diff --git a/core/src/processing/opengl/PJOGL.java b/core/src/processing/opengl/PJOGL.java index 597d63ae3..994ff21d1 100644 --- a/core/src/processing/opengl/PJOGL.java +++ b/core/src/processing/opengl/PJOGL.java @@ -66,16 +66,16 @@ public class PJOGL extends PGL { // Public members to access the underlying GL objects and context /** Basic GL functionality, common to all profiles */ - public static GL gl; + public GL gl; /** GLU interface **/ - public static GLU glu; + public GLU glu; /** The rendering context (holds rendering state info) */ - public static GLContext context; + public GLContext context; /** The canvas where OpenGL rendering takes place */ - public static Canvas canvas; + public Canvas canvas; /** Selected GL profile */ public static GLProfile profile; @@ -132,32 +132,38 @@ public class PJOGL extends PGL { // Protected JOGL-specific objects needed to access the GL profiles /** The capabilities of the OpenGL rendering surface */ - protected static GLCapabilitiesImmutable capabilities; + protected GLCapabilitiesImmutable capabilities; /** The rendering surface */ - protected static GLDrawable drawable; + protected GLDrawable drawable; /** GLES2 functionality (shaders, etc) */ - protected static GL2ES2 gl2; + protected GL2ES2 gl2; /** GL3 interface */ - protected static GL2GL3 gl3; + protected GL2GL3 gl3; /** GL2 desktop functionality (blit framebuffer, map buffer range, * multisampled renerbuffers) */ - protected static GL2 gl2x; + protected GL2 gl2x; /** The AWT-OpenGL canvas */ - protected static GLCanvas canvasAWT; + protected GLCanvas canvasAWT; - /** The NEWT-OpenGL canvas */ - protected static NewtCanvasAWT canvasNEWT; + /** The shared AWT-OpenGL canvas */ + protected static GLCanvas sharedCanvasAWT; /** The NEWT window */ - protected static GLWindow window; + protected GLWindow windowNEWT; + + /** The shared NEWT window */ + protected static GLWindow sharedWindowNEWT; + + /** The NEWT-OpenGL canvas */ + protected NewtCanvasAWT canvasNEWT; /** The listener that fires the frame rendering in Processing */ - protected static PGLListener listener; + protected PGLListener listener; /** This countdown latch is used to maintain the synchronization between * Processing's drawing thread and JOGL's rendering thread */ @@ -173,13 +179,13 @@ public class PJOGL extends PGL { // JOGL's FBO-layer /** Back (== draw, current frame) buffer */ - protected static FBObject backFBO; + protected FBObject backFBO; /** Sink buffer, used in the multisampled case */ - protected static FBObject sinkFBO; + protected FBObject sinkFBO; /** Front (== read, previous frame) buffer */ - protected static FBObject frontFBO; - protected static FBObject.TextureAttachment backTexAttach; - protected static FBObject.TextureAttachment frontTexAttach; + protected FBObject frontFBO; + protected FBObject.TextureAttachment backTexAttach; + protected FBObject.TextureAttachment frontTexAttach; protected boolean changedFrontTex = false; protected boolean changedBackTex = false; @@ -213,6 +219,12 @@ public class PJOGL extends PGL { } + @Override + public Canvas getCanvas() { + return canvas; + } + + @Override protected void setFps(float fps) { if (!setFps || targetFps != fps) { @@ -264,14 +276,18 @@ public class PJOGL extends PGL { tex2DFragShaderSource = convertFragmentSource(tex2DFragShaderSource, 120, 150); texRectFragShaderSource = convertFragmentSource(texRectFragShaderSource, 120, 150); } - } else { + } + + if (canvasAWT != null && canvasNEWT != null) { // Restarting... if (canvasAWT != null) { + sharedCanvasAWT = null; canvasAWT.removeGLEventListener(listener); pg.parent.removeListeners(canvasAWT); pg.parent.remove(canvasAWT); } else if (canvasNEWT != null) { - window.removeGLEventListener(listener); + sharedWindowNEWT = null; + windowNEWT.removeGLEventListener(listener); pg.parent.remove(canvasNEWT); } sinkFBO = backFBO = frontFBO = null; @@ -318,6 +334,11 @@ public class PJOGL extends PGL { if (WINDOW_TOOLKIT == AWT) { canvasAWT = new GLCanvas(caps); + if (sharedCanvasAWT == null) { + sharedCanvasAWT = canvasAWT; + } else { + canvasAWT.setSharedAutoDrawable(sharedCanvasAWT); + } canvasAWT.setBounds(0, 0, pg.width, pg.height); canvasAWT.setBackground(new Color(pg.backgroundColor, true)); canvasAWT.setFocusable(true); @@ -329,8 +350,13 @@ public class PJOGL extends PGL { canvas = canvasAWT; canvasNEWT = null; } else if (WINDOW_TOOLKIT == NEWT) { - window = GLWindow.create(caps); - canvasNEWT = new NewtCanvasAWT(window); + windowNEWT = GLWindow.create(caps); + if (sharedWindowNEWT == null) { + sharedWindowNEWT = windowNEWT; + } else { + windowNEWT.setSharedAutoDrawable(sharedWindowNEWT); + } + canvasNEWT = new NewtCanvasAWT(windowNEWT); canvasNEWT.setBounds(0, 0, pg.width, pg.height); canvasNEWT.setBackground(new Color(pg.backgroundColor, true)); canvasNEWT.setFocusable(true); @@ -372,18 +398,18 @@ public class PJOGL extends PGL { } else if (WINDOW_TOOLKIT == NEWT) { if (EVENTS_TOOLKIT == NEWT) { NEWTMouseListener mouseListener = new NEWTMouseListener(); - window.addMouseListener(mouseListener); + windowNEWT.addMouseListener(mouseListener); NEWTKeyListener keyListener = new NEWTKeyListener(); - window.addKeyListener(keyListener); + windowNEWT.addKeyListener(keyListener); NEWTWindowListener winListener = new NEWTWindowListener(); - window.addWindowListener(winListener); + windowNEWT.addWindowListener(winListener); } else if (EVENTS_TOOLKIT == AWT) { pg.parent.removeListeners(canvasNEWT); pg.parent.addListeners(canvasNEWT); } listener = new PGLListener(); - window.addGLEventListener(listener); + windowNEWT.addGLEventListener(listener); } if (canvas != null) { @@ -400,7 +426,7 @@ public class PJOGL extends PGL { canvasAWT.removeGLEventListener(listener); pg.parent.removeListeners(canvasAWT); } else if (canvasNEWT != null) { - window.removeGLEventListener(listener); + windowNEWT.removeGLEventListener(listener); } GLProfile.shutdown(); } @@ -484,7 +510,7 @@ public class PJOGL extends PGL { protected Texture wrapBackTexture(Texture texture) { if (texture == null || changedBackTex) { if (USE_JOGL_FBOLAYER) { - texture = new Texture(); + texture = new Texture(pg); texture.init(pg.width, pg.height, backTexAttach.getName(), TEXTURE_2D, RGBA, backTexAttach.getWidth(), backTexAttach.getHeight(), @@ -511,7 +537,7 @@ public class PJOGL extends PGL { protected Texture wrapFrontTexture(Texture texture) { if (texture == null || changedFrontTex) { if (USE_JOGL_FBOLAYER) { - texture = new Texture(); + texture = new Texture(pg); texture.init(pg.width, pg.height, backTexAttach.getName(), TEXTURE_2D, RGBA, frontTexAttach.getWidth(), frontTexAttach.getHeight(), @@ -606,6 +632,42 @@ public class PJOGL extends PGL { } + @Override + protected void getGL(PGL pgl) { + PJOGL pjogl = (PJOGL)pgl; + + this.drawable = pjogl.drawable; + this.context = pjogl.context; + this.glContext = pjogl.glContext; + this.glThread = pjogl.glThread; + + this.gl = pjogl.gl; + this.gl2 = pjogl.gl2; + this.gl2x = pjogl.gl2x; + this.gl3 = pjogl.gl3; + } + + + protected void getGL(GLAutoDrawable glDrawable) { + context = glDrawable.getContext(); + glContext = context.hashCode(); + glThread = Thread.currentThread(); + + gl = context.getGL(); + gl2 = gl.getGL2ES2(); + try { + gl2x = gl.getGL2(); + } catch (javax.media.opengl.GLException e) { + gl2x = null; + } + try { + gl3 = gl.getGL2GL3(); + } catch (javax.media.opengl.GLException e) { + gl3 = null; + } + } + + @Override protected boolean canDraw() { return pg.initialized && pg.parent.isDisplayable(); @@ -625,7 +687,7 @@ public class PJOGL extends PGL { if (WINDOW_TOOLKIT == AWT) { canvasAWT.display(); } else if (WINDOW_TOOLKIT == NEWT) { - window.display(); + windowNEWT.display(); } try { drawLatch.await(DRAW_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); @@ -653,7 +715,7 @@ public class PJOGL extends PGL { if (WINDOW_TOOLKIT == AWT) { canvasAWT.swapBuffers(); } else if (WINDOW_TOOLKIT == NEWT) { - window.swapBuffers(); + windowNEWT.swapBuffers(); } } @@ -778,6 +840,8 @@ public class PJOGL extends PGL { frontTexAttach = (FBObject.TextureAttachment)frontFBO. getColorbuffer(0); } else { + changedFrontTex = changedBackTex = sinkFBO == null; + // Default setting (to save resources): the front and back // textures are the same. sinkFBO = backFBO.getSamplingSinkFBO(); @@ -785,7 +849,6 @@ public class PJOGL extends PGL { getColorbuffer(0); frontTexAttach = backTexAttach; } - } else { // w/out multisampling, rendering is done on the back buffer. frontFBO = fboDrawable.getFBObject(GL.GL_FRONT); @@ -826,25 +889,25 @@ public class PJOGL extends PGL { //getGL(glDrawable); } - private void getGL(GLAutoDrawable glDrawable) { - drawable = glDrawable; - context = glDrawable.getContext(); - glContext = context.hashCode(); - glThread = Thread.currentThread(); - - gl = context.getGL(); - gl2 = gl.getGL2ES2(); - try { - gl2x = gl.getGL2(); - } catch (javax.media.opengl.GLException e) { - gl2x = null; - } - try { - gl3 = gl.getGL2GL3(); - } catch (javax.media.opengl.GLException e) { - gl3 = null; - } - } +// private void getGL(GLAutoDrawable glDrawable) { +// drawable = glDrawable; +// context = glDrawable.getContext(); +// glContext = context.hashCode(); +// glThread = Thread.currentThread(); +// +// gl = context.getGL(); +// gl2 = gl.getGL2ES2(); +// try { +// gl2x = gl.getGL2(); +// } catch (javax.media.opengl.GLException e) { +// gl2x = null; +// } +// try { +// gl3 = gl.getGL2GL3(); +// } catch (javax.media.opengl.GLException e) { +// gl3 = null; +// } +// } } protected void nativeMouseEvent(com.jogamp.newt.event.MouseEvent nativeEvent, @@ -913,6 +976,9 @@ public class PJOGL extends PGL { } protected class NEWTWindowListener implements com.jogamp.newt.event.WindowListener { + public NEWTWindowListener() { + super(); + } @Override public void windowGainedFocus(com.jogamp.newt.event.WindowEvent arg0) { pg.parent.focusGained(null); @@ -945,6 +1011,9 @@ public class PJOGL extends PGL { // NEWT mouse listener protected class NEWTMouseListener extends com.jogamp.newt.event.MouseAdapter { + public NEWTMouseListener() { + super(); + } @Override public void mousePressed(com.jogamp.newt.event.MouseEvent e) { nativeMouseEvent(e, MouseEvent.PRESS); @@ -981,6 +1050,9 @@ public class PJOGL extends PGL { // NEWT key listener protected class NEWTKeyListener extends com.jogamp.newt.event.KeyAdapter { + public NEWTKeyListener() { + super(); + } @Override public void keyPressed(com.jogamp.newt.event.KeyEvent e) { nativeKeyEvent(e, KeyEvent.PRESS); @@ -1106,41 +1178,6 @@ public class PJOGL extends PGL { } - @Override - protected String[] convertFragmentSource(String[] fragSrc0, - int version0, int version1) { - String[] fragSrc = new String[fragSrc0.length + 2]; - fragSrc[0] = "#version 150"; - fragSrc[1] = "out vec4 fragColor;"; - for (int i = 0; i < fragSrc0.length; i++) { - String line = fragSrc0[i]; - line = line.replace("varying", "in"); - line = line.replace("attribute", "in"); - line = line.replace("gl_FragColor", "fragColor"); - line = line.replace("texture", "texMap"); - line = line.replace("texMap2D(", "texture("); - line = line.replace("texMap2DRect(", "texture("); - fragSrc[i + 2] = line; - } - return fragSrc; - } - - - @Override - protected String[] convertVertexSource(String[] vertSrc0, - int version0, int version1) { - String[] vertSrc = new String[vertSrc0.length + 1]; - vertSrc[0] = "#version 150"; - for (int i = 0; i < vertSrc0.length; i++) { - String line = vertSrc0[i]; - line = line.replace("attribute", "in"); - line = line.replace("varying", "out"); - vertSrc[i + 1] = line; - } - return vertSrc; - } - - /////////////////////////////////////////////////////////// // Tessellator diff --git a/core/src/processing/opengl/PShader.java b/core/src/processing/opengl/PShader.java index 87c8e311b..97dbbc891 100644 --- a/core/src/processing/opengl/PShader.java +++ b/core/src/processing/opengl/PShader.java @@ -77,7 +77,8 @@ public class PShader implements PConstants { // be called by different renderers within a single application // (the one corresponding to the main surface, or other offscreen // renderers). - protected PGraphicsOpenGL pg; + protected PGraphicsOpenGL primaryPG; + protected PGraphicsOpenGL currentPG; protected PGL pgl; protected int context; // The context that created this shader. @@ -108,6 +109,9 @@ public class PShader implements PConstants { protected IntBuffer intBuffer; protected FloatBuffer floatBuffer; + protected boolean loadedAttributes = false; + protected boolean loadedUniforms = false; + // Uniforms common to all shader types protected int transformMatLoc; protected int modelviewMatLoc; @@ -177,7 +181,8 @@ public class PShader implements PConstants { public PShader(PApplet parent) { this(); this.parent = parent; - pgl = PGraphicsOpenGL.pgl; + primaryPG = (PGraphicsOpenGL)parent.g; + pgl = primaryPG.pgl; context = pgl.createEmptyContext(); } @@ -192,7 +197,8 @@ public class PShader implements PConstants { */ public PShader(PApplet parent, String vertFilename, String fragFilename) { this.parent = parent; - pgl = PGraphicsOpenGL.pgl; + primaryPG = (PGraphicsOpenGL)parent.g; + pgl = primaryPG.pgl; this.vertexURL = null; this.fragmentURL = null; @@ -230,7 +236,8 @@ public class PShader implements PConstants { */ public PShader(PApplet parent, URL vertURL, URL fragURL) { this.parent = parent; - pgl = PGraphicsOpenGL.pgl; + primaryPG = (PGraphicsOpenGL)parent.g; + pgl = primaryPG.pgl; this.vertexURL = vertURL; this.fragmentURL = fragURL; @@ -263,7 +270,8 @@ public class PShader implements PConstants { public PShader(PApplet parent, String[] vertSource, String[] fragSource) { this.parent = parent; - pgl = PGraphicsOpenGL.pgl; + primaryPG = (PGraphicsOpenGL)parent.g; + pgl = primaryPG.pgl; this.vertexURL = null; this.fragmentURL = null; @@ -825,7 +833,7 @@ public class PShader implements PConstants { pgl.uniformMatrix4fv(loc, 1, false, floatBuffer); } else if (val.type == UniformValue.SAMPLER2D) { PImage img = (PImage)val.value; - Texture tex = PGraphicsOpenGL.pgPrimary.getTexture(img); + Texture tex = currentPG.getTexture(img); if (textures == null) textures = new HashMap(); textures.put(loc, tex); @@ -891,7 +899,7 @@ public class PShader implements PConstants { protected void init() { if (glProgram == 0 || contextIsOutdated()) { context = pgl.getCurrentContext(); - glProgram = PGraphicsOpenGL.createGLSLProgramObject(context); + glProgram = PGraphicsOpenGL.createGLSLProgramObject(context, pgl); boolean vertRes = true; if (hasVertexShader()) { @@ -961,7 +969,7 @@ public class PShader implements PConstants { * @param shaderSource a string containing the shader's code */ protected boolean compileVertexShader() { - glVertex = PGraphicsOpenGL.createGLSLVertShaderObject(context); + glVertex = PGraphicsOpenGL.createGLSLVertShaderObject(context, pgl); pgl.shaderSource(glVertex, PApplet.join(vertexShaderSource, "\n")); pgl.compileShader(glVertex); @@ -982,7 +990,7 @@ public class PShader implements PConstants { * @param shaderSource a string containing the shader's code */ protected boolean compileFragmentShader() { - glFragment = PGraphicsOpenGL.createGLSLFragShaderObject(context); + glFragment = PGraphicsOpenGL.createGLSLFragShaderObject(context, pgl); pgl.shaderSource(glFragment, PApplet.join(fragmentShaderSource, "\n")); pgl.compileShader(glFragment); @@ -1001,15 +1009,15 @@ public class PShader implements PConstants { protected void dispose() { if (glVertex != 0) { - PGraphicsOpenGL.deleteGLSLVertShaderObject(glVertex, context); + PGraphicsOpenGL.deleteGLSLVertShaderObject(glVertex, context, pgl); glVertex = 0; } if (glFragment != 0) { - PGraphicsOpenGL.deleteGLSLFragShaderObject(glFragment, context); + PGraphicsOpenGL.deleteGLSLFragShaderObject(glFragment, context, pgl); glFragment = 0; } if (glProgram != 0) { - PGraphicsOpenGL.deleteGLSLProgramObject(glProgram, context); + PGraphicsOpenGL.deleteGLSLProgramObject(glProgram, context, pgl); glProgram = 0; } } @@ -1048,30 +1056,37 @@ public class PShader implements PConstants { // // Processing specific + protected int getType() { return type; } + protected void setType(int type) { this.type = type; } + protected boolean hasType() { return POINT <= type && type <= TEXLIGHT; } + protected boolean isPointShader() { return type == POINT; } + protected boolean isLineShader() { return type == LINE; } + protected boolean isPolyShader() { return POLY <= type && type <= TEXLIGHT; } + protected boolean checkPolyType(int type) { if (getType() == PShader.POLY) return true; @@ -1091,15 +1106,17 @@ public class PShader implements PConstants { return true; } + protected int getLastTexUnit() { return texUnits == null ? -1 : texUnits.size() - 1; } + protected void setRenderer(PGraphicsOpenGL pg) { - this.pg = pg; + this.currentPG = pg; } - boolean loadedAttributes = false; + protected void loadAttributes() { if (loadedAttributes) return; @@ -1126,7 +1143,6 @@ public class PShader implements PConstants { } - boolean loadedUniforms = false; protected void loadUniforms() { if (loadedUniforms) return; transformMatLoc = getUniformLoc("transform"); @@ -1171,25 +1187,25 @@ public class PShader implements PConstants { protected void setCommonUniforms() { if (-1 < transformMatLoc) { - pg.updateGLProjmodelview(); - setUniformMatrix(transformMatLoc, pg.glProjmodelview); + currentPG.updateGLProjmodelview(); + setUniformMatrix(transformMatLoc, currentPG.glProjmodelview); } if (-1 < modelviewMatLoc) { - pg.updateGLModelview(); - setUniformMatrix(modelviewMatLoc, pg.glModelview); + currentPG.updateGLModelview(); + setUniformMatrix(modelviewMatLoc, currentPG.glModelview); } if (-1 < projectionMatLoc) { - pg.updateGLProjection(); - setUniformMatrix(projectionMatLoc, pg.glProjection); + currentPG.updateGLProjection(); + setUniformMatrix(projectionMatLoc, currentPG.glProjection); } if (-1 < viewportLoc) { - float x = pg.viewport.get(0); - float y = pg.viewport.get(1); - float w = pg.viewport.get(2); - float h = pg.viewport.get(3); + float x = currentPG.viewport.get(0); + float y = currentPG.viewport.get(1); + float w = currentPG.viewport.get(2); + float h = currentPG.viewport.get(3); setUniformValue(viewportLoc, x, y, w, h); } @@ -1197,15 +1213,15 @@ public class PShader implements PConstants { bufferUnit = getLastTexUnit() + 1; setUniformValue(bufferLoc, bufferUnit); pgl.activeTexture(PGL.TEXTURE0 + bufferUnit); - pg.bindFrontTexture(); + currentPG.bindFrontTexture(); } else { bufferUnit = -1; } } protected void bindTyped() { - if (pg == null) { - setRenderer(PGraphicsOpenGL.pgCurrent); + if (currentPG == null) { + setRenderer(primaryPG.getCurrentPG()); loadAttributes(); loadUniforms(); } @@ -1217,8 +1233,8 @@ public class PShader implements PConstants { if (-1 < normalLoc) pgl.enableVertexAttribArray(normalLoc); if (-1 < normalMatLoc) { - pg.updateGLNormal(); - setUniformMatrix(normalMatLoc, pg.glNormal); + currentPG.updateGLNormal(); + setUniformMatrix(normalMatLoc, currentPG.glNormal); } if (-1 < ambientLoc) pgl.enableVertexAttribArray(ambientLoc); @@ -1226,17 +1242,17 @@ public class PShader implements PConstants { if (-1 < emissiveLoc) pgl.enableVertexAttribArray(emissiveLoc); if (-1 < shininessLoc) pgl.enableVertexAttribArray(shininessLoc); - int count = pg.lightCount; + int count = currentPG.lightCount; setUniformValue(lightCountLoc, count); if (0 < count) { - setUniformVector(lightPositionLoc, pg.lightPosition, 4, count); - setUniformVector(lightNormalLoc, pg.lightNormal, 3, count); - setUniformVector(lightAmbientLoc, pg.lightAmbient, 3, count); - setUniformVector(lightDiffuseLoc, pg.lightDiffuse, 3, count); - setUniformVector(lightSpecularLoc, pg.lightSpecular, 3, count); - setUniformVector(lightFalloffLoc, pg.lightFalloffCoefficients, + setUniformVector(lightPositionLoc, currentPG.lightPosition, 4, count); + setUniformVector(lightNormalLoc, currentPG.lightNormal, 3, count); + setUniformVector(lightAmbientLoc, currentPG.lightAmbient, 3, count); + setUniformVector(lightDiffuseLoc, currentPG.lightDiffuse, 3, count); + setUniformVector(lightSpecularLoc, currentPG.lightSpecular, 3, count); + setUniformVector(lightFalloffLoc, currentPG.lightFalloffCoefficients, 3, count); - setUniformVector(lightSpotLoc, pg.lightSpotParameters, 2, count); + setUniformVector(lightSpotLoc, currentPG.lightSpotParameters, 2, count); } if (-1 < directionLoc) pgl.enableVertexAttribArray(directionLoc); @@ -1244,8 +1260,8 @@ public class PShader implements PConstants { if (-1 < offsetLoc) pgl.enableVertexAttribArray(offsetLoc); if (-1 < perspectiveLoc) { - if (pg.getHint(ENABLE_STROKE_PERSPECTIVE) && - pg.nonOrthoProjection()) { + if (currentPG.getHint(ENABLE_STROKE_PERSPECTIVE) && + currentPG.nonOrthoProjection()) { setUniformValue(perspectiveLoc, 1); } else { setUniformValue(perspectiveLoc, 0); @@ -1253,11 +1269,11 @@ public class PShader implements PConstants { } if (-1 < scaleLoc) { - if (pg.getHint(DISABLE_OPTIMIZED_STROKE)) { + if (currentPG.getHint(DISABLE_OPTIMIZED_STROKE)) { setUniformValue(scaleLoc, 1.0f, 1.0f, 1.0f); } else { float f = PGL.STROKE_DISPLACEMENT; - if (pg.orthoProjection()) { + if (currentPG.orthoProjection()) { setUniformValue(scaleLoc, 1, 1, f); } else { setUniformValue(scaleLoc, f, f, f); @@ -1291,7 +1307,7 @@ public class PShader implements PConstants { if (-1 < bufferLoc) { pgl.requestFBOLayer(); pgl.activeTexture(PGL.TEXTURE0 + bufferUnit); - pg.unbindFrontTexture(); + currentPG.unbindFrontTexture(); pgl.activeTexture(PGL.TEXTURE0); } diff --git a/core/src/processing/opengl/PShapeOpenGL.java b/core/src/processing/opengl/PShapeOpenGL.java index 704bbdea5..6db7fddca 100644 --- a/core/src/processing/opengl/PShapeOpenGL.java +++ b/core/src/processing/opengl/PShapeOpenGL.java @@ -290,9 +290,9 @@ public class PShapeOpenGL extends PShape { } - public PShapeOpenGL(PApplet parent, int family) { - pg = PGraphicsOpenGL.pgCurrent; - pgl = PGraphicsOpenGL.pgl; + public PShapeOpenGL(PGraphicsOpenGL pg, int family) { + this.pg = pg; + pgl = pg.pgl; context = pgl.createEmptyContext(); glPolyVertex = 0; @@ -568,20 +568,19 @@ public class PShapeOpenGL extends PShape { // Shape creation (temporary hack) - public static PShapeOpenGL createShape3D(PApplet parent, PShape src) { + public static PShapeOpenGL createShape3D(PGraphicsOpenGL pg, PShape src) { PShapeOpenGL dest = null; if (src.getFamily() == GROUP) { - dest = PGraphics3D.createShapeImpl(parent, GROUP); - copyGroup3D(parent, src, dest); + dest = PGraphics3D.createShapeImpl(pg, GROUP); + copyGroup3D(pg, src, dest); } else if (src.getFamily() == PRIMITIVE) { - dest = PGraphics3D.createShapeImpl(parent, src.getKind(), - src.getParams()); + dest = PGraphics3D.createShapeImpl(pg, src.getKind(), src.getParams()); PShape.copyPrimitive(src, dest); } else if (src.getFamily() == GEOMETRY) { - dest = PGraphics3D.createShapeImpl(parent, PShape.GEOMETRY); + dest = PGraphics3D.createShapeImpl(pg, PShape.GEOMETRY); PShape.copyGeometry(src, dest); } else if (src.getFamily() == PATH) { - dest = PGraphics3D.createShapeImpl(parent, PATH); + dest = PGraphics3D.createShapeImpl(pg, PATH); PShape.copyPath(src, dest); } dest.setName(src.getName()); @@ -592,20 +591,19 @@ public class PShapeOpenGL extends PShape { } - static public PShapeOpenGL createShape2D(PApplet parent, PShape src) { + static public PShapeOpenGL createShape2D(PGraphicsOpenGL pg, PShape src) { PShapeOpenGL dest = null; if (src.getFamily() == GROUP) { - dest = PGraphics2D.createShapeImpl(parent, GROUP); - copyGroup2D(parent, src, dest); + dest = PGraphics2D.createShapeImpl(pg, GROUP); + copyGroup2D(pg, src, dest); } else if (src.getFamily() == PRIMITIVE) { - dest = PGraphics2D.createShapeImpl(parent, src.getKind(), - src.getParams()); + dest = PGraphics2D.createShapeImpl(pg, src.getKind(), src.getParams()); PShape.copyPrimitive(src, dest); } else if (src.getFamily() == GEOMETRY) { - dest = PGraphics2D.createShapeImpl(parent, PShape.GEOMETRY); + dest = PGraphics2D.createShapeImpl(pg, PShape.GEOMETRY); PShape.copyGeometry(src, dest); } else if (src.getFamily() == PATH) { - dest = PGraphics2D.createShapeImpl(parent, PATH); + dest = PGraphics2D.createShapeImpl(pg, PATH); PShape.copyPath(src, dest); } dest.setName(src.getName()); @@ -615,25 +613,25 @@ public class PShapeOpenGL extends PShape { } - static public void copyGroup3D(PApplet parent, PShape src, PShape dest) { + static public void copyGroup3D(PGraphicsOpenGL pg, PShape src, PShape dest) { copyMatrix(src, dest); copyStyles(src, dest); copyImage(src, dest); for (int i = 0; i < src.getChildCount(); i++) { - PShape c = createShape3D(parent, src.getChild(i)); + PShape c = createShape3D(pg, src.getChild(i)); dest.addChild(c); } } - static public void copyGroup2D(PApplet parent, PShape src, PShape dest) { + static public void copyGroup2D(PGraphicsOpenGL pg, PShape src, PShape dest) { copyMatrix(src, dest); copyStyles(src, dest); copyImage(src, dest); for (int i = 0; i < src.getChildCount(); i++) { - PShape c = createShape2D(parent, src.getChild(i)); + PShape c = createShape2D(pg, src.getChild(i)); dest.addChild(c); } } @@ -2413,9 +2411,9 @@ public class PShapeOpenGL extends PShape { PShape tess; if (is3D()) { - tess = PGraphics3D.createShapeImpl(pg.parent, PShape.GEOMETRY); + tess = PGraphics3D.createShapeImpl(pg, PShape.GEOMETRY); } else if (is2D()) { - tess = PGraphics2D.createShapeImpl(pg.parent, PShape.GEOMETRY); + tess = PGraphics2D.createShapeImpl(pg, PShape.GEOMETRY); } else { PGraphics.showWarning("This shape is not either 2D or 3D!"); return null; @@ -2643,7 +2641,7 @@ public class PShapeOpenGL extends PShape { tessellator.setInGeometry(inGeo); tessellator.setTessGeometry(tessGeo); tessellator.setFill(fill || image != null); - tessellator.setTexCache(null, null, null); + tessellator.setTexCache(null, null); tessellator.setStroke(stroke); tessellator.setStrokeColor(strokeColor); tessellator.setStrokeWeight(strokeWeight); @@ -3651,56 +3649,56 @@ public class PShapeOpenGL extends PShape { tessGeo.updatePolyVerticesBuffer(); if (glPolyVertex == 0) - glPolyVertex = PGraphicsOpenGL.createVertexBufferObject(context); + glPolyVertex = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyVertex); pgl.bufferData(PGL.ARRAY_BUFFER, 4 * sizef, tessGeo.polyVerticesBuffer, PGL.STATIC_DRAW); tessGeo.updatePolyColorsBuffer(); if (glPolyColor == 0) - glPolyColor = PGraphicsOpenGL.createVertexBufferObject(context); + glPolyColor = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyColor); pgl.bufferData(PGL.ARRAY_BUFFER, sizei, tessGeo.polyColorsBuffer, PGL.STATIC_DRAW); tessGeo.updatePolyNormalsBuffer(); if (glPolyNormal == 0) - glPolyNormal = PGraphicsOpenGL.createVertexBufferObject(context); + glPolyNormal = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyNormal); pgl.bufferData(PGL.ARRAY_BUFFER, 3 * sizef, tessGeo.polyNormalsBuffer, PGL.STATIC_DRAW); tessGeo.updatePolyTexCoordsBuffer(); if (glPolyTexcoord == 0) - glPolyTexcoord = PGraphicsOpenGL.createVertexBufferObject(context); + glPolyTexcoord = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyTexcoord); pgl.bufferData(PGL.ARRAY_BUFFER, 2 * sizef, tessGeo.polyTexCoordsBuffer, PGL.STATIC_DRAW); tessGeo.updatePolyAmbientBuffer(); if (glPolyAmbient == 0) - glPolyAmbient = PGraphicsOpenGL.createVertexBufferObject(context); + glPolyAmbient = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyAmbient); pgl.bufferData(PGL.ARRAY_BUFFER, sizei, tessGeo.polyAmbientBuffer, PGL.STATIC_DRAW); tessGeo.updatePolySpecularBuffer(); if (glPolySpecular == 0) - glPolySpecular = PGraphicsOpenGL.createVertexBufferObject(context); + glPolySpecular = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolySpecular); pgl.bufferData(PGL.ARRAY_BUFFER, sizei, tessGeo.polySpecularBuffer, PGL.STATIC_DRAW); tessGeo.updatePolyEmissiveBuffer(); if (glPolyEmissive == 0) - glPolyEmissive = PGraphicsOpenGL.createVertexBufferObject(context); + glPolyEmissive = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyEmissive); pgl.bufferData(PGL.ARRAY_BUFFER, sizei, tessGeo.polyEmissiveBuffer, PGL.STATIC_DRAW); tessGeo.updatePolyShininessBuffer(); if (glPolyShininess == 0) - glPolyShininess = PGraphicsOpenGL.createVertexBufferObject(context); + glPolyShininess = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPolyShininess); pgl.bufferData(PGL.ARRAY_BUFFER, sizef, tessGeo.polyShininessBuffer, PGL.STATIC_DRAW); @@ -3709,7 +3707,7 @@ public class PShapeOpenGL extends PShape { tessGeo.updatePolyIndicesBuffer(); if (glPolyIndex == 0) - glPolyIndex = PGraphicsOpenGL.createVertexBufferObject(context); + glPolyIndex = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, glPolyIndex); pgl.bufferData(PGL.ELEMENT_ARRAY_BUFFER, tessGeo.polyIndexCount * PGL.SIZEOF_INDEX, @@ -3726,21 +3724,21 @@ public class PShapeOpenGL extends PShape { tessGeo.updateLineVerticesBuffer(); if (glLineVertex == 0) - glLineVertex = PGraphicsOpenGL.createVertexBufferObject(context); + glLineVertex = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glLineVertex); pgl.bufferData(PGL.ARRAY_BUFFER, 4 * sizef, tessGeo.lineVerticesBuffer, PGL.STATIC_DRAW); tessGeo.updateLineColorsBuffer(); if (glLineColor == 0) - glLineColor = PGraphicsOpenGL.createVertexBufferObject(context); + glLineColor = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glLineColor); pgl.bufferData(PGL.ARRAY_BUFFER, sizei, tessGeo.lineColorsBuffer, PGL.STATIC_DRAW); tessGeo.updateLineDirectionsBuffer(); if (glLineAttrib == 0) - glLineAttrib = PGraphicsOpenGL.createVertexBufferObject(context); + glLineAttrib = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glLineAttrib); pgl.bufferData(PGL.ARRAY_BUFFER, 4 * sizef, tessGeo.lineDirectionsBuffer, PGL.STATIC_DRAW); @@ -3749,7 +3747,7 @@ public class PShapeOpenGL extends PShape { tessGeo.updateLineIndicesBuffer(); if (glLineIndex == 0) - glLineIndex = PGraphicsOpenGL.createVertexBufferObject(context); + glLineIndex = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, glLineIndex); pgl.bufferData(PGL.ELEMENT_ARRAY_BUFFER, tessGeo.lineIndexCount * PGL.SIZEOF_INDEX, @@ -3766,21 +3764,21 @@ public class PShapeOpenGL extends PShape { tessGeo.updatePointVerticesBuffer(); if (glPointVertex == 0) - glPointVertex = PGraphicsOpenGL.createVertexBufferObject(context); + glPointVertex = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPointVertex); pgl.bufferData(PGL.ARRAY_BUFFER, 4 * sizef, tessGeo.pointVerticesBuffer, PGL.STATIC_DRAW); tessGeo.updatePointColorsBuffer(); if (glPointColor == 0) - glPointColor = PGraphicsOpenGL.createVertexBufferObject(context); + glPointColor = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPointColor); pgl.bufferData(PGL.ARRAY_BUFFER, sizei, tessGeo.pointColorsBuffer, PGL.STATIC_DRAW); tessGeo.updatePointOffsetsBuffer(); if (glPointAttrib == 0) - glPointAttrib = PGraphicsOpenGL.createVertexBufferObject(context); + glPointAttrib = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ARRAY_BUFFER, glPointAttrib); pgl.bufferData(PGL.ARRAY_BUFFER, 2 * sizef, tessGeo.pointOffsetsBuffer, PGL.STATIC_DRAW); @@ -3789,7 +3787,7 @@ public class PShapeOpenGL extends PShape { tessGeo.updatePointIndicesBuffer(); if (glPointIndex == 0) - glPointIndex = PGraphicsOpenGL.createVertexBufferObject(context); + glPointIndex = PGraphicsOpenGL.createVertexBufferObject(context, pgl); pgl.bindBuffer(PGL.ELEMENT_ARRAY_BUFFER, glPointIndex); pgl.bufferData(PGL.ELEMENT_ARRAY_BUFFER, tessGeo.pointIndexCount * PGL.SIZEOF_INDEX, @@ -3870,47 +3868,47 @@ public class PShapeOpenGL extends PShape { protected void deletePolyBuffers() { if (glPolyVertex != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPolyVertex, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPolyVertex, context, pgl); glPolyVertex = 0; } if (glPolyColor != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPolyColor, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPolyColor, context, pgl); glPolyColor = 0; } if (glPolyNormal != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPolyNormal, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPolyNormal, context, pgl); glPolyNormal = 0; } if (glPolyTexcoord != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPolyTexcoord, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPolyTexcoord, context, pgl); glPolyTexcoord = 0; } if (glPolyAmbient != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPolyAmbient, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPolyAmbient, context, pgl); glPolyAmbient = 0; } if (glPolySpecular != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPolySpecular, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPolySpecular, context, pgl); glPolySpecular = 0; } if (glPolyEmissive != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPolyEmissive, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPolyEmissive, context, pgl); glPolyEmissive = 0; } if (glPolyShininess != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPolyShininess, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPolyShininess, context, pgl); glPolyShininess = 0; } if (glPolyIndex != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPolyIndex, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPolyIndex, context, pgl); glPolyIndex = 0; } } @@ -3918,22 +3916,22 @@ public class PShapeOpenGL extends PShape { protected void deleteLineBuffers() { if (glLineVertex != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glLineVertex, context); + PGraphicsOpenGL.deleteVertexBufferObject(glLineVertex, context, pgl); glLineVertex = 0; } if (glLineColor != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glLineColor, context); + PGraphicsOpenGL.deleteVertexBufferObject(glLineColor, context, pgl); glLineColor = 0; } if (glLineAttrib != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glLineAttrib, context); + PGraphicsOpenGL.deleteVertexBufferObject(glLineAttrib, context, pgl); glLineAttrib = 0; } if (glLineIndex != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glLineIndex, context); + PGraphicsOpenGL.deleteVertexBufferObject(glLineIndex, context, pgl); glLineIndex = 0; } } @@ -3941,22 +3939,22 @@ public class PShapeOpenGL extends PShape { protected void deletePointBuffers() { if (glPointVertex != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPointVertex, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPointVertex, context, pgl); glPointVertex = 0; } if (glPointColor != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPointColor, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPointColor, context, pgl); glPointColor = 0; } if (glPointAttrib != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPointAttrib, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPointAttrib, context, pgl); glPointAttrib = 0; } if (glPointIndex != 0) { - PGraphicsOpenGL.deleteVertexBufferObject(glPointIndex, context); + PGraphicsOpenGL.deleteVertexBufferObject(glPointIndex, context, pgl); glPointIndex = 0; } } diff --git a/core/src/processing/opengl/Texture.java b/core/src/processing/opengl/Texture.java index 1c45ef481..990d584e3 100644 --- a/core/src/processing/opengl/Texture.java +++ b/core/src/processing/opengl/Texture.java @@ -52,13 +52,13 @@ public class Texture implements PConstants { * to linear */ protected static final int LINEAR = 3; /** Bilinear sampling: both magnification filtering is set to linear and - * minification either to linear-mipmap-nearest (linear interplation is used + * minification either to linear-mipmap-nearest (linear interpolation is used * within a mipmap, but not between different mipmaps). */ protected static final int BILINEAR = 4; /** Trilinear sampling: magnification filtering set to linear, minification to * linear-mipmap-linear, which offers the best mipmap quality since linear * interpolation to compute the value in each of two maps and then - * interpolates linearly between these two value. */ + * interpolates linearly between these two values. */ protected static final int TRILINEAR = 5; @@ -83,6 +83,7 @@ public class Texture implements PConstants { public int glWidth; public int glHeight; + protected PGraphicsOpenGL pg; protected PGL pgl; // The interface between Processing and OpenGL. protected int context; // The context that created this texture. protected boolean colorBuffer; // true if it is the color attachment of @@ -118,8 +119,9 @@ public class Texture implements PConstants { // Constructors. - public Texture() { - pgl = PGraphicsOpenGL.pgl; + public Texture(PGraphicsOpenGL pg) { + this.pg = pg; + pgl = pg.pgl; context = pgl.createEmptyContext(); colorBuffer = false; @@ -134,8 +136,8 @@ public class Texture implements PConstants { * @param width int * @param height int */ - public Texture(int width, int height) { - this(width, height, new Parameters()); + public Texture(PGraphicsOpenGL pg, int width, int height) { + this(pg, width, height, new Parameters()); } @@ -146,8 +148,9 @@ public class Texture implements PConstants { * @param height int * @param params Parameters */ - public Texture(int width, int height, Object params) { - pgl = PGraphicsOpenGL.pgl; + public Texture(PGraphicsOpenGL pg, int width, int height, Object params) { + this.pg = pg; + pgl = pg.pgl; context = pgl.createEmptyContext(); colorBuffer = false; @@ -249,7 +252,7 @@ public class Texture implements PConstants { dispose(); // Creating new texture with the appropriate size. - Texture tex = new Texture(wide, high, getParameters()); + Texture tex = new Texture(pg, wide, high, getParameters()); // Copying the contents of this texture into tex. tex.set(this); @@ -511,17 +514,17 @@ public class Texture implements PConstants { } if (tempFbo == null) { - tempFbo = new FrameBuffer(glWidth, glHeight); + tempFbo = new FrameBuffer(pg, glWidth, glHeight); } // Attaching the texture to the color buffer of a FBO, binding the FBO and // reading the pixels from the current draw buffer (which is the color // buffer of the FBO). tempFbo.setColorBuffer(this); - PGraphicsOpenGL.pushFramebuffer(); - PGraphicsOpenGL.setFramebuffer(tempFbo); + pg.pushFramebuffer(); + pg.setFramebuffer(tempFbo); tempFbo.readPixels(); - PGraphicsOpenGL.popFramebuffer(); + pg.popFramebuffer(); tempFbo.getPixels(pixels); convertToARGB(pixels); @@ -881,7 +884,7 @@ public class Texture implements PConstants { public void getBufferPixels(int[] pixels) { BufferData data = null; if (usedBuffers != null && 0 < usedBuffers.size()) { - // the last used buffer is the one currently stored in the opengl the + // the last used buffer is the one currently stored in the opengl // texture data = usedBuffers.getLast(); } else if (bufferCache != null && 0 < bufferCache.size()) { @@ -1157,7 +1160,7 @@ public class Texture implements PConstants { } context = pgl.getCurrentContext(); - glName = PGraphicsOpenGL.createTextureObject(context); + glName = PGraphicsOpenGL.createTextureObject(context, pgl); pgl.bindTexture(glTarget, glName); pgl.texParameteri(glTarget, PGL.TEXTURE_MIN_FILTER, glMinFilter); @@ -1237,7 +1240,7 @@ public class Texture implements PConstants { } if (tempFbo == null) { - tempFbo = new FrameBuffer(glWidth, glHeight); + tempFbo = new FrameBuffer(pg, glWidth, glHeight); } // This texture is the color (destination) buffer of the FBO. @@ -1245,8 +1248,8 @@ public class Texture implements PConstants { tempFbo.disableDepthTest(); // FBO copy: - PGraphicsOpenGL.pushFramebuffer(); - PGraphicsOpenGL.setFramebuffer(tempFbo); + pg.pushFramebuffer(); + pg.setFramebuffer(tempFbo); // Clear the color buffer to make sure that the alpha channel is set to // full transparency pgl.clearColor(0, 0, 0, 0); @@ -1266,7 +1269,7 @@ public class Texture implements PConstants { tex.glWidth, tex.glHeight, tempFbo.width, tempFbo.height, x, y, w, h, x, y, w, h); } - PGraphicsOpenGL.popFramebuffer(); + pg.popFramebuffer(); updateTexels(x, y, w, h); } @@ -1277,7 +1280,7 @@ public class Texture implements PConstants { int texWidth, int texHeight, int x, int y, int w, int h, boolean scale) { if (tempFbo == null) { - tempFbo = new FrameBuffer(glWidth, glHeight); + tempFbo = new FrameBuffer(pg, glWidth, glHeight); } // This texture is the color (destination) buffer of the FBO. @@ -1285,8 +1288,8 @@ public class Texture implements PConstants { tempFbo.disableDepthTest(); // FBO copy: - PGraphicsOpenGL.pushFramebuffer(); - PGraphicsOpenGL.setFramebuffer(tempFbo); + pg.pushFramebuffer(); + pg.setFramebuffer(tempFbo); if (scale) { // Rendering tex into "this", and scaling the source rectangle // to cover the entire destination region. @@ -1302,7 +1305,7 @@ public class Texture implements PConstants { texWidth, texHeight, tempFbo.width, tempFbo.height, x, y, w, h, x, y, w, h); } - PGraphicsOpenGL.popFramebuffer(); + pg.popFramebuffer(); updateTexels(x, y, w, h); } diff --git a/java/examples/Basics/Web/LoadingImages/LoadingImages.pde b/java/examples/Basics/Web/LoadingImages/LoadingImages.pde index fee3d2c1e..9f2f3fa04 100644 --- a/java/examples/Basics/Web/LoadingImages/LoadingImages.pde +++ b/java/examples/Basics/Web/LoadingImages/LoadingImages.pde @@ -12,7 +12,7 @@ PImage img; void setup() { size(640, 360); - img = loadImage("http://processing.org/img/processing.gif"); + img = loadImage("http://processing.org/img/processing-web.png"); noLoop(); } diff --git a/java/examples/Demos/Tests/EmbedFrameTest/EmbedFrameTest.pde b/java/examples/Demos/Tests/EmbedFrameTest/EmbedFrameTest.pde new file mode 100644 index 000000000..9a29eb58b --- /dev/null +++ b/java/examples/Demos/Tests/EmbedFrameTest/EmbedFrameTest.pde @@ -0,0 +1,88 @@ +// Based on code by GeneKao (https://github.com/GeneKao) + +import peasy.*; + +import javax.swing.JFrame; +import java.awt.BorderLayout; +import java.awt.Insets; +EmbeddedSketch eSketch; +ChildApplet child = new ChildApplet(); +boolean mousePressedOnParent = false; +PeasyCam cam, cam2; + +void setup() { + size(320, 240, P3D); + cam = new PeasyCam(this, 300); + eSketch = new EmbeddedSketch(child); + smooth(); +} + +void draw() { + background(250); + if (mousePressed) { + fill(0); + text("Mouse pressed on parent.", 10, 10); + fill(0, 240, 0); + ellipse(mouseX, mouseY, 60, 60); + mousePressedOnParent = true; + } else { + fill(20); + ellipse(width/2, height/2, 60, 60); + mousePressedOnParent = false; + } + box(100); + if (eSketch.sketch.mousePressed) { + text("Mouse pressed on child.", 10, 30); + } +} + +//The JFrame which will contain the child applet +class EmbeddedSketch extends JFrame { + PApplet sketch; + EmbeddedSketch(PApplet p) { + int w = 400; + int h = 400; + sketch = p; + setVisible(true); + + setLayout(new BorderLayout()); + add(p, BorderLayout.CENTER); + p.init(); + + Insets insets = getInsets(); + setSize(insets.left + w, insets.top + h); + p.setBounds(insets.left, insets.top, w, h); + + setLocation(500, 200); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } +} + +class ChildApplet extends PApplet { + void setup() { + size(400, 400, P3D); + smooth(); + cam2 = new PeasyCam(this, 300); + cam2.reset(); + } + + void draw() { + background(0); + if (mousePressed) { + fill(240, 0, 0); + ellipse(mouseX, mouseY, 20, 20); + fill(255); + text("Mouse pressed on child.", 10, 30); + } else { + fill(255); + ellipse(width/2, height/2, 20, 20); + } + + box(100, 200, 100); + if (mousePressedOnParent) { + fill(255); + text("Mouse pressed on parent", 20, 20); + } + } +} + diff --git a/java/examples/Topics/Advanced Data/HashMapClass/HashMapClass.pde b/java/examples/Topics/Advanced Data/HashMapClass/HashMapClass.pde index 966641e24..bf8959bc2 100644 --- a/java/examples/Topics/Advanced Data/HashMapClass/HashMapClass.pde +++ b/java/examples/Topics/Advanced Data/HashMapClass/HashMapClass.pde @@ -8,8 +8,8 @@ * If you are familiar with associative arrays from other languages, * this is the same idea. * - * A simpler example is CountingStrings which uses IntHash instead of - * HashMap. The Processing classes IntHash, FloatHash, and StringHash + * A simpler example is CountingStrings which uses IntDict instead of + * HashMap. The Processing classes IntDict, FloatDict, and StringDict * offer a simpler way of pairing Strings with numbers or other Strings. * Here we use a HashMap because we want to pair a String with a custom * object, in this case a "Word" object that stores two numbers. diff --git a/java/libraries/glw/examples/BigWindow/BigWindow.pde b/java/libraries/glw/examples/BigWindow/BigWindow.pde deleted file mode 100644 index 3149b6996..000000000 --- a/java/libraries/glw/examples/BigWindow/BigWindow.pde +++ /dev/null @@ -1,13 +0,0 @@ -import processing.glw.*; - -void setup() { - size(2560, 1440, GLW.P2D); - frameRate(180); -} - -void draw() { - background(255, 0, 0); - - fill(255); - text("FPS: " + frameRate, mouseX, mouseY); -} diff --git a/java/libraries/glw/examples/LargeStage/LargeStage.pde b/java/libraries/glw/examples/LargeStage/LargeStage.pde new file mode 100644 index 000000000..c40f9b0bc --- /dev/null +++ b/java/libraries/glw/examples/LargeStage/LargeStage.pde @@ -0,0 +1,25 @@ +import processing.glw.*; + +PGraphics stage; + +void setup() { + // The main window will be hidden, only GLW.RENDERER + // can be used in size() + size(100, 100, GLW.RENDERER); + + stage = createGraphics(2560, 1440, GLW.P2D); + GLW.createWindow(stage); + frameRate(180); +} + +void draw() { + // The draw() method is used to update the offscreen surfaces, + // but not to draw directly to the screen. + stage.beginDraw(); + stage.background(200); + stage.fill(255); + stage.ellipse(mouseX, mouseY, 50, 50); + stage.fill(0); + stage.text(frameRate, 100, 100); + stage.endDraw(); +} \ No newline at end of file diff --git a/java/libraries/glw/examples/MultipleWindows/MultipleWindows.pde b/java/libraries/glw/examples/MultipleWindows/MultipleWindows.pde new file mode 100644 index 000000000..902fdbcd4 --- /dev/null +++ b/java/libraries/glw/examples/MultipleWindows/MultipleWindows.pde @@ -0,0 +1,28 @@ +import processing.glw.*; + +PGraphics canvas1; +PGraphics canvas2; + +void setup() { + size(100, 100, GLW.RENDERER); + canvas1 = createGraphics(320, 240, GLW.P2D); + canvas2 = createGraphics(320, 240, GLW.P2D); + GLW.createWindow(canvas1); + GLW.createWindow(canvas2); +} + +void draw() { + canvas1.beginDraw(); + canvas1.background(200); + canvas1.ellipse(mouseX, mouseY, 100, 100); + canvas1.endDraw(); + + canvas2.beginDraw(); + canvas2.background(170); + canvas2.ellipse(mouseX, mouseY, 50, 50); + canvas2.endDraw(); +} + +void keyPressed() { + GLW.getFocusedWindow().setVisible(false); +} \ No newline at end of file diff --git a/java/libraries/glw/src/processing/glw/GLW.java b/java/libraries/glw/src/processing/glw/GLW.java index c45805cb6..5968461e7 100644 --- a/java/libraries/glw/src/processing/glw/GLW.java +++ b/java/libraries/glw/src/processing/glw/GLW.java @@ -1,6 +1,52 @@ package processing.glw; -public interface GLW { - static final String P2D = "processing.glw.PGraphics2D"; - static final String P3D = "processing.glw.PGraphics3D"; +import processing.core.PGraphics; + +import com.jogamp.newt.opengl.GLWindow; + +import java.util.HashMap; + +public class GLW { + static public final String RENDERER = "processing.glw.PGraphicsGLW"; + static public final String OPENGL = "processing.glw.PGraphicsGLW"; + + static public final String P2D = "processing.glw.PGraphics2D"; + static public final String P3D = "processing.glw.PGraphics3D"; + + static protected HashMap windows = + new HashMap(); + + public GLW() { + } + + static public void createWindow(PGraphics pg) { + if (pg instanceof PGraphics2D || pg instanceof PGraphics3D) { + windows.put(pg, null); + } else { + throw new RuntimeException("Only GLW.P2D or GLW.P3D surfaces can be attached to a window"); + } + } + + static public GLWindow getWindow(PGraphics pg) { + return windows.get(pg); + } + + static public boolean isFocused(PGraphics pg) { + GLWindow win = windows.get(pg); + return win != null && win.hasFocus(); + } + + static public PGraphics getFocusedGraphics() { + for (PGraphics pg: windows.keySet()) { + if (isFocused(pg)) return pg; + } + return null; + } + + static public GLWindow getFocusedWindow() { + for (PGraphics pg: windows.keySet()) { + if (isFocused(pg)) return windows.get(pg); + } + return null; + } } diff --git a/java/libraries/glw/src/processing/glw/PGraphics2D.java b/java/libraries/glw/src/processing/glw/PGraphics2D.java index b28f93f31..90b52899c 100644 --- a/java/libraries/glw/src/processing/glw/PGraphics2D.java +++ b/java/libraries/glw/src/processing/glw/PGraphics2D.java @@ -6,5 +6,5 @@ import processing.opengl.PGraphicsOpenGL; public class PGraphics2D extends processing.opengl.PGraphics2D { protected PGL createPGL(PGraphicsOpenGL pg) { return new PNEWT(pg); - } + } } diff --git a/java/libraries/glw/src/processing/glw/PGraphicsGLW.java b/java/libraries/glw/src/processing/glw/PGraphicsGLW.java index d436454c2..34c334fce 100644 --- a/java/libraries/glw/src/processing/glw/PGraphicsGLW.java +++ b/java/libraries/glw/src/processing/glw/PGraphicsGLW.java @@ -26,11 +26,60 @@ import processing.opengl.PGL; import processing.opengl.PGraphicsOpenGL; /** - * LWJGL renderer. + * GLW renderer. It's only role is to drive the main animation loop by calling + * requestDraw() and so allowing the offscreen canvases to be drawn inside the + * draw() method of the sketch. Currently, it cannot be used to draw into. * */ public class PGraphicsGLW extends PGraphicsOpenGL { protected PGL createPGL(PGraphicsOpenGL pg) { return new PNEWT(pg); - } + } + + public void beginDraw() { + if (primarySurface) { + setCurrentPG(this); + } else { + throw new RuntimeException("GLW renderer cannot be used as an offscreen surface"); + } + + report("top beginDraw()"); + + if (!checkGLThread()) { + return; + } + + if (drawing) { + return; + } + + if (!glParamsRead) { + getGLParameters(); + } + + drawing = true; + + report("bot beginDraw()"); + } + + public void endDraw() { + report("top endDraw()"); + + if (!drawing) { + return; + } + + if (primarySurface) { + setCurrentPG(null); + } else { + throw new RuntimeException("GLW renderer cannot be used as an offscreen surface."); + } + drawing = false; + + report("bot endDraw()"); + } + + protected void vertexImpl(float x, float y, float z, float u, float v) { + throw new RuntimeException("The main GLW renderer cannot be used to draw to."); + } } diff --git a/java/libraries/glw/src/processing/glw/PNEWT.java b/java/libraries/glw/src/processing/glw/PNEWT.java index 61922b288..08d3de581 100644 --- a/java/libraries/glw/src/processing/glw/PNEWT.java +++ b/java/libraries/glw/src/processing/glw/PNEWT.java @@ -24,15 +24,24 @@ package processing.glw; +import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLDrawableFactory; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLException; import javax.media.opengl.GLProfile; import com.jogamp.newt.opengl.GLWindow; import com.jogamp.newt.event.WindowAdapter; import com.jogamp.newt.event.WindowEvent; +import processing.core.PApplet; +import processing.core.PGraphics; +import processing.opengl.PGL; import processing.opengl.PGraphicsOpenGL; import processing.opengl.PJOGL; +import processing.opengl.Texture; + public class PNEWT extends PJOGL { @@ -43,51 +52,216 @@ public class PNEWT extends PJOGL { USE_JOGL_FBOLAYER = false; } + protected static GLCapabilities sharedCaps; + protected static GLAutoDrawable sharedDrawable; + public PNEWT(PGraphicsOpenGL pg) { super(pg); } - + protected void initSurface(int antialias) { + if (!(pg instanceof PGraphicsGLW)) { + throw new RuntimeException("GLW.RENDERER is the only option in size() when using the GLW library."); + } + if (profile == null) { - profile = GLProfile.getDefault(); - } else { - window.removeGLEventListener(listener); - pg.parent.remove(canvasNEWT); + if (PROFILE == 2) { + try { + profile = GLProfile.getGL2ES1(); + } catch (GLException ex) { + profile = GLProfile.getMaxFixedFunc(true); + } + } else if (PROFILE == 3) { + try { + profile = GLProfile.getGL2GL3(); + } catch (GLException ex) { + profile = GLProfile.getMaxProgrammable(true); + } + if (!profile.isGL3()) { + PGraphics.showWarning("Requested profile GL3 but is not available, got: " + profile); + } + } else if (PROFILE == 4) { + try { + profile = GLProfile.getGL4ES3(); + } catch (GLException ex) { + profile = GLProfile.getMaxProgrammable(true); + } + if (!profile.isGL4()) { + PGraphics.showWarning("Requested profile GL4 but is not available, got: " + profile); + } + } else throw new RuntimeException(UNSUPPORTED_GLPROF_ERROR); + + if (2 < PROFILE) { + texVertShaderSource = convertVertexSource(texVertShaderSource, 120, 150); + tex2DFragShaderSource = convertFragmentSource(tex2DFragShaderSource, 120, 150); + texRectFragShaderSource = convertFragmentSource(texRectFragShaderSource, 120, 150); + } } // Setting up the desired capabilities; - GLCapabilities caps = new GLCapabilities(profile); - caps.setAlphaBits(REQUESTED_ALPHA_BITS); - caps.setDepthBits(REQUESTED_DEPTH_BITS); - caps.setStencilBits(REQUESTED_STENCIL_BITS); - - if (1 < antialias) { - caps.setSampleBuffers(true); - caps.setNumSamples(antialias); - } else { - caps.setSampleBuffers(false); - } - fboLayerRequested = false; - - window = GLWindow.create(caps); - window.setSize(pg.width, pg.height); - window.setVisible(true); - pg.parent.frame.setVisible(false); - - canvas = canvasNEWT; - canvasAWT = null; + sharedCaps = new GLCapabilities(profile); + sharedCaps.setAlphaBits(REQUESTED_ALPHA_BITS); + sharedCaps.setDepthBits(REQUESTED_DEPTH_BITS); + sharedCaps.setStencilBits(REQUESTED_STENCIL_BITS); - window.addWindowListener(new WindowAdapter() { - @Override - public void windowDestroyNotify(final WindowEvent e) { - System.exit(0); - } - }); + sharedCaps.setPBuffer(false); + sharedCaps.setFBO(false); + sharedCaps.setSampleBuffers(false); - registerListeners(); + fboLayerRequested = false; + sharedDrawable = GLDrawableFactory.getFactory(profile).createDummyAutoDrawable(null, true, sharedCaps, null); + sharedDrawable.display(); // triggers GLContext object creation and native realization. + DummyListener listener = new DummyListener(); + sharedDrawable.addGLEventListener(listener); + + pg.parent.frame.setVisible(false); + } + + + protected boolean displayable() { + return false; + } + + + protected void beginDraw(boolean clear0) { + } + + + protected void endDraw(boolean clear0) { + } + + + protected void requestDraw() { + createWindows(); + + // Calling display() so the main draw() method is triggered, where the + // offscreen GLW canvases can be updated. + sharedDrawable.display(); + + displayWindows(); + } + + private void createWindows() { + for (PGraphics pg: GLW.windows.keySet()) { + GLWindow win = GLW.windows.get(pg); + if (win == null) { + win = GLWindow.create(sharedCaps); + win.setSharedAutoDrawable(sharedDrawable); + win.setSize(pg.width, pg.height); + win.setTitle("TEST"); + win.setVisible(true); + GLW.windows.put(pg, win); + + NEWTListener listener = new NEWTListener(pg); + win.addGLEventListener(listener); + + NEWTMouseListener mouseListener = new NEWTMouseListener(); + win.addMouseListener(mouseListener); + NEWTKeyListener keyListener = new NEWTKeyListener(); + win.addKeyListener(keyListener); + NEWTWindowListener winListener = new NEWTWindowListener(); + win.addWindowListener(winListener); + + win.addWindowListener(new WindowAdapter() { + @Override + public void windowDestroyNotify(final WindowEvent e) { + } + }); + } + } } + + private void displayWindows() { + int totalCount = 0; + int realizedCount = 0; + for (GLWindow win: GLW.windows.values()) { + if (win != null) { + totalCount++; + if (win.isRealized()) realizedCount++; + win.display(); + } + } + + if (0 < totalCount && realizedCount == 0) { + // All windows where closed, exit the application + sharedDrawable.destroy(); + System.exit(0); + } + } + + + protected class DummyListener implements GLEventListener { + public DummyListener() { + } + @Override + public void display(GLAutoDrawable glDrawable) { + getGL(glDrawable); + pg.parent.handleDraw(); + } + + @Override + public void dispose(GLAutoDrawable adrawable) { + } + + @Override + public void init(GLAutoDrawable glDrawable) { + getGL(glDrawable); + + capabilities = glDrawable.getChosenGLCapabilities(); + if (!hasFBOs()) { + throw new RuntimeException(MISSING_FBO_ERROR); + } + if (!hasShaders()) { + throw new RuntimeException(MISSING_GLSL_ERROR); + } + if (USE_JOGL_FBOLAYER && capabilities.isFBO()) { + int maxs = maxSamples(); + numSamples = PApplet.min(capabilities.getNumSamples(), maxs); + } + } + + @Override + public void reshape(GLAutoDrawable glDrawable, int x, int y, int w, int h) { + } + } + + + protected class NEWTListener implements GLEventListener { + PGraphicsOpenGL pg; + PNEWT pgl; + + public NEWTListener(PGraphics pg) { + this.pg = (PGraphicsOpenGL)pg; + pgl = (PNEWT)this.pg.pgl; + } + + @Override + public void display(GLAutoDrawable glDrawable) { + pgl.getGL(glDrawable); + Texture tex = pg.getTexture(false); + if (tex != null) { + pgl.disable(PGL.BLEND); + pgl.drawTexture(tex.glTarget, tex.glName, + tex.glWidth, tex.glHeight, + 0, 0, pg.width, pg.height); + pgl.enable(PGL.BLEND); + } + } + + @Override + public void dispose(GLAutoDrawable adrawable) { + } + + @Override + public void init(GLAutoDrawable glDrawable) { + } + + @Override + public void reshape(GLAutoDrawable glDrawable, int x, int y, int w, int h) { + } + } } diff --git a/java/libraries/lwjgl/src/processing/lwjgl/PLWJGL.java b/java/libraries/lwjgl/src/processing/lwjgl/PLWJGL.java index 6409152ae..7be2db1e0 100644 --- a/java/libraries/lwjgl/src/processing/lwjgl/PLWJGL.java +++ b/java/libraries/lwjgl/src/processing/lwjgl/PLWJGL.java @@ -125,6 +125,11 @@ public class PLWJGL extends PGL { if (glu == null) glu = new GLU(); } + + public Canvas getCanvas() { + return canvas; + } + protected void setFps(float fps) { if (!setFps || targetFps != fps) { @@ -1991,4 +1996,9 @@ public class PLWJGL extends PGL { public void drawBuffer(int buf) { GL11.glDrawBuffer(buf); } + + + @Override + protected void getGL(PGL pgl) { + } } diff --git a/java/libraries/minim/examples/AnalyzeSound/AnalyzeSound.pde b/java/libraries/minim/examples/AnalyzeSound/AnalyzeSound.pde index d2122efb7..46bdec13a 100644 --- a/java/libraries/minim/examples/AnalyzeSound/AnalyzeSound.pde +++ b/java/libraries/minim/examples/AnalyzeSound/AnalyzeSound.pde @@ -7,6 +7,9 @@ * the frequency content of a signal. You've seen * visualizations like this before in music players * and car stereos. + *

+ * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ */ import ddf.minim.analysis.*; diff --git a/java/libraries/minim/examples/CreateAnInstrument/CreateAnInstrument.pde b/java/libraries/minim/examples/CreateAnInstrument/CreateAnInstrument.pde new file mode 100644 index 000000000..e43c3f339 --- /dev/null +++ b/java/libraries/minim/examples/CreateAnInstrument/CreateAnInstrument.pde @@ -0,0 +1,85 @@ +/** + * This sketch demonstrates how to create synthesized sound with Minim using an AudioOutput and + * an Instrument we define. By using the playNote method you can schedule notes to played + * at some point in the future, essentially allowing to you create musical scores with code. + * Because they are constructed with code, they can be either deterministic or different every time. + * This sketch creates a deterministic score, meaning it is the same every time you run the sketch. + *

+ * For more complex examples of using playNote check out algorithmicCompExample and compositionExample + * in the Synthesis folder. + *

+ * For more information about Minim and additional features, visit http://code.compartmental.net/minim/ + */ + +import ddf.minim.*; +import ddf.minim.ugens.*; + +Minim minim; +AudioOutput out; + +// to make an Instrument we must define a class +// that implements the Instrument interface. +class SineInstrument implements Instrument +{ + Oscil wave; + Line ampEnv; + + SineInstrument( float frequency ) + { + // make a sine wave oscillator + // the amplitude is zero because + // we are going to patch a Line to it anyway + wave = new Oscil( frequency, 0, Waves.SINE ); + ampEnv = new Line(); + ampEnv.patch( wave.amplitude ); + } + + // this is called by the sequencer when this instrument + // should start making sound. the duration is expressed in seconds. + void noteOn( float duration ) + { + // start the amplitude envelope + ampEnv.activate( duration, 0.5f, 0 ); + // attach the oscil to the output so it makes sound + wave.patch( out ); + } + + // this is called by the sequencer when the instrument should + // stop making sound + void noteOff() + { + wave.unpatch( out ); + } +} + +void setup() +{ + size(512, 200, P3D); + + minim = new Minim(this); + + // use the getLineOut method of the Minim object to get an AudioOutput object + out = minim.getLineOut(); + + // when providing an Instrument, we always specify start time and duration + out.playNote( 0.0, 0.9, new SineInstrument( 97.99 ) ); + out.playNote( 1.0, 0.9, new SineInstrument( 123.47 ) ); + + // we can use the Frequency class to create frequencies from pitch names + out.playNote( 2.0, 2.9, new SineInstrument( Frequency.ofPitch( "C3" ).asHz() ) ); + out.playNote( 3.0, 1.9, new SineInstrument( Frequency.ofPitch( "E3" ).asHz() ) ); + out.playNote( 4.0, 0.9, new SineInstrument( Frequency.ofPitch( "G3" ).asHz() ) ); +} + +void draw() +{ + background(0); + stroke(255); + + // draw the waveforms + for(int i = 0; i < out.bufferSize() - 1; i++) + { + line( i, 50 + out.left.get(i)*50, i+1, 50 + out.left.get(i+1)*50 ); + line( i, 150 + out.right.get(i)*50, i+1, 150 + out.right.get(i+1)*50 ); + } +} diff --git a/java/libraries/minim/examples/GetMetaData/GetMetaData.pde b/java/libraries/minim/examples/GetMetaData/GetMetaData.pde index fe47ea429..717152ba2 100644 --- a/java/libraries/minim/examples/GetMetaData/GetMetaData.pde +++ b/java/libraries/minim/examples/GetMetaData/GetMetaData.pde @@ -10,6 +10,9 @@ * If you load WAV file or other non-tagged file, most of the metadata * will be empty, but you will still have information like the filename * and the length. + *

+ * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ */ import ddf.minim.*; @@ -52,14 +55,3 @@ void draw() text("Publisher: " + meta.publisher(), 5, y+=yi); text("Encoded: " + meta.encoded(), 5, y+=yi); } - - -void stop() -{ - // always close Minim audio classes when you are done with them - groove.close(); - // always stop Minim before exiting - minim.stop(); - - super.stop(); -} diff --git a/java/libraries/minim/examples/GetMetaData/data/groove.mp3 b/java/libraries/minim/examples/GetMetaData/data/groove.mp3 index abfd3c811..22fd64fd4 100644 Binary files a/java/libraries/minim/examples/GetMetaData/data/groove.mp3 and b/java/libraries/minim/examples/GetMetaData/data/groove.mp3 differ diff --git a/java/libraries/minim/examples/MonitorInput/MonitorInput.pde b/java/libraries/minim/examples/MonitorInput/MonitorInput.pde index 3ec94cc6f..86679683b 100644 --- a/java/libraries/minim/examples/MonitorInput/MonitorInput.pde +++ b/java/libraries/minim/examples/MonitorInput/MonitorInput.pde @@ -1,12 +1,17 @@ /** * This sketch demonstrates how to monitor the currently active audio input - * of the computer using an AudioInput. What you will actually + * of the computer using an AudioInput. What you will actually * be monitoring depends on the current settings of the machine the sketch is running on. * Typically, you will be monitoring the built-in microphone, but if running on a desktop - * its feasible that the user may have the actual audio output of the computer + * it's feasible that the user may have the actual audio output of the computer * as the active audio input, or something else entirely. *

- * When you run your sketch as an applet you will need to sign it in order to get an input. + * Press 'm' to toggle monitoring on and off. + *

+ * When you run your sketch as an applet you will need to sign it in order to get an input. + *

+ * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ */ import ddf.minim.*; @@ -22,9 +27,6 @@ void setup() // use the getLineIn method of the Minim object to get an AudioInput in = minim.getLineIn(); - - // uncomment this line to *hear* what is being monitored, in addition to seeing it - in.enableMonitoring(); } void draw() @@ -38,4 +40,22 @@ void draw() line( i, 50 + in.left.get(i)*50, i+1, 50 + in.left.get(i+1)*50 ); line( i, 150 + in.right.get(i)*50, i+1, 150 + in.right.get(i+1)*50 ); } + + String monitoringState = in.isMonitoring() ? "enabled" : "disabled"; + text( "Input monitoring is currently " + monitoringState + ".", 5, 15 ); +} + +void keyPressed() +{ + if ( key == 'm' || key == 'M' ) + { + if ( in.isMonitoring() ) + { + in.disableMonitoring(); + } + else + { + in.enableMonitoring(); + } + } } diff --git a/java/libraries/minim/examples/PatchingAnInput/PatchingAnInput.pde b/java/libraries/minim/examples/PatchingAnInput/PatchingAnInput.pde index 54b8719aa..e756a6d17 100644 --- a/java/libraries/minim/examples/PatchingAnInput/PatchingAnInput.pde +++ b/java/libraries/minim/examples/PatchingAnInput/PatchingAnInput.pde @@ -4,6 +4,9 @@ * a UGen. In this case, we patch an Oscil generating a sine wave into * the amplitude input of an Oscil generating a square wave. The result * is known as amplitude modulation. + *

+ * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ */ import ddf.minim.*; diff --git a/java/libraries/minim/examples/PlayAFile/PlayAFile.pde b/java/libraries/minim/examples/PlayAFile/PlayAFile.pde index a906820ea..c95eb3956 100644 --- a/java/libraries/minim/examples/PlayAFile/PlayAFile.pde +++ b/java/libraries/minim/examples/PlayAFile/PlayAFile.pde @@ -1,6 +1,9 @@ /** * This sketch demonstrates how to play a file with Minim using an AudioPlayer.
* It's also a good example of how to draw the waveform of the audio. + *

+ * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ */ import ddf.minim.*; @@ -20,7 +23,9 @@ void setup() // sketch folder. you can also pass an absolute path, or a URL. player = minim.loadFile("marcus_kellis_theme.mp3"); - // play the file + // play the file from start to finish. + // if you want to play the file again, + // you need to call rewind() first. player.play(); } diff --git a/java/libraries/minim/examples/RecordAudioInput/RecordAudioInput.pde b/java/libraries/minim/examples/RecordAudioInput/RecordAudioInput.pde index 296d23814..b5983fde7 100644 --- a/java/libraries/minim/examples/RecordAudioInput/RecordAudioInput.pde +++ b/java/libraries/minim/examples/RecordAudioInput/RecordAudioInput.pde @@ -1,8 +1,13 @@ /** * This sketch demonstrates how to an AudioRecorder to record audio to disk. - * To use this sketch you need to have something plugged into the line-in on your computer, or else be working on a - * laptop with an active built-in microphone. Press 'r' to toggle recording on and off and the press 's' to save to disk. + * To use this sketch you need to have something plugged into the line-in on your computer, + * or else be working on a laptop with an active built-in microphone. + *

+ * Press 'r' to toggle recording on and off and the press 's' to save to disk. * The recorded file will be placed in the sketch folder of the sketch. + *

+ * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ */ import ddf.minim.*; @@ -18,11 +23,9 @@ void setup() minim = new Minim(this); in = minim.getLineIn(); - // create a recorder that will record from the input to the filename specified, using buffered recording - // buffered recording means that all captured audio will be written into a sample buffer - // then when save() is called, the contents of the buffer will actually be written to a file + // create a recorder that will record from the input to the filename specified // the file will be located in the sketch's root folder. - recorder = minim.createRecorder(in, "myrecording.wav", true); + recorder = minim.createRecorder(in, "myrecording.wav"); textFont(createFont("Arial", 12)); } diff --git a/java/libraries/minim/examples/RecordAudioOutput/RecordAudioOutput.pde b/java/libraries/minim/examples/RecordAudioOutput/RecordAudioOutput.pde index ae7cefdbf..c34d03f1e 100644 --- a/java/libraries/minim/examples/RecordAudioOutput/RecordAudioOutput.pde +++ b/java/libraries/minim/examples/RecordAudioOutput/RecordAudioOutput.pde @@ -2,6 +2,9 @@ * This sketch demonstrates how to use an AudioRecorder to record audio to disk. * Press 'r' to toggle recording on and off and the press 's' to save to disk. * The recorded file will be placed in the sketch folder of the sketch. + *

+ * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ */ import ddf.minim.*; @@ -19,11 +22,9 @@ void setup() out = minim.getLineOut(); - // create a recorder that will record from the input to the filename specified, using buffered recording - // buffered recording means that all captured audio will be written into a sample buffer - // then when save() is called, the contents of the buffer will actually be written to a file + // create a recorder that will record from the output to the filename specified // the file will be located in the sketch's root folder. - recorder = minim.createRecorder(out, "myrecording.wav", true); + recorder = minim.createRecorder(out, "myrecording.wav"); // patch some sound into the output so we have something to record Oscil wave = new Oscil( 440.f, 1.0f ); diff --git a/java/libraries/minim/examples/Scrubbing/Scrubbing.pde b/java/libraries/minim/examples/Scrubbing/Scrubbing.pde index dc3c5fedd..23f24bdb1 100644 --- a/java/libraries/minim/examples/Scrubbing/Scrubbing.pde +++ b/java/libraries/minim/examples/Scrubbing/Scrubbing.pde @@ -5,6 +5,9 @@ * But the end result is convincing enough. *

* The positioning code is inside of the Play, Rewind, and Forward classes, which are in button.pde. + *

+ * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ */ import ddf.minim.*; diff --git a/java/libraries/minim/examples/SequenceSound/SequenceSound.pde b/java/libraries/minim/examples/SequenceSound/SequenceSound.pde index 9d4878e94..c23c8fa36 100644 --- a/java/libraries/minim/examples/SequenceSound/SequenceSound.pde +++ b/java/libraries/minim/examples/SequenceSound/SequenceSound.pde @@ -6,8 +6,11 @@ * sketch creates a deterministic score, meaning it is the same every time you run the sketch. It also demonstrates * a couple different versions of the playNote method. *

- * For more complex examples of using playNote check out algorithmicCompExample and compositionExample - * in the Synthesis folder. + * For more complex examples of using playNote check out + * algorithmicCompExample and compositionExample in the Synthesis folder. + *

+ * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ */ import ddf.minim.*; @@ -25,6 +28,20 @@ void setup() // use the getLineOut method of the Minim object to get an AudioOutput object out = minim.getLineOut(); + // set the tempo of the sequencer + // this makes the first argument of playNote + // specify the start time in quarter notes + // and the duration becomes relative to the length of a quarter note + // by default the tempo is 60 BPM (beats per minute). + // at 60 BPM both start time and duration can be interpreted as seconds. + // to retrieve the current tempo, use getTempo(). + out.setTempo( 80 ); + + // pause the sequencer so our note play back will be rock solid + // if you don't do this, then tiny bits of error can occur since + // the sequencer is running in parallel with you note queueing. + out.pauseNotes(); + // given start time, duration, and frequency out.playNote( 0.0, 0.9, 97.99 ); out.playNote( 1.0, 0.9, 123.47 ); @@ -41,15 +58,17 @@ void setup() out.playNote( 7.0, "G4" ); // the note offset is simply added into the start time of - // every subsequenct call to playNote. It's expressed in beats, - // but since the default tempo of an AudioOuput is 60 beats per minute, - // this particular call translates to 8.1 seconds, as you might expect. + // every subsequenct call to playNote. It's expressed in beats. + // to get the current note offset, use getNoteOffset(). out.setNoteOffset( 8.1 ); // because only given a note name or frequency // starttime defaults to 0.0 and duration defaults to 1.0 out.playNote( "G5" ); out.playNote( 987.77 ); + + // now we can start the sequencer again to hear our sequence + out.resumeNotes(); } void draw() diff --git a/java/libraries/minim/examples/SoundSpectrum/SoundSpectrum.pde b/java/libraries/minim/examples/SoundSpectrum/SoundSpectrum.pde index c9095d953..73de17a0c 100644 --- a/java/libraries/minim/examples/SoundSpectrum/SoundSpectrum.pde +++ b/java/libraries/minim/examples/SoundSpectrum/SoundSpectrum.pde @@ -6,8 +6,8 @@ * these can be calculated: Linearly, by grouping equal numbers of adjacent frequency bands, or * Logarithmically, by grouping frequency bands by octave, which is more akin to how humans hear sound. *
- * This sketch illustrates the difference between viewing the full spectrum, linearly spaced averaged bands, - * and logarithmically spaced averaged bands. + * This sketch illustrates the difference between viewing the full spectrum, + * linearly spaced averaged bands, and logarithmically spaced averaged bands. *

* From top to bottom: *

    @@ -19,6 +19,8 @@ * Moving the mouse across the sketch will highlight a band in each spectrum and display what the center * frequency of that band is. The averaged bands are drawn so that they line up with full spectrum bands they * are averages of. In this way, you can clearly see how logarithmic averages differ from linear averages. + *

    + * For more information about Minim and additional features, visit http://code.compartmental.net/minim/ */ import ddf.minim.analysis.*; diff --git a/java/libraries/minim/examples/SynthesizeSound/SynthesizeSound.pde b/java/libraries/minim/examples/SynthesizeSound/SynthesizeSound.pde index 4a836e1e2..64b8d915d 100644 --- a/java/libraries/minim/examples/SynthesizeSound/SynthesizeSound.pde +++ b/java/libraries/minim/examples/SynthesizeSound/SynthesizeSound.pde @@ -1,9 +1,16 @@ /** * This sketch demonstrates how to create synthesized sound with Minim * using an AudioOutput and an Oscil. An Oscil is a UGen object, - * one of many different types included with Minim. For many more examples - * of UGens included with Minim, have a look in the Synthesis - * folder of the Minim examples. + * one of many different types included with Minim. By using + * the numbers 1 thru 5, you can change the waveform being used + * by the Oscil to make sound. These basic waveforms are the + * basis of much audio synthesis. + * + * For many more examples of UGens included with Minim, + * have a look in the Synthesis folder of the Minim examples. + *

    + * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ */ import ddf.minim.*; @@ -32,11 +39,62 @@ void draw() { background(0); stroke(255); + strokeWeight(1); - // draw the waveforms + // draw the waveform of the output for(int i = 0; i < out.bufferSize() - 1; i++) { - line( i, 50 + out.left.get(i)*50, i+1, 50 + out.left.get(i+1)*50 ); - line( i, 150 + out.right.get(i)*50, i+1, 150 + out.right.get(i+1)*50 ); + line( i, 50 - out.left.get(i)*50, i+1, 50 - out.left.get(i+1)*50 ); + line( i, 150 - out.right.get(i)*50, i+1, 150 - out.right.get(i+1)*50 ); + } + + // draw the waveform we are using in the oscillator + stroke( 128, 0, 0 ); + strokeWeight(4); + for( int i = 0; i < width-1; ++i ) + { + point( i, height/2 - (height*0.49) * wave.getWaveform().value( (float)i / width ) ); + } +} + +void mouseMoved() +{ + // usually when setting the amplitude and frequency of an Oscil + // you will want to patch something to the amplitude and frequency inputs + // but this is a quick and easy way to turn the screen into + // an x-y control for them. + + float amp = map( mouseY, 0, height, 1, 0 ); + wave.setAmplitude( amp ); + + float freq = map( mouseX, 0, width, 110, 880 ); + wave.setFrequency( freq ); +} + +void keyPressed() +{ + switch( key ) + { + case '1': + wave.setWaveform( Waves.SINE ); + break; + + case '2': + wave.setWaveform( Waves.TRIANGLE ); + break; + + case '3': + wave.setWaveform( Waves.SAW ); + break; + + case '4': + wave.setWaveform( Waves.SQUARE ); + break; + + case '5': + wave.setWaveform( Waves.QUARTERPULSE ); + break; + + default: break; } } diff --git a/java/libraries/minim/examples/TriggerASample/TriggerASample.pde b/java/libraries/minim/examples/TriggerASample/TriggerASample.pde index fa4a0c878..dc39a4091 100644 --- a/java/libraries/minim/examples/TriggerASample/TriggerASample.pde +++ b/java/libraries/minim/examples/TriggerASample/TriggerASample.pde @@ -22,6 +22,9 @@ *

    * Use 'k' and 's' to trigger a kick drum sample and a snare sample, respectively. * You will see their waveforms drawn when they are played back. + *

    + * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ */ import ddf.minim.*; diff --git a/java/libraries/minim/examples/delayExample/delayExample.pde b/java/libraries/minim/examples/delayExample/delayExample.pde index dbf69a4ac..6c2b7941b 100644 --- a/java/libraries/minim/examples/delayExample/delayExample.pde +++ b/java/libraries/minim/examples/delayExample/delayExample.pde @@ -1,10 +1,12 @@ -/* delayExample - is an example of using the Delay UGen in a continuous sound example. - Use the mouse to control the delay time and the amount of feedback - in the delay unit. - author: Anderson Mills - Anderson Mills's work was supported by numediart (www.numediart.org) -*/ +/* delayExample
    + * is an example of using the Delay UGen in a continuous sound example. + *

    + * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ + *

    + * author: Anderson Mills
    + * Anderson Mills's work was supported by numediart (www.numediart.org) + */ // import everything necessary to make sound. import ddf.minim.*; @@ -14,47 +16,44 @@ import ddf.minim.ugens.*; // more than one methods (setup(), draw(), stop()). Minim minim; AudioOutput out; -Delay myDelay1; +Delay myDelay; // setup is run once at the beginning void setup() { // initialize the drawing window - size( 512, 200, P2D ); + size( 512, 200 ); // initialize the minim and out objects minim = new Minim(this); - out = minim.getLineOut( Minim.MONO, 2048 ); + out = minim.getLineOut(); - // initialize myDelay1 with continual feedback and no audio passthrough - myDelay1 = new Delay( 0.6, 0.9, true, false ); + // initialize myDelay with continual feedback and audio passthrough + myDelay = new Delay( 0.4, 0.5, true, true ); + + // sawh will create a Sawtooth wave with the requested number of harmonics. + // like with Waves.randomNHarms for sine waves, + // you can create a richer sounding sawtooth this way. + Waveform saw = Waves.sawh( 15 ); // create the Blip that will be used - Oscil myBlip = new Oscil( 245.0, 0.3, Waves.saw( 15 ) ); + Oscil myBlip = new Oscil( 245.0, 0.3, saw ); + // Waves.square will create a square wave with an uneven duty-cycle, + // also known as a pulse wave. a square wave has only two values, + // either -1 or 1 and the duty cycle indicates how much of the wave + // should -1 and how much 1. in this case, we are asking for a square + // wave that is -1 90% of the time, and 1 10% of the time. + Waveform square = Waves.square( 0.9 ); // create an LFO to be used for an amplitude envelope - Oscil myLFO = new Oscil( 0.5, 0.3, Waves.square( 0.005 ) ); - // our LFO will operate on a base amplitude - Constant baseAmp = new Constant(0.3); - // we get the final amplitude by summing the two - Summer ampSum = new Summer(); - - Summer sum = new Summer(); - - // patch everything together - // the LFO is patched into a summer along with a constant value - // and that sum is used to drive the amplitude of myBlip - baseAmp.patch( ampSum ); - myLFO.patch( ampSum ); - ampSum.patch( myBlip.amplitude ); + Oscil myLFO = new Oscil( 1, 0.3, square ); + // offset the center value of the LFO so that it outputs 0 + // for the long portion of the duty cycle + myLFO.offset.setLastValue( 0.3 ); - // the Blip is patched directly into the sum - myBlip.patch( sum ); + myLFO.patch( myBlip.amplitude ); - // and the Blip is patched through the delay into the sum. - myBlip.patch( myDelay1 ).patch( sum ); - - // patch the sum into the output - sum.patch( out ); + // and the Blip is patched through the delay into the output + myBlip.patch( myDelay ).patch( out ); } // draw is run many times @@ -73,7 +72,10 @@ void draw() // draw a line from one buffer position to the next for both channels line( x1, 50 + out.left.get(i)*50, x2, 50 + out.left.get(i+1)*50); line( x1, 150 + out.right.get(i)*50, x2, 150 + out.right.get(i+1)*50); - } + } + + text( "Delay time is " + myDelay.delTime.getLastValue(), 5, 15 ); + text( "Delay amplitude (feedback) is " + myDelay.delAmp.getLastValue(), 5, 30 ); } // when the mouse is moved, change the delay parameters @@ -81,8 +83,8 @@ void mouseMoved() { // set the delay time by the horizontal location float delayTime = map( mouseX, 0, width, 0.0001, 0.5 ); - myDelay1.setDelTime( delayTime ); + myDelay.setDelTime( delayTime ); // set the feedback factor by the vertical location - float feedbackFactor = map( mouseY, 0, height, 0.0, 0.99 ); - myDelay1.setDelAmp( feedbackFactor ); + float feedbackFactor = map( mouseY, 0, height, 0.99, 0.0 ); + myDelay.setDelAmp( feedbackFactor ); } diff --git a/java/libraries/minim/examples/filterExample/filterExample.pde b/java/libraries/minim/examples/filterExample/filterExample.pde index 3035fc5f1..2b82c52ba 100644 --- a/java/libraries/minim/examples/filterExample/filterExample.pde +++ b/java/libraries/minim/examples/filterExample/filterExample.pde @@ -1,10 +1,13 @@ -/* filterExample - is an example of using the different filters - in continuous sound. - - author: Damien Di Fede, Anderson Mills - Anderson Mills's work was supported by numediart (www.numediart.org) -*/ +/* filterExample
    + * is an example of using the different filters + * in continuous sound. + *

    + * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ + *

    + * author: Damien Di Fede, Anderson Mills
    + * Anderson Mills's work was supported by numediart (www.numediart.org) + */ // import everything necessary to make sound. import ddf.minim.*; diff --git a/java/libraries/minim/examples/frequencyModulation/frequencyModulation.pde b/java/libraries/minim/examples/frequencyModulation/frequencyModulation.pde index 8338ea098..7fcb7645a 100644 --- a/java/libraries/minim/examples/frequencyModulation/frequencyModulation.pde +++ b/java/libraries/minim/examples/frequencyModulation/frequencyModulation.pde @@ -1,10 +1,12 @@ /* frequencyModulation -

    - A simple example for doing FM (frequency modulation) using two Oscils. - Use the mouse to control the speed and range of the frequency modulation. -

    - Author: Damien Di Fede -*/ +

    + A simple example for doing FM (frequency modulation) using two Oscils. +

    + For more information about Minim and additional features, + visit http://code.compartmental.net/minim/ +

    + Author: Damien Di Fede + */ // import everything necessary to make sound. import ddf.minim.*; @@ -62,6 +64,9 @@ void draw() line( x1, 50 + out.left.get(i)*50, x2, 50 + out.left.get(i+1)*50); line( x1, 150 + out.right.get(i)*50, x2, 150 + out.right.get(i+1)*50); } + + text( "Modulation frequency: " + fm.frequency.getLastValue(), 5, 15 ); + text( "Modulation amplitude: " + fm.amplitude.getLastValue(), 5, 30 ); } // we can change the parameters of the frequency modulation Oscil @@ -71,6 +76,6 @@ void mouseMoved() float modulateAmount = map( mouseY, 0, height, 220, 1 ); float modulateFrequency = map( mouseX, 0, width, 0.1, 100 ); - fm.frequency.setLastValue( modulateFrequency ); - fm.amplitude.setLastValue( modulateAmount ); + fm.setFrequency( modulateFrequency ); + fm.setAmplitude( modulateAmount ); } diff --git a/java/libraries/minim/examples/loadFileIntoBuffer/loadFileIntoBuffer.pde b/java/libraries/minim/examples/loadFileIntoBuffer/loadFileIntoBuffer.pde index ec5c6d1b4..32db474e4 100644 --- a/java/libraries/minim/examples/loadFileIntoBuffer/loadFileIntoBuffer.pde +++ b/java/libraries/minim/examples/loadFileIntoBuffer/loadFileIntoBuffer.pde @@ -4,6 +4,9 @@ * a file from the data folder into a MultiChannelBuffer and then modifies that sample data before * using it to create a Sampler UGen. You can hear the result of this modification by hitting * the space bar. + *

    + * For more information about Minim and additional features, + * visit http://code.compartmental.net/minim/ */ import ddf.minim.*; diff --git a/java/libraries/serial/examples/SerialCallResponse/SerialCallResponse.pde b/java/libraries/serial/examples/SerialCallResponse/SerialCallResponse.pde index b68641ac0..627b27a75 100644 --- a/java/libraries/serial/examples/SerialCallResponse/SerialCallResponse.pde +++ b/java/libraries/serial/examples/SerialCallResponse/SerialCallResponse.pde @@ -33,7 +33,7 @@ void setup() { ypos = height/2; // Print a list of the serial ports, for debugging purposes: - println(Serial.list()); + printArray(Serial.list()); // I know that the first port in the serial list on my mac // is always my FTDI adaptor, so I open Serial.list()[0]. diff --git a/java/libraries/serial/examples/SerialDuplex/SerialDuplex.pde b/java/libraries/serial/examples/SerialDuplex/SerialDuplex.pde index 1a012a206..38dc2f204 100644 --- a/java/libraries/serial/examples/SerialDuplex/SerialDuplex.pde +++ b/java/libraries/serial/examples/SerialDuplex/SerialDuplex.pde @@ -22,7 +22,7 @@ void setup() { textFont(myFont); // List all the available serial ports: - println(Serial.list()); + printArray(Serial.list()); // I know that the first port in the serial list on my mac // is always my FTDI adaptor, so I open Serial.list()[0]. diff --git a/java/libraries/serial/examples/SerialMultiple/SerialMultiple.pde b/java/libraries/serial/examples/SerialMultiple/SerialMultiple.pde index cd46cbcd8..0646a4dc9 100644 --- a/java/libraries/serial/examples/SerialMultiple/SerialMultiple.pde +++ b/java/libraries/serial/examples/SerialMultiple/SerialMultiple.pde @@ -13,7 +13,7 @@ int[] dataIn = new int[2]; // a list to hold data from the serial ports void setup() { size(400, 300); // print a list of the serial ports: - println(Serial.list()); + printArray(Serial.list()); // On my machine, the first and third ports in the list // were the serial ports that my microcontrollers were // attached to. diff --git a/java/libraries/serial/src/processing/serial/Serial.java b/java/libraries/serial/src/processing/serial/Serial.java index 52463e324..befad4afa 100644 --- a/java/libraries/serial/src/processing/serial/Serial.java +++ b/java/libraries/serial/src/processing/serial/Serial.java @@ -166,7 +166,7 @@ public class Serial implements SerialPortEventListener { } } - + public boolean getCTS() { try { return port.isCTS(); @@ -189,7 +189,7 @@ public class Serial implements SerialPortEventListener { return SerialPortList.getPortProperties(portName); } - + public int last() { if (inBuffer == readOffset) { return -1; @@ -366,6 +366,10 @@ public class Serial implements SerialPortEventListener { while (0 < (toRead = port.getInputBufferBytesCount())) { // this method can be called from the context of another thread synchronized (buffer) { + // read one byte at a time if the sketch is using serialEvent + if (serialEventMethod != null) { + toRead = 1; + } // enlarge buffer if necessary if (buffer.length < inBuffer+toRead) { byte temp[] = new byte[buffer.length<<1]; @@ -376,27 +380,27 @@ public class Serial implements SerialPortEventListener { byte[] read = port.readBytes(toRead); System.arraycopy(read, 0, buffer, inBuffer, read.length); inBuffer += read.length; - if (serialEventMethod != null) { - if ((0 < bufferUntilSize && bufferUntilSize <= inBuffer-readOffset) || - (0 == bufferUntilSize && bufferUntilByte == buffer[inBuffer-1])) { - try { - // serialEvent() is invoked in the context of the current (serial) thread - // which means that serialization and atomic variables need to be used to - // guarantee reliable operation (and better not draw() etc..) - // serialAvailable() does not provide any real benefits over using - // available() and read() inside draw - but this function has no - // thread-safety issues since it's being invoked during pre in the context - // of the Processing applet - serialEventMethod.invoke(parent, new Object[] { this }); - } catch (Exception e) { - System.err.println("Error, disabling serialEvent() for "+port.getPortName()); - System.err.println(e.getLocalizedMessage()); - serialEventMethod = null; - } + } + if (serialEventMethod != null) { + if ((0 < bufferUntilSize && bufferUntilSize <= inBuffer-readOffset) || + (0 == bufferUntilSize && bufferUntilByte == buffer[inBuffer-1])) { + try { + // serialEvent() is invoked in the context of the current (serial) thread + // which means that serialization and atomic variables need to be used to + // guarantee reliable operation (and better not draw() etc..) + // serialAvailable() does not provide any real benefits over using + // available() and read() inside draw - but this function has no + // thread-safety issues since it's being invoked during pre in the context + // of the Processing applet + serialEventMethod.invoke(parent, new Object[] { this }); + } catch (Exception e) { + System.err.println("Error, disabling serialEvent() for "+port.getPortName()); + System.err.println(e.getLocalizedMessage()); + serialEventMethod = null; } } - invokeSerialAvailable = true; } + invokeSerialAvailable = true; } } catch (SerialPortException e) { throw new RuntimeException("Error reading from serial port " + e.getPortName() + ": " + e.getExceptionType()); diff --git a/java/libraries/serial/test/serial_latency/arduino/serial_latency.ino b/java/libraries/serial/test/serial_latency/arduino/serial_latency.ino new file mode 100644 index 000000000..d4711fe64 --- /dev/null +++ b/java/libraries/serial/test/serial_latency/arduino/serial_latency.ino @@ -0,0 +1,12 @@ +void setup() { + Serial.begin(115200); +} + +void loop() { + while (true) { + int in = Serial.read(); + if (in != -1) { + Serial.write(in); + } + } +} diff --git a/java/libraries/serial/test/serial_latency/serial_latency.pde b/java/libraries/serial/test/serial_latency/serial_latency.pde new file mode 100644 index 000000000..17e0a2756 --- /dev/null +++ b/java/libraries/serial/test/serial_latency/serial_latency.pde @@ -0,0 +1,53 @@ +// Arduino Duemilanove (168) on OS X 10.9 +// with either 115200 or 38400 bps +// on Processing 2.0.3 (cu & tty): 24 ms avg, 35 ms max +// on Processing 2.1b1 (cu & tty): 18 ms avg, 35 ms max + +import processing.serial.*; +Serial serial; +int start; +byte out = '@'; +int last_send = 0; +byte[] in = new byte[32768]; +long num_fail = 0; +long num_recv = 0; +int max_latency = 0; + +void setup() { + println(serial.list()); + // change this accordingly + serial = new Serial(this, serial.list()[0], 115200); + start = millis(); +} + +void draw() { + background(255); + if (0 < serial.available()) { + int recv = serial.readBytes(in); + for (int i=0; i < recv; i++) { + if (in[i] == out) { + num_recv++; + int now = millis(); + if (max_latency < now-last_send) { + max_latency = now-last_send; + } + last_send = 0; + } + } + } + if (last_send != 0 && 1000 < millis()-last_send) { + num_fail++; + last_send = 0; + println(num_fail+" bytes timed out"); + } + if (last_send == 0) { + if (out == 'Z') { + out = '@'; + } + serial.write(++out); + last_send = millis(); + } + fill(0); + text(((millis()-start)/(float)num_recv+" ms avg"), 0, height/2); + text(max_latency+" ms max", 0, height/2+20); +} diff --git a/java/libraries/serial/test/serial_throughput/arduino/serial_thoughput.ino b/java/libraries/serial/test/serial_throughput/arduino/serial_thoughput.ino new file mode 100644 index 000000000..dc950ce5a --- /dev/null +++ b/java/libraries/serial/test/serial_throughput/arduino/serial_thoughput.ino @@ -0,0 +1,9 @@ +void setup() { + Serial.begin(115200); +} + +void loop() { + while (true) { + Serial.write('.'); + } +} diff --git a/java/libraries/serial/test/serial_throughput/serial_throughput.pde b/java/libraries/serial/test/serial_throughput/serial_throughput.pde new file mode 100644 index 000000000..477fdf3bc --- /dev/null +++ b/java/libraries/serial/test/serial_throughput/serial_throughput.pde @@ -0,0 +1,32 @@ +import processing.serial.*; +Serial serial; +int start; +byte[] in = new byte[32768]; +long num_ok = 0; +long num_fail = 0; +long num_recv = 0; + +void setup() { + println(serial.list()); + // change this accordingly + serial = new Serial(this, serial.list()[0], 115200); + start = millis(); +} + +void draw() { + background(255); + if (0 < serial.available()) { + int recv = serial.readBytes(in); + for (int i=0; i < recv; i++) { + if (in[i] == '.') { + num_ok++; + } else { + num_fail++; + println("Received "+num_fail+" unexpected bytes"); + } + num_recv++; + } + } + fill(0); + text(num_recv/((millis()-start)/1000.0), 0, height/2); +} diff --git a/java/libraries/video/examples/Capture/HsvSpace/HsvSpace.pde b/java/libraries/video/examples/Capture/HsvSpace/HsvSpace.pde index 9f72fe96f..c77208410 100644 --- a/java/libraries/video/examples/Capture/HsvSpace/HsvSpace.pde +++ b/java/libraries/video/examples/Capture/HsvSpace/HsvSpace.pde @@ -80,8 +80,9 @@ void draw() { rotateY(radians(36 + leftRightAngle)); //, 0, 1, 0); rotateX(radians(-228 + upDownAngle)); //, 1, 0, 0); - if (blobby) { - stroke(0.35, 0.35, 0.25, 0.15); + strokeWeight(0.1); + if (blobby) { + stroke(0.35, 0.35, 0.25, 0.15); wireCone(MAX_RADIUS, MAX_RADIUS * CONE_HEIGHT, 18, 18); } else { diff --git a/java/libraries/video/src/processing/video/Capture.java b/java/libraries/video/src/processing/video/Capture.java index 7cad36876..8ff7de94e 100644 --- a/java/libraries/video/src/processing/video/Capture.java +++ b/java/libraries/video/src/processing/video/Capture.java @@ -107,6 +107,7 @@ public class Capture extends PImage implements PConstants { protected int reqHeight; protected boolean useBufferSink = false; + protected boolean outdatedPixels = true; protected Object bufferSink; protected Method sinkCopyMethod; protected Method sinkSetMethod; @@ -375,6 +376,7 @@ public class Capture extends PImage implements PConstants { } if (useBufferSink) { // The native buffer from gstreamer is copied to the buffer sink. + outdatedPixels = true; if (natBuffer == null) { return; } @@ -442,10 +444,32 @@ public class Capture extends PImage implements PConstants { e.printStackTrace(); } } + + // super.loadPixels() sets loaded to true, but in the useBufferSink mode, + // the contents of the pixels array is overriden by the buffers coming + // from gstreamer, so we don't want PGraphicsOpenGL replacing the OpenGL + // texture with the pixels. + setLoaded(false); + outdatedPixels = false; } } + public int get(int x, int y) { + if (outdatedPixels) loadPixels(); + return super.get(x, y); + } + + + protected void getImpl(int sourceX, int sourceY, + int sourceWidth, int sourceHeight, + PImage target, int targetX, int targetY) { + if (outdatedPixels) loadPixels(); + super.getImpl(sourceX, sourceY, sourceWidth, sourceHeight, + target, targetX, targetY); + } + + //////////////////////////////////////////////////////////// // List methods. diff --git a/java/libraries/video/src/processing/video/Movie.java b/java/libraries/video/src/processing/video/Movie.java index fcb0056e6..694f92f27 100644 --- a/java/libraries/video/src/processing/video/Movie.java +++ b/java/libraries/video/src/processing/video/Movie.java @@ -79,6 +79,7 @@ public class Movie extends PImage implements PConstants { protected boolean seeking = false; protected boolean useBufferSink = false; + protected boolean outdatedPixels = true; protected Object bufferSink; protected Method sinkCopyMethod; protected Method sinkSetMethod; @@ -487,6 +488,7 @@ public class Movie extends PImage implements PConstants { } if (useBufferSink) { // The native buffer from gstreamer is copied to the buffer sink. + outdatedPixels = true; if (natBuffer == null) { return; } @@ -565,11 +567,33 @@ public class Movie extends PImage implements PConstants { } catch (Exception e) { e.printStackTrace(); } - } + } + + // super.loadPixels() sets loaded to true, but in the useBufferSink mode, + // the contents of the pixels array is overriden by the buffers coming + // from gstreamer, so we don't want PGraphicsOpenGL replacing the OpenGL + // texture with the pixels. + setLoaded(false); + outdatedPixels = false; } } + public int get(int x, int y) { + if (outdatedPixels) loadPixels(); + return super.get(x, y); + } + + + protected void getImpl(int sourceX, int sourceY, + int sourceWidth, int sourceHeight, + PImage target, int targetX, int targetY) { + if (outdatedPixels) loadPixels(); + super.getImpl(sourceX, sourceY, sourceWidth, sourceHeight, + target, targetX, targetY); + } + + //////////////////////////////////////////////////////////// // Initialization methods.