diff --git a/android/core/src/processing/core/PApplet.java b/android/core/src/processing/core/PApplet.java index 1f2d2e576..7341146ea 100644 --- a/android/core/src/processing/core/PApplet.java +++ b/android/core/src/processing/core/PApplet.java @@ -4854,6 +4854,26 @@ public class PApplet extends Activity implements PConstants, Runnable { } + static public String getExtension(String filename) { + String extension; + + String lower = filename.toLowerCase(); + int dot = filename.lastIndexOf('.'); + if (dot == -1) { + extension = "unknown"; // no extension found + } + extension = lower.substring(dot + 1); + + // check for, and strip any parameters on the url, i.e. + // filename.jpg?blah=blah&something=that + int question = extension.indexOf('?'); + if (question != -1) { + extension = extension.substring(0, question); + } + + return extension; + } + ////////////////////////////////////////////////////////////// diff --git a/android/core/src/processing/core/PConstants.java b/android/core/src/processing/core/PConstants.java index 077692e20..cd033fb25 100644 --- a/android/core/src/processing/core/PConstants.java +++ b/android/core/src/processing/core/PConstants.java @@ -385,12 +385,26 @@ public interface PConstants { * textures with normalized coordinates */ public static final int TEXTURE2D = 0; - /** This constant identifies the nearest texture filter (point sampling) */ - //public static final int POINT = 2; // shared with shape feature - /** This constant identifies the linear texture filter, usually called bilinear sampling */ - public static final int BILINEAR = 3; - /** This constant identifies the linear/linear function to build mipmaps */ - public static final int TRILINEAR = 4; + /** Texture quality constants */ + public static final int LOW = 0; + public static final int MEDIUM = 1; + public static final int HIGH = 2; + public static final int BEST = 3; + + /** Point sampling: both magnification and minification filtering are set to nearest */ + //public static final int POINT = 2; // shared with shape feature + /** Linear sampling: magnification filtering is nearest, minification set to linear */ + public 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 within a mipmap, but + * not between different mipmaps). */ + public 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. */ + public static final int TRILINEAR = 5; + /** This constant identifies the clamp-to-edge wrapping mode */ public static final int CLAMP = 0; /** This constant identifies the repeat wrapping mode */ @@ -399,12 +413,12 @@ public interface PConstants { // shaders - static public final int FILL_SHADER_SIMPLE = 0; - static public final int FILL_SHADER_LIT = 1; - static public final int FILL_SHADER_TEX = 2; - static public final int FILL_SHADER_FULL = 3; - static public final int LINE_SHADER = 4; - static public final int POINT_SHADER = 5; + static public final int FLAT_SHADER = 0; + static public final int LIGHT_SHADER = 1; + static public final int TEXTURE_SHADER = 2; + static public final int FULL_SHADER = 3; + static public final int LINE3D_SHADER = 4; + static public final int POINT3D_SHADER = 5; // stroke modes @@ -513,7 +527,10 @@ public interface PConstants { static final int ENABLE_PERSPECTIVE_CORRECTED_LINES = 10; static final int DISABLE_PERSPECTIVE_CORRECTED_LINES = -10; - static final int HINT_COUNT = 11; + static final int DISABLE_TEXTURE_MIPMAPS = 11; + static final int ENABLE_TEXTURE_MIPMAPS = -11; + + static final int HINT_COUNT = 12; // error messages diff --git a/android/core/src/processing/core/PGraphics.java b/android/core/src/processing/core/PGraphics.java index 296960f37..81ffb51aa 100644 --- a/android/core/src/processing/core/PGraphics.java +++ b/android/core/src/processing/core/PGraphics.java @@ -127,7 +127,7 @@ public class PGraphics extends PImage implements PConstants { public boolean smooth = false; /// the anti-aliasing level for renderers that support it - protected int antialias; + protected int quality; // ........................................................ @@ -517,8 +517,11 @@ public class PGraphics extends PImage implements PConstants { * vertex() calls will be based on coordinates that are * based on the IMAGE or NORMALIZED. */ - public int textureMode; + public int textureMode = IMAGE; + public int textureWrap = CLAMP; + public int textureQuality = BEST; + /** * Current horizontal coordinate for texture, will always * be between 0 and 1, even if using textureMode(IMAGE). @@ -5048,7 +5051,7 @@ public class PGraphics extends PImage implements PConstants { * Display a warning that the specified method is only available with 3D. * @param method The method name (no parentheses) */ - static protected void showDepthWarning(String method) { + static public void showDepthWarning(String method) { showWarning(method + "() can only be used with a renderer that " + "supports 3D, such as P3D or OPENGL."); } @@ -5059,7 +5062,7 @@ public class PGraphics extends PImage implements PConstants { * can only be used with x and y parameters in this renderer. * @param method The method name (no parentheses) */ - static protected void showDepthWarningXYZ(String method) { + static public void showDepthWarningXYZ(String method) { showWarning(method + "() with x, y, and z coordinates " + "can only be used with a renderer that " + "supports 3D, such as P3D or OPENGL. " + @@ -5070,7 +5073,7 @@ public class PGraphics extends PImage implements PConstants { /** * Display a warning that the specified method is simply unavailable. */ - static protected void showMethodWarning(String method) { + static public void showMethodWarning(String method) { showWarning(method + "() is not available with this renderer."); } @@ -5080,7 +5083,7 @@ public class PGraphics extends PImage implements PConstants { * other variations are). For instance, if vertex(x, y, u, v) is not * available, but vertex(x, y) is just fine. */ - static protected void showVariationWarning(String str) { + static public void showVariationWarning(String str) { showWarning(str + " is not available with this renderer."); } @@ -5090,7 +5093,7 @@ public class PGraphics extends PImage implements PConstants { * that it could be either a completely missing function, although other * variations of it may still work properly. */ - static protected void showMissingWarning(String method) { + static public void showMissingWarning(String method) { showWarning(method + "(), or this particular variation of it, " + "is not available with this renderer."); } diff --git a/android/core/src/processing/core/PShape.java b/android/core/src/processing/core/PShape.java index 1d9e3aaec..331681e9a 100644 --- a/android/core/src/processing/core/PShape.java +++ b/android/core/src/processing/core/PShape.java @@ -336,6 +336,14 @@ public class PShape implements PConstants { } + /** + * Return true if this shape is 2D. Defaults to true. + */ + public boolean is2D() { + return true; + } + + /** * Return true if this shape is 3D. Defaults to false. */ @@ -343,6 +351,7 @@ public class PShape implements PConstants { return false; } + /////////////////////////////////////////////////////////// // @@ -663,120 +672,125 @@ public class PShape implements PConstants { g.popStyle(); } } - //////////////////////////////////////////////////////////////////////// // - // The new copy methods to put an SVG into a PShape3D, for example + // Shape copy - public PShape copy(PGraphics g) { - PShape res = null; - if (family == GROUP) { - res = g.createShape(GROUP); - copyGroup(g, res); - } else if (family == PRIMITIVE) { - res = g.createShape(kind, params); - copyPrimitive(res); - } else if (family == GEOMETRY) { - res = g.createShape(kind); - copyGeometry(res); - } else if (family == PATH) { - res = g.createShape(PATH); - copyPath(res); + + static public PShape createShape(PApplet parent, PShape src) { + PShape dest = null; + if (src.family == GROUP) { + dest = parent.createShape(GROUP); + PShape.copyGroup(parent, src, dest); + } else if (src.family == PRIMITIVE) { + dest = parent.createShape(src.kind, src.params); + PShape.copyPrimitive(src, dest); + } else if (src.family == GEOMETRY) { + dest = parent.createShape(src.kind); + PShape.copyGeometry(src, dest); + } else if (src.family == PATH) { + dest = parent.createShape(PATH); + PShape.copyPath(src, dest); } - return res; + dest.setName(src.name); + return dest; } - protected void copyGroup(PGraphics g, PShape s) { - if (matrix != null) { - s.applyMatrix(matrix); - } - copyStyles(s); - copyImage(s); - for (int i = 0; i < childCount; i++) { - PShape c = children[i].copy(g); - s.addChild(c); + static public void copyGroup(PApplet parent, PShape src, PShape dest) { + copyMatrix(src, dest); + copyStyles(src, dest); + copyImage(src, dest); + for (int i = 0; i < src.childCount; i++) { + PShape c = PShape.createShape(parent, src.children[i]); + dest.addChild(c); } } - protected void copyPrimitive(PShape s) { - if (matrix != null) { - s.applyMatrix(matrix); - } - copyStyles(s); - copyImage(s); + static public void copyPrimitive(PShape src, PShape dest) { + copyMatrix(src, dest); + copyStyles(src, dest); + copyImage(src, dest); } - protected void copyGeometry(PShape s) { - if (matrix != null) { - s.applyMatrix(matrix); - } - copyStyles(s); - copyImage(s); + + static public void copyGeometry(PShape src, PShape dest) { + copyMatrix(src, dest); + copyStyles(src, dest); + copyImage(src, dest); - if (style) { - for (int i = 0; i < vertexCount; i++) { - float[] vert = vertices[i]; + if (src.style) { + for (int i = 0; i < src.vertexCount; i++) { + float[] vert = src.vertices[i]; + + // Do we need to copy these as well? // s.ambient(vert[AR] * 255, vert[AG] * 255, vert[AB] * 255); // s.specular(vert[SPR] * 255, vert[SPG] * 255, vert[SPB] * 255); // s.emissive(vert[ER] * 255, vert[EG] * 255, vert[EB] * 255); // s.shininess(vert[SHINE]); - s.normal(vert[NX], vert[NY], vert[NZ]); - s.vertex(vert[X], vert[Y], vert[Z], vert[U], vert[V]); + dest.normal(vert[NX], vert[NY], vert[NZ]); + dest.vertex(vert[X], vert[Y], vert[Z], vert[U], vert[V]); } } else { - for (int i = 0; i < vertexCount; i++) { - float[] vert = vertices[i]; + for (int i = 0; i < src.vertexCount; i++) { + float[] vert = src.vertices[i]; if (vert[PGraphics.Z] == 0) { - s.vertex(vert[X], vert[Y]); + dest.vertex(vert[X], vert[Y]); } else { - s.vertex(vert[X], vert[Y], vert[Z]); + dest.vertex(vert[X], vert[Y], vert[Z]); } } } - s.end(); + dest.end(); } - protected void copyPath(PShape s) { - if (matrix != null) { - s.applyMatrix(matrix); - } - copyStyles(s); - copyImage(s); - s.close = close; - s.setPath(vertexCount, vertices, vertexCodeCount, vertexCodes); - + + static public void copyPath(PShape src, PShape dest) { + copyMatrix(src, dest); + copyStyles(src, dest); + copyImage(src, dest); + dest.close = src.close; + dest.setPath(src.vertexCount, src.vertices, src.vertexCodeCount, src.vertexCodes); } - protected void copyStyles(PShape s) { - if (stroke) { - s.stroke = true; - s.strokeColor = strokeColor; - s.strokeWeight = strokeWeight; - s.strokeCap = strokeCap; - s.strokeJoin = strokeJoin; + + static public void copyMatrix(PShape src, PShape dest) { + if (src.matrix != null) { + dest.applyMatrix(src.matrix); + } + } + + static public void copyStyles(PShape src, PShape dest) { + if (src.stroke) { + dest.stroke = true; + dest.strokeColor = src.strokeColor; + dest.strokeWeight = src.strokeWeight; + dest.strokeCap = src.strokeCap; + dest.strokeJoin = src.strokeJoin; } else { - s.stroke = false; + dest.stroke = false; } - if (fill) { - s.fill = true; - s.fillColor = fillColor; + if (src.fill) { + dest.fill = true; + dest.fillColor = src.fillColor; } else { - s.fill = false; + dest.fill = false; } } - protected void copyImage(PShape s) { - if (image != null) { - s.texture(image); + + static public void copyImage(PShape src, PShape dest) { + if (src.image != null) { + dest.texture(src.image); } } + //////////////////////////////////////////////////////////////////////// diff --git a/android/core/src/processing/opengl/geom/LinePath.java b/android/core/src/processing/opengl/LinePath.java similarity index 98% rename from android/core/src/processing/opengl/geom/LinePath.java rename to android/core/src/processing/opengl/LinePath.java index b58b48bf1..fc7f8b55b 100644 --- a/android/core/src/processing/opengl/geom/LinePath.java +++ b/android/core/src/processing/opengl/LinePath.java @@ -1,3 +1,5 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + /* * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -23,7 +25,7 @@ * have any questions. */ -package processing.opengl.geom; +package processing.opengl; import processing.core.PMatrix2D; @@ -392,7 +394,7 @@ public class LinePath { float miterlimit, PMatrix2D transform) { final LinePath dest = new LinePath(); - strokeTo(src, weight, caps, join, miterlimit, transform, new LineSink() { + strokeTo(src, weight, caps, join, miterlimit, transform, new LineStroker() { public void moveTo(int x0, int y0) { dest.moveTo(S15_16ToFloat(x0), S15_16ToFloat(y0)); } @@ -418,7 +420,7 @@ public class LinePath { private static void strokeTo(LinePath src, float width, int caps, int join, float miterlimit, PMatrix2D transform, - LineSink lsink) { + LineStroker lsink) { lsink = new LineStroker(lsink, FloatToS15_16(width), caps, join, FloatToS15_16(miterlimit), transform == null ? identity : transform); @@ -428,7 +430,7 @@ public class LinePath { } - private static void pathTo(PathIterator pi, LineSink lsink) { + private static void pathTo(PathIterator pi, LineStroker lsink) { float coords[] = new float[2]; while (!pi.isDone()) { switch (pi.currentSegment(coords)) { diff --git a/android/core/src/processing/opengl/geom/LineStroker.java b/android/core/src/processing/opengl/LineStroker.java similarity index 90% rename from android/core/src/processing/opengl/geom/LineStroker.java rename to android/core/src/processing/opengl/LineStroker.java index 883e192e1..3e770e9bb 100644 --- a/android/core/src/processing/opengl/geom/LineStroker.java +++ b/android/core/src/processing/opengl/LineStroker.java @@ -1,3 +1,5 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + /* * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. @@ -23,28 +25,20 @@ * questions. */ -package processing.opengl.geom; +package processing.opengl; import processing.core.PMatrix2D; -public class LineStroker extends LineSink { - LineSink output; - - int lineWidth; - - int capStyle; - - int joinStyle; - - int miterLimit; - - int m00, m01; - - int m10, m11; - - int lineWidth2; - - long scaledLineWidth2; +public class LineStroker { + private LineStroker output; +// private int lineWidth; + private int capStyle; + private int joinStyle; +// private int miterLimit; + private int m00, m01; + private int m10, m11; + private int lineWidth2; + private long scaledLineWidth2; // For any pen offset (pen_dx, pen_dy) that does not depend on // the line orientation, the pen should be transformed so that: @@ -59,45 +53,30 @@ public class LineStroker extends LineSink { // // pen_dx'(r, theta) = r*(m00*cos(theta) + m01*sin(theta)) // pen_dy'(r, theta) = r*(m10*cos(theta) + m11*sin(theta)) - int numPenSegments; + private int numPenSegments; + private int[] pen_dx; + private int[] pen_dy; - int[] pen_dx; - - int[] pen_dy; - - boolean[] penIncluded; - - int[] join; - - int[] offset = new int[2]; - - int[] reverse = new int[100]; - - int[] miter = new int[2]; - - long miterLimitSq; - - int prev; - - int rindex; - - boolean started; - - boolean lineToOrigin; - - boolean joinToOrigin; - - int sx0, sy0, sx1, sy1, x0, y0, x1, y1; - - int mx0, my0, mx1, my1, omx, omy; - - int lx0, ly0, lx1, ly1, lx0p, ly0p, px0, py0; - - double m00_2_m01_2; - - double m10_2_m11_2; - - double m00_m10_m01_m11; + private boolean[] penIncluded; + private int[] join; + private int[] offset = new int[2]; + private int[] reverse = new int[100]; + private int[] miter = new int[2]; + private long miterLimitSq; + private int prev; + private int rindex; + private boolean started; + private boolean lineToOrigin; + private boolean joinToOrigin; +// private int sx0, sy0, sx1, sy1, x0, y0, x1, y1; +// private int mx0, my0, mx1, my1, omx, omy; +// private int lx0, ly0, lx1, ly1, lx0p, ly0p, px0, py0; + private int sx0, sy0, sx1, sy1, x0, y0; + private int mx0, my0, omx, omy; + private int px0, py0; + private double m00_2_m01_2; + private double m10_2_m11_2; + private double m00_m10_m01_m11; /** * Empty constructor. setOutput and setParameters @@ -110,7 +89,7 @@ public class LineStroker extends LineSink { * Constructs a LineStroker. * * @param output - * an output LineSink. + * an output LineStroker. * @param lineWidth * the desired line width in pixels, in S15.16 format. * @param capStyle @@ -127,19 +106,19 @@ public class LineStroker extends LineSink { * required in order to produce consistently shaped end caps and * joins. */ - public LineStroker(LineSink output, int lineWidth, int capStyle, int joinStyle, + public LineStroker(LineStroker output, int lineWidth, int capStyle, int joinStyle, int miterLimit, PMatrix2D transform) { setOutput(output); setParameters(lineWidth, capStyle, joinStyle, miterLimit, transform); } /** - * Sets the output LineSink of this LineStroker. + * Sets the output LineStroker of this LineStroker. * * @param output - * an output LineSink. + * an output LineStroker. */ - public void setOutput(LineSink output) { + public void setOutput(LineStroker output) { this.output = output; } @@ -169,12 +148,12 @@ public class LineStroker extends LineSink { this.m10 = LinePath.FloatToS15_16(transform.m10); this.m11 = LinePath.FloatToS15_16(transform.m11); - this.lineWidth = lineWidth; +// this.lineWidth = lineWidth; this.lineWidth2 = lineWidth >> 1; this.scaledLineWidth2 = ((long) m00 * lineWidth2) >> 16; this.capStyle = capStyle; this.joinStyle = joinStyle; - this.miterLimit = miterLimit; +// this.miterLimit = miterLimit; this.m00_2_m01_2 = (double) m00 * m00 + (double) m01 * m01; this.m10_2_m11_2 = (double) m10 * m10 + (double) m11 * m11; @@ -203,7 +182,7 @@ public class LineStroker extends LineSink { for (int i = 0; i < numPenSegments; i++) { double r = lineWidth / 2.0; - double theta = (double) i * 2.0 * Math.PI / numPenSegments; + double theta = i * 2 * Math.PI / numPenSegments; double cos = Math.cos(theta); double sin = Math.sin(theta); @@ -528,12 +507,12 @@ public class LineStroker extends LineSink { emitLineTo(x0 - mx, y0 - my, true); emitLineTo(x1 - mx, y1 - my, true); - lx0 = x1 + mx; - ly0 = y1 + my; - lx0p = x1 - mx; - ly0p = y1 - my; - lx1 = x1; - ly1 = y1; +// lx0 = x1 + mx; +// ly0 = y1 + my; +// lx0p = x1 - mx; +// ly0p = y1 - my; +// lx1 = x1; +// ly1 = y1; this.omx = mx; this.omy = my; @@ -639,8 +618,8 @@ public class LineStroker extends LineSink { long lineLength(long ldx, long ldy) { long ldet = ((long) m00 * m11 - (long) m01 * m10) >> 16; - long la = ((long) ldy * m00 - (long) ldx * m10) / ldet; - long lb = ((long) ldy * m01 - (long) ldx * m11) / ldet; + long la = (ldy * m00 - ldx * m10) / ldet; + long lb = (ldy * m01 - ldx * m11) / ldet; long llen = (int) LinePath.hypot(la, lb); return llen; } @@ -650,8 +629,8 @@ public class LineStroker extends LineSink { drawRoundJoin(x0, y0, omx, omy, -omx, -omy, 1, false, false, ROUND_JOIN_THRESHOLD); } else if (capStyle == LinePath.CAP_SQUARE) { - long ldx = (long) (px0 - x0); - long ldy = (long) (py0 - y0); + long ldx = px0 - x0; + long ldy = py0 - y0; long llen = lineLength(ldx, ldy); if (0 < llen) { long s = (long) lineWidth2 * 65536 / llen; @@ -673,8 +652,8 @@ public class LineStroker extends LineSink { drawRoundJoin(sx0, sy0, -mx0, -my0, mx0, my0, 1, false, false, ROUND_JOIN_THRESHOLD); } else if (capStyle == LinePath.CAP_SQUARE) { - long ldx = (long) (sx1 - sx0); - long ldy = (long) (sy1 - sy0); + long ldx = sx1 - sx0; + long ldy = sy1 - sy0; long llen = lineLength(ldx, ldy); if (0 < llen) { long s = (long) lineWidth2 * 65536 / llen; diff --git a/android/core/src/processing/opengl/PFontTexture.java b/android/core/src/processing/opengl/PFontTexture.java index 56dc04b89..f06dc8575 100644 --- a/android/core/src/processing/opengl/PFontTexture.java +++ b/android/core/src/processing/opengl/PFontTexture.java @@ -3,7 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2004-08 Ben Fry and Casey Reas + Copyright (c) 2011-12 Ben Fry and Casey Reas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -23,11 +23,13 @@ package processing.opengl; +import processing.core.PApplet; +import processing.core.PConstants; +import processing.core.PFont; +import processing.core.PImage; + import java.util.HashMap; -import processing.core.*; - - /** * All the infrastructure needed for optimized font rendering * in OpenGL. Basically, this special class is needed because @@ -50,6 +52,7 @@ class PFontTexture implements PConstants { protected PGraphicsOpenGL pg; protected PGL pgl; protected PFont font; + protected boolean is3D; protected int maxTexWidth; protected int maxTexHeight; @@ -63,11 +66,12 @@ class PFontTexture implements PConstants { protected TextureInfo[] glyphTexinfos; protected HashMap texinfoMap; - public PFontTexture(PApplet parent, PFont font, int maxw, int maxh) { + public PFontTexture(PApplet parent, PFont font, int maxw, int maxh, boolean is3D) { this.parent = parent; this.font = font; pg = (PGraphicsOpenGL)parent.g; pgl = pg.pgl; + this.is3D = is3D; initTexture(maxw, maxh); } @@ -113,7 +117,17 @@ class PFontTexture implements PConstants { resize = false; } - PTexture tex = new PTexture(parent, w, h, new PTexture.Parameters(ARGB, BILINEAR)); + PTexture tex; + if (is3D) { + // Bilinear sampling ensures that the texture doesn't look pixelated either + // when it is magnified or minified... + tex = new PTexture(parent, w, h, new PTexture.Parameters(ARGB, 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 PTexture(parent, w, h, new PTexture.Parameters(ARGB, LINEAR, false)); + } if (textures == null) { textures = new PTexture[1]; diff --git a/android/core/src/processing/opengl/PFramebuffer.java b/android/core/src/processing/opengl/PFramebuffer.java index d24fde7bc..52610130d 100644 --- a/android/core/src/processing/opengl/PFramebuffer.java +++ b/android/core/src/processing/opengl/PFramebuffer.java @@ -1,31 +1,33 @@ /* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* - Part of the Processing project - http://processing.org + Part of the Processing project - http://processing.org - Copyright (c) 2010 Ben Fry and Casey Reas + Copyright (c) 2011-12 Ben Fry and Casey Reas - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License version 2.1 as published by the Free Software Foundation. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General - Public License along with this library; if not, write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA - */ + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ package processing.opengl; -import java.nio.IntBuffer; import processing.core.PApplet; import processing.core.PConstants; +import java.nio.IntBuffer; + /** * Encapsulates a Frame Buffer Object for offscreen rendering. * When created with onscreen == true, it represents the normal @@ -36,6 +38,7 @@ import processing.core.PConstants; * * By Andres Colubri. */ + public class PFramebuffer implements PConstants { protected PApplet parent; protected PGraphicsOpenGL pg; diff --git a/android/core/src/processing/opengl/PGL.java b/android/core/src/processing/opengl/PGL.java index a05c81593..fa430d5cb 100644 --- a/android/core/src/processing/opengl/PGL.java +++ b/android/core/src/processing/opengl/PGL.java @@ -109,7 +109,7 @@ public class PGL { /** Maximum length of linear paths to be stroked with the * full algorithm that generates accurate caps and joins. */ - public static final int MAX_CAPS_JOINS_LENGTH = 500; + public static final int MAX_CAPS_JOINS_LENGTH = 1000; /** Minimum array size to use arrayCopy method(). **/ static protected final int MIN_ARRAYCOPY_SIZE = 2; @@ -184,9 +184,10 @@ public class PGL { public static final int GL_UNSIGNED_SHORT = GLES20.GL_UNSIGNED_SHORT; public static final int GL_FLOAT = GLES20.GL_FLOAT; - public static final int GL_NEAREST = GLES20.GL_NEAREST; - public static final int GL_LINEAR = GLES20.GL_LINEAR; - public static final int GL_LINEAR_MIPMAP_LINEAR = GLES20.GL_LINEAR_MIPMAP_LINEAR; + public static final int GL_NEAREST = GLES20.GL_NEAREST; + public static final int GL_LINEAR = GLES20.GL_LINEAR; + public static final int GL_LINEAR_MIPMAP_NEAREST = GLES20.GL_LINEAR_MIPMAP_NEAREST; + public static final int GL_LINEAR_MIPMAP_LINEAR = GLES20.GL_LINEAR_MIPMAP_LINEAR; public static final int GL_CLAMP_TO_EDGE = GLES20.GL_CLAMP_TO_EDGE; public static final int GL_REPEAT = GLES20.GL_REPEAT; diff --git a/android/core/src/processing/opengl/PGraphics2D.java b/android/core/src/processing/opengl/PGraphics2D.java index 3b7c233ce..01f1b7128 100644 --- a/android/core/src/processing/opengl/PGraphics2D.java +++ b/android/core/src/processing/opengl/PGraphics2D.java @@ -22,28 +22,436 @@ package processing.opengl; +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.GZIPInputStream; + +import processing.core.PApplet; +import processing.core.PGraphics; +import processing.core.PMatrix3D; +import processing.core.PShape; +import processing.core.PShapeSVG; +import processing.data.XML; public class PGraphics2D extends PGraphicsOpenGL { public PGraphics2D() { super(); - //hints[ENABLE_ACCURATE_2D] = true; hints[ENABLE_PERSPECTIVE_CORRECTED_LINES] = false; } - /** - * Return true if this renderer supports 2D drawing. Defaults to true. - */ + + ////////////////////////////////////////////////////////////// + + // RENDERER SUPPORT QUERIES + + public boolean is2D() { return true; } - - /** - * Return true if this renderer supports 2D drawing. Defaults to false. - */ + public boolean is3D() { return false; } + + ////////////////////////////////////////////////////////////// + + // HINTS + + + public void hint(int which) { + if (which == ENABLE_PERSPECTIVE_CORRECTED_LINES) { + showWarning("2D lines cannot be perspective-corrected."); + return; + } + super.hint(which); + } + + + ////////////////////////////////////////////////////////////// + + // SHAPE + + + public void shape(PShape shape) { + if (shape.is2D()) { + super.shape(shape); + } else { + showWarning("The shape object is not 2D, cannot be displayed with this renderer"); + } + } + + + public void shape(PShape shape, float x, float y) { + if (shape.is2D()) { + super.shape(shape, x, y); + } else { + showWarning("The shape object is not 2D, cannot be displayed with this renderer"); + } + } + + + public void shape(PShape shape, float a, float b, float c, float d) { + if (shape.is2D()) { + super.shape(shape, a, b, c, d); + } else { + showWarning("The shape object is not 2D, cannot be displayed with this renderer"); + } + } + + + public void shape(PShape shape, float x, float y, float z) { + showDepthWarningXYZ("shape"); + } + + + public void shape(PShape shape, float x, float y, float z, float c, float d, float e) { + showDepthWarningXYZ("shape"); + } + + + ////////////////////////////////////////////////////////////// + + // SHAPE I/O + + + static protected boolean isSupportedExtension(String extension) { + return extension.equals("svg") || extension.equals("svgz"); + } + + + static protected PShape2D loadShapeImpl(PGraphics pg, String filename, String extension) { + PShapeSVG svg = null; + + if (extension.equals("svg")) { + svg = new PShapeSVG(pg.parent, filename); + + } else if (extension.equals("svgz")) { + try { + InputStream input = new GZIPInputStream(pg.parent.createInput(filename)); + XML xml = new XML(PApplet.createReader(input)); + svg = new PShapeSVG(xml); + } catch (IOException e) { + e.printStackTrace(); + } + } + + if (svg != null) { + PShape2D p2d = PShape2D.createShape(pg.parent, svg); + return p2d; + } else { + return null; + } + } + + + ////////////////////////////////////////////////////////////// + + // SHAPE CREATION + + + public PShape createShape(PShape source) { + return PShape2D.createShape(parent, source); + } + + + public PShape createShape() { + return createShape(POLYGON); + } + + + public PShape createShape(int type) { + return createShapeImpl(parent, type); + } + + + public PShape createShape(int kind, float... p) { + return createShapeImpl(parent, kind, p); + } + + + static protected PShape2D createShapeImpl(PApplet parent, int type) { + PShape2D shape = null; + if (type == PShape.GROUP) { + shape = new PShape2D(parent, PShape.GROUP); + } else if (type == PShape.PATH) { + shape = new PShape2D(parent, PShape.PATH); + } else if (type == POINTS) { + shape = new PShape2D(parent, PShape.GEOMETRY); + shape.setKind(POINTS); + } else if (type == LINES) { + shape = new PShape2D(parent, PShape.GEOMETRY); + shape.setKind(LINES); + } else if (type == TRIANGLE || type == TRIANGLES) { + shape = new PShape2D(parent, PShape.GEOMETRY); + shape.setKind(TRIANGLES); + } else if (type == TRIANGLE_FAN) { + shape = new PShape2D(parent, PShape.GEOMETRY); + shape.setKind(TRIANGLE_FAN); + } else if (type == TRIANGLE_STRIP) { + shape = new PShape2D(parent, PShape.GEOMETRY); + shape.setKind(TRIANGLE_STRIP); + } else if (type == QUAD || type == QUADS) { + shape = new PShape2D(parent, PShape.GEOMETRY); + shape.setKind(QUADS); + } else if (type == QUAD_STRIP) { + shape = new PShape2D(parent, PShape.GEOMETRY); + shape.setKind(QUAD_STRIP); + } else if (type == POLYGON) { + shape = new PShape2D(parent, PShape.GEOMETRY); + shape.setKind(POLYGON); + } + return shape; + } + + + static protected PShape2D createShapeImpl(PApplet parent, int kind, float... p) { + PShape2D shape = null; + int len = p.length; + + if (kind == POINT) { + if (len != 2) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape2D(parent, PShape.PRIMITIVE); + shape.setKind(POINT); + } else if (kind == LINE) { + if (len != 4) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape2D(parent, PShape.PRIMITIVE); + shape.setKind(LINE); + } else if (kind == TRIANGLE) { + if (len != 6) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape2D(parent, PShape.PRIMITIVE); + shape.setKind(TRIANGLE); + } else if (kind == QUAD) { + if (len != 8) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape2D(parent, PShape.PRIMITIVE); + shape.setKind(QUAD); + } else if (kind == RECT) { + if (len != 4 && len != 5 && len != 8) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape2D(parent, PShape.PRIMITIVE); + shape.setKind(RECT); + } else if (kind == ELLIPSE) { + if (len != 4) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape2D(parent, PShape.PRIMITIVE); + shape.setKind(ELLIPSE); + } else if (kind == ARC) { + if (len != 6) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape2D(parent, PShape.PRIMITIVE); + shape.setKind(ARC); + } else if (kind == BOX) { + showWarning("Primitive not supported in 2D"); + } else if (kind == SPHERE) { + showWarning("Primitive not supported in 2D"); + } else { + showWarning("Unrecognized primitive type"); + } + + if (shape != null) { + shape.setParams(p); + } + + return shape; + } + + + ////////////////////////////////////////////////////////////// + + // BEZIER VERTICES + + + public void bezierVertex(float x2, float y2, float z2, + float x3, float y3, float z3, + float x4, float y4, float z4) { + showDepthWarningXYZ("bezierVertex"); + } + + + ////////////////////////////////////////////////////////////// + + // QUADRATIC BEZIER VERTICES + + + public void quadraticVertex(float x2, float y2, float z2, + float x4, float y4, float z4) { + showDepthWarningXYZ("quadVertex"); + } + + + ////////////////////////////////////////////////////////////// + + // CURVE VERTICES + + + public void curveVertex(float x, float y, float z) { + showDepthWarningXYZ("curveVertex"); + } + + + ////////////////////////////////////////////////////////////// + + // BOX + + + public void box(float w, float h, float d) { + showMethodWarning("box"); + } + + + ////////////////////////////////////////////////////////////// + + // SPHERE + + + public void sphere(float r) { + showMethodWarning("sphere"); + } + + + ////////////////////////////////////////////////////////////// + + // VERTEX SHAPES + + + public void vertex(float x, float y, float z) { + showDepthWarningXYZ("vertex"); + } + + public void vertex(float x, float y, float z, float u, float v) { + showDepthWarningXYZ("vertex"); + } + + ////////////////////////////////////////////////////////////// + + // MATRIX TRANSFORMATIONS + + public void translate(float tx, float ty, float tz) { + showVariationWarning("translate"); + } + + public void rotateX(float angle) { + showDepthWarning("rotateX"); + } + + public void rotateY(float angle) { + showDepthWarning("rotateY"); + } + + public void rotateZ(float angle) { + showDepthWarning("rotateZ"); + } + + public void rotate(float angle, float vx, float vy, float vz) { + showVariationWarning("rotate"); + } + + public void applyMatrix(PMatrix3D source) { + showVariationWarning("applyMatrix"); + } + + public void applyMatrix(float n00, float n01, float n02, float n03, + float n10, float n11, float n12, float n13, + float n20, float n21, float n22, float n23, + float n30, float n31, float n32, float n33) { + showVariationWarning("applyMatrix"); + } + + public void scale(float sx, float sy, float sz) { + showDepthWarningXYZ("scale"); + } + + ////////////////////////////////////////////////////////////// + + // SCREEN AND MODEL COORDS + + public float screenX(float x, float y, float z) { + showDepthWarningXYZ("screenX"); + return 0; + } + + public float screenY(float x, float y, float z) { + showDepthWarningXYZ("screenY"); + return 0; + } + + public float screenZ(float x, float y, float z) { + showDepthWarningXYZ("screenZ"); + return 0; + } + + public PMatrix3D getMatrix(PMatrix3D target) { + showVariationWarning("getMatrix"); + return target; + } + + public void setMatrix(PMatrix3D source) { + showVariationWarning("setMatrix"); + } + + ////////////////////////////////////////////////////////////// + + // LIGHTS + + public void lights() { + showMethodWarning("lights"); + } + + public void noLights() { + showMethodWarning("noLights"); + } + + public void ambientLight(float red, float green, float blue) { + showMethodWarning("ambientLight"); + } + + public void ambientLight(float red, float green, float blue, + float x, float y, float z) { + showMethodWarning("ambientLight"); + } + + public void directionalLight(float red, float green, float blue, + float nx, float ny, float nz) { + showMethodWarning("directionalLight"); + } + + public void pointLight(float red, float green, float blue, + float x, float y, float z) { + showMethodWarning("pointLight"); + } + + public void spotLight(float red, float green, float blue, + float x, float y, float z, + float nx, float ny, float nz, + float angle, float concentration) { + showMethodWarning("spotLight"); + } + + public void lightFalloff(float constant, float linear, float quadratic) { + showMethodWarning("lightFalloff"); + } + + public void lightSpecular(float v1, float v2, float v3) { + showMethodWarning("lightSpecular"); + } } \ No newline at end of file diff --git a/android/core/src/processing/opengl/PGraphics3D.java b/android/core/src/processing/opengl/PGraphics3D.java index ca925692e..81fb8f64e 100644 --- a/android/core/src/processing/opengl/PGraphics3D.java +++ b/android/core/src/processing/opengl/PGraphics3D.java @@ -22,6 +22,550 @@ package processing.opengl; +import java.io.BufferedReader; +import java.util.ArrayList; +import java.util.Hashtable; + +import processing.core.PApplet; +import processing.core.PGraphics; +import processing.core.PImage; +import processing.core.PShape; +import processing.core.PVector; public class PGraphics3D extends PGraphicsOpenGL { + + ////////////////////////////////////////////////////////////// + + // RENDERER SUPPORT QUERIES + + + public boolean is2D() { + return false; + } + + + public boolean is3D() { + return true; + } + + + ////////////////////////////////////////////////////////////// + + // SHAPE I/O + + + static protected boolean isSupportedExtension(String extension) { + return extension.equals("obj"); + } + + + static protected PShape loadShapeImpl(PGraphics pg, String filename, String ext) { + ArrayList vertices = new ArrayList(); + ArrayList normals = new ArrayList(); + ArrayList textures = new ArrayList(); + ArrayList faces = new ArrayList(); + ArrayList materials = new ArrayList(); + + BufferedReader reader = pg.parent.createReader(filename); + parseOBJ(pg.parent, reader, vertices, normals, textures, faces, materials); + + int prevColorMode = pg.colorMode; + float prevColorModeX = pg.colorModeX; + float prevColorModeY = pg.colorModeY; + float prevColorModeZ = pg.colorModeZ; + float prevColorModeA = pg.colorModeA; + boolean prevStroke = pg.stroke; + int prevTextureMode = pg.textureMode; + pg.colorMode(RGB, 1); + pg.stroke = false; + pg.textureMode = NORMAL; + + // The OBJ geometry is stored in a group shape, + // with each face in a separate child geometry + // shape. + PShape root = createShapeImpl(pg.parent, GROUP); + + int mtlIdxCur = -1; + OBJMaterial mtl = null; + for (int i = 0; i < faces.size(); i++) { + OBJFace face = faces.get(i); + + // Getting current material. + if (mtlIdxCur != face.matIdx) { + mtlIdxCur = PApplet.max(0, face.matIdx); // To make sure that at least we get the default material. + mtl = materials.get(mtlIdxCur); + } + + // Creating child shape for current face. + PShape child; + if (face.vertIdx.size() == 3) { + child = createShapeImpl(pg.parent, TRIANGLES); // Face is a triangle, so using appropriate shape kind. + } else if (face.vertIdx.size() == 4) { + child = createShapeImpl(pg.parent, QUADS); // Face is a quad, so using appropriate shape kind. + } else { + child = createShapeImpl(pg.parent, POLYGON); // Face is a general polygon + } + + // Setting material properties for the new face + child.fill(mtl.kd.x, mtl.kd.y, mtl.kd.z); + child.ambient(mtl.ka.x, mtl.ka.y, mtl.ka.z); + child.specular(mtl.ks.x, mtl.ks.y, mtl.ks.z); + child.shininess(mtl.ns); + if (mtl.kdMap != null) { + // If current material is textured, then tinting the texture using the diffuse color. + child.tint(mtl.kd.x, mtl.kd.y, mtl.kd.z, mtl.d); + } + + for (int j = 0; j < face.vertIdx.size(); j++){ + int vertIdx, normIdx; + PVector vert, norms; + + vert = norms = null; + + vertIdx = face.vertIdx.get(j).intValue() - 1; + vert = vertices.get(vertIdx); + + if (j < face.normIdx.size()) { + normIdx = face.normIdx.get(j).intValue() - 1; + if (-1 < normIdx) { + norms = normals.get(normIdx); + } + } + + if (mtl != null && mtl.kdMap != null) { + // This face is textured. + int texIdx; + PVector tex = null; + + if (j < face.texIdx.size()) { + texIdx = face.texIdx.get(j).intValue() - 1; + if (-1 < texIdx) { + tex = textures.get(texIdx); + } + } + + child.texture(mtl.kdMap); + if (norms != null) { + child.normal(norms.x, norms.y, norms.z); + } + if (tex != null) { + child.vertex(vert.x, vert.y, vert.z, tex.x, tex.y); + } else { + child.vertex(vert.x, vert.y, vert.z); + } + } else { + // This face is not textured. + if (norms != null) { + child.normal(norms.x, norms.y, norms.z); + } + child.vertex(vert.x, vert.y, vert.z); + } + } + + child.end(CLOSE); + root.addChild(child); + } + + pg.colorMode(prevColorMode, prevColorModeX, prevColorModeY, prevColorModeZ, prevColorModeA); + pg.stroke = prevStroke; + pg.textureMode = prevTextureMode; + + return root; + } + + + ////////////////////////////////////////////////////////////// + + // SHAPE CREATION + + + public PShape createShape(PShape source) { + return PShape3D.createShape(parent, source); + } + + + public PShape createShape() { + return createShape(POLYGON); + } + + + public PShape createShape(int type) { + return createShapeImpl(parent, type); + } + + + public PShape createShape(int kind, float... p) { + return createShapeImpl(parent, kind, p); + } + + + static protected PShape3D createShapeImpl(PApplet parent, int type) { + PShape3D shape = null; + if (type == PShape.GROUP) { + shape = new PShape3D(parent, PShape.GROUP); + } else if (type == PShape.PATH) { + shape = new PShape3D(parent, PShape.PATH); + } else if (type == POINTS) { + shape = new PShape3D(parent, PShape.GEOMETRY); + shape.setKind(POINTS); + } else if (type == LINES) { + shape = new PShape3D(parent, PShape.GEOMETRY); + shape.setKind(LINES); + } else if (type == TRIANGLE || type == TRIANGLES) { + shape = new PShape3D(parent, PShape.GEOMETRY); + shape.setKind(TRIANGLES); + } else if (type == TRIANGLE_FAN) { + shape = new PShape3D(parent, PShape.GEOMETRY); + shape.setKind(TRIANGLE_FAN); + } else if (type == TRIANGLE_STRIP) { + shape = new PShape3D(parent, PShape.GEOMETRY); + shape.setKind(TRIANGLE_STRIP); + } else if (type == QUAD || type == QUADS) { + shape = new PShape3D(parent, PShape.GEOMETRY); + shape.setKind(QUADS); + } else if (type == QUAD_STRIP) { + shape = new PShape3D(parent, PShape.GEOMETRY); + shape.setKind(QUAD_STRIP); + } else if (type == POLYGON) { + shape = new PShape3D(parent, PShape.GEOMETRY); + shape.setKind(POLYGON); + } + return shape; + } + + + static protected PShape3D createShapeImpl(PApplet parent, int kind, float... p) { + PShape3D shape = null; + int len = p.length; + + if (kind == POINT) { + if (len != 2 && len != 3) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape3D(parent, PShape.PRIMITIVE); + shape.setKind(POINT); + } else if (kind == LINE) { + if (len != 4 && len != 6) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape3D(parent, PShape.PRIMITIVE); + shape.setKind(LINE); + } else if (kind == TRIANGLE) { + if (len != 6) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape3D(parent, PShape.PRIMITIVE); + shape.setKind(TRIANGLE); + } else if (kind == QUAD) { + if (len != 8) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape3D(parent, PShape.PRIMITIVE); + shape.setKind(QUAD); + } else if (kind == RECT) { + if (len != 4 && len != 5 && len != 8) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape3D(parent, PShape.PRIMITIVE); + shape.setKind(RECT); + } else if (kind == ELLIPSE) { + if (len != 4) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape3D(parent, PShape.PRIMITIVE); + shape.setKind(ELLIPSE); + } else if (kind == ARC) { + if (len != 6) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape3D(parent, PShape.PRIMITIVE); + shape.setKind(ARC); + } else if (kind == BOX) { + if (len != 1 && len != 3) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape3D(parent, PShape.PRIMITIVE); + shape.setKind(BOX); + } else if (kind == SPHERE) { + if (len != 1) { + showWarning("Wrong number of parameters"); + return null; + } + shape = new PShape3D(parent, PShape.PRIMITIVE); + shape.setKind(SPHERE); + } else { + showWarning("Unrecognized primitive type"); + } + + if (shape != null) { + shape.setParams(p); + } + + return shape; + } + + + ////////////////////////////////////////////////////////////// + + // OBJ LOADING + + + static protected void parseOBJ(PApplet parent, + BufferedReader reader, ArrayList vertices, + ArrayList normals, + ArrayList textures, + ArrayList faces, + ArrayList materials) { + Hashtable mtlTable = new Hashtable(); + int mtlIdxCur = -1; + boolean readv, readvn, readvt; + try { + + readv = readvn = readvt = false; + String line; + String gname = "object"; + while ((line = reader.readLine()) != null) { + // Parse the line. + + // The below patch/hack comes from Carlos Tomas Marti and is a + // fix for single backslashes in Rhino obj files + + // BEGINNING OF RHINO OBJ FILES HACK + // Statements can be broken in multiple lines using '\' at the + // end of a line. + // In regular expressions, the backslash is also an escape + // character. + // The regular expression \\ matches a single backslash. This + // regular expression as a Java string, becomes "\\\\". + // That's right: 4 backslashes to match a single one. + while (line.contains("\\")) { + line = line.split("\\\\")[0]; + final String s = reader.readLine(); + if (s != null) + line += s; + } + // END OF RHINO OBJ FILES HACK + + String[] elements = line.split("\\s+"); + // if not a blank line, process the line. + if (elements.length > 0) { + if (elements[0].equals("v")) { + // vertex + PVector tempv = new PVector(Float.valueOf(elements[1]).floatValue(), + Float.valueOf(elements[2]).floatValue(), + Float.valueOf(elements[3]).floatValue()); + vertices.add(tempv); + readv = true; + } else if (elements[0].equals("vn")) { + // normal + PVector tempn = new PVector(Float.valueOf(elements[1]).floatValue(), + Float.valueOf(elements[2]).floatValue(), + Float.valueOf(elements[3]).floatValue()); + normals.add(tempn); + readvn = true; + } else if (elements[0].equals("vt")) { + // uv, inverting v to take into account Processing's invertex Y axis with + // respect to OpenGL. + PVector tempv = new PVector(Float.valueOf(elements[1]).floatValue(), + 1 - Float.valueOf(elements[2]).floatValue()); + textures.add(tempv); + readvt = true; + } else if (elements[0].equals("o")) { + // Object name is ignored, for now. + } else if (elements[0].equals("mtllib")) { + if (elements[1] != null) { + BufferedReader mreader = parent.createReader(elements[1]); + if (mreader != null) { + parseMTL(parent, mreader, materials, mtlTable); + } + } + } else if (elements[0].equals("g")) { + gname = 1 < elements.length ? elements[1] : ""; + } else if (elements[0].equals("usemtl")) { + // Getting index of current active material (will be applied on all subsequent faces). + if (elements[1] != null) { + String mtlname = elements[1]; + if (mtlTable.containsKey(mtlname)) { + Integer tempInt = mtlTable.get(mtlname); + mtlIdxCur = tempInt.intValue(); + } else { + mtlIdxCur = -1; + } + } + } else if (elements[0].equals("f")) { + // Face setting + OBJFace face = new OBJFace(); + face.matIdx = mtlIdxCur; + face.name = gname; + + for (int i = 1; i < elements.length; i++) { + String seg = elements[i]; + + if (seg.indexOf("/") > 0) { + String[] forder = seg.split("/"); + + if (forder.length > 2) { + // Getting vertex and texture and normal indexes. + if (forder[0].length() > 0 && readv) { + face.vertIdx.add(Integer.valueOf(forder[0])); + } + + if (forder[1].length() > 0 && readvt) { + face.texIdx.add(Integer.valueOf(forder[1])); + } + + if (forder[2].length() > 0 && readvn) { + face.normIdx.add(Integer.valueOf(forder[2])); + } + } else if (forder.length > 1) { + // Getting vertex and texture/normal indexes. + if (forder[0].length() > 0 && readv) { + face.vertIdx.add(Integer.valueOf(forder[0])); + } + + if (forder[1].length() > 0) { + if (readvt) { + face.texIdx.add(Integer.valueOf(forder[1])); + } else if (readvn) { + face.normIdx.add(Integer.valueOf(forder[1])); + } + + } + + } else if (forder.length > 0) { + // Getting vertex index only. + if (forder[0].length() > 0 && readv) { + face.vertIdx.add(Integer.valueOf(forder[0])); + } + } + } else { + // Getting vertex index only. + if (seg.length() > 0 && readv) { + face.vertIdx.add(Integer.valueOf(seg)); + } + } + } + + faces.add(face); + } + } + } + + if (materials.size() == 0) { + // No materials definition so far. Adding one default material. + OBJMaterial defMtl = new OBJMaterial(); + materials.add(defMtl); + } + + } catch (Exception e) { + e.printStackTrace(); + } + } + + + static protected void parseMTL(PApplet parent, + BufferedReader reader, ArrayList materials, + Hashtable materialsHash) { + try { + String line; + OBJMaterial currentMtl = null; + while ((line = reader.readLine()) != null) { + // Parse the line + line = line.trim(); + + String elements[] = line.split("\\s+"); + + if (elements.length > 0) { + // Extract the material data. + + if (elements[0].equals("newmtl")) { + // Starting new material. + String mtlname = elements[1]; + currentMtl = new OBJMaterial(mtlname); + materialsHash.put(mtlname, new Integer(materials.size())); + materials.add(currentMtl); + } else if (elements[0].equals("map_Kd") && elements.length > 1) { + // Loading texture map. + String texname = elements[1]; + currentMtl.kdMap = parent.loadImage(texname); + } else if (elements[0].equals("Ka") && elements.length > 3) { + // The ambient color of the material + currentMtl.ka.x = Float.valueOf(elements[1]).floatValue(); + currentMtl.ka.y = Float.valueOf(elements[2]).floatValue(); + currentMtl.ka.z = Float.valueOf(elements[3]).floatValue(); + } else if (elements[0].equals("Kd") && elements.length > 3) { + // The diffuse color of the material + currentMtl.kd.x = Float.valueOf(elements[1]).floatValue(); + currentMtl.kd.y = Float.valueOf(elements[2]).floatValue(); + currentMtl.kd.z = Float.valueOf(elements[3]).floatValue(); + } else if (elements[0].equals("Ks") && elements.length > 3) { + // The specular color weighted by the specular coefficient + currentMtl.ks.x = Float.valueOf(elements[1]).floatValue(); + currentMtl.ks.y = Float.valueOf(elements[2]).floatValue(); + currentMtl.ks.z = Float.valueOf(elements[3]).floatValue(); + } else if ((elements[0].equals("d") || elements[0].equals("Tr")) && elements.length > 1) { + // Reading the alpha transparency. + currentMtl.d = Float.valueOf(elements[1]).floatValue(); + } else if (elements[0].equals("Ns") && elements.length > 1) { + // The specular component of the Phong shading model + currentMtl.ns = Float.valueOf(elements[1]).floatValue(); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + + // Stores a face from an OBJ file + static protected class OBJFace { + ArrayList vertIdx; + ArrayList texIdx; + ArrayList normIdx; + int matIdx; + String name; + + OBJFace() { + vertIdx = new ArrayList(); + texIdx = new ArrayList(); + normIdx = new ArrayList(); + matIdx = -1; + name = ""; + } + } + + + // Stores a material defined in an MTL file. + static protected class OBJMaterial { + String name; + PVector ka; + PVector kd; + PVector ks; + float d; + float ns; + PImage kdMap; + + OBJMaterial() { + this("default"); + } + + OBJMaterial(String name) { + this.name = name; + ka = new PVector(0.5f, 0.5f, 0.5f); + kd = new PVector(0.5f, 0.5f, 0.5f); + ks = new PVector(0.5f, 0.5f, 0.5f); + d = 1.0f; + ns = 0.0f; + kdMap = null; + } + } } \ No newline at end of file diff --git a/android/core/src/processing/opengl/PGraphicsOpenGL.java b/android/core/src/processing/opengl/PGraphicsOpenGL.java index 55367e19f..e30f196b1 100644 --- a/android/core/src/processing/opengl/PGraphicsOpenGL.java +++ b/android/core/src/processing/opengl/PGraphicsOpenGL.java @@ -1,47 +1,60 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + /* - Part of the Processing project - http://processing.org + Part of the Processing project - http://processing.org - Copyright (c) 2010 Ben Fry and Casey Reas + Copyright (c) 2004-12 Ben Fry and Casey Reas - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License version 2.1 as published by the Free Software Foundation. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License version 2.1 as published by the Free Software Foundation. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General - Public License along with this library; if not, write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA */ package processing.opengl; -import java.io.BufferedReader; +import processing.core.PApplet; +import processing.core.PFont; +import processing.core.PGraphics; +import processing.core.PImage; +import processing.core.PMatrix; +import processing.core.PMatrix2D; +import processing.core.PMatrix3D; +import processing.core.PShape; +import processing.core.PVector; + import java.net.URL; import java.nio.*; -import java.util.*; - -import processing.core.*; -import processing.opengl.geom.LinePath; - - -// drawPixels is missing...calls to glDrawPixels are commented out -// setRasterPos() is also commented out +import java.util.ArrayList; +import java.util.Collections; +import java.util.EmptyStackException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; +import java.util.Stack; /** * OpenGL renderer. - * + * */ public class PGraphicsOpenGL extends PGraphics { /** Interface between Processing and OpenGL */ public PGL pgl; - /** The PApplet renderer. For the primary surface, pg == this. */ - protected PGraphicsOpenGL pg; + /** The main PApplet renderer. */ + protected static PGraphicsOpenGL pgPrimary = null; + + /** The renderer currently in use. */ + protected static PGraphicsOpenGL pgCurrent = null; // ........................................................ @@ -54,7 +67,7 @@ public class PGraphicsOpenGL extends PGraphics { static protected final int FLUSH_WHEN_FULL = 1; /** Type of geometry: immediate is that generated with beginShape/vertex/ - * endShape, retained is the result of creating a PShape3D object with + * endShape, retained is the result of creating a PShapeOpenGL object with * createShape. */ static protected final int IMMEDIATE = 0; static protected final int RETAINED = 1; @@ -66,28 +79,28 @@ public class PGraphicsOpenGL extends PGraphics { // VBOs for immediate rendering: - public int glFillVertexBufferID; - public int glFillColorBufferID; - public int glFillNormalBufferID; - public int glFillTexCoordBufferID; - public int glFillAmbientBufferID; - public int glFillSpecularBufferID; - public int glFillEmissiveBufferID; - public int glFillShininessBufferID; - public int glFillIndexBufferID; - protected boolean fillBuffersCreated = false; - protected PGL.Context fillBuffersContext; + public int glPolyVertexBufferID; + public int glPolyColorBufferID; + public int glPolyNormalBufferID; + public int glPolyTexcoordBufferID; + public int glPolyAmbientBufferID; + public int glPolySpecularBufferID; + public int glPolyEmissiveBufferID; + public int glPolyShininessBufferID; + public int glPolyIndexBufferID; + protected boolean polyBuffersCreated = false; + protected PGL.Context polyBuffersContext; public int glLineVertexBufferID; public int glLineColorBufferID; - public int glLineDirWidthBufferID; + public int glLineAttribBufferID; public int glLineIndexBufferID; protected boolean lineBuffersCreated = false; protected PGL.Context lineBuffersContext; public int glPointVertexBufferID; public int glPointColorBufferID; - public int glPointSizeBufferID; + public int glPointAttribBufferID; public int glPointIndexBufferID; protected boolean pointBuffersCreated = false; protected PGL.Context pointBuffersContext; @@ -103,7 +116,7 @@ public class PGraphicsOpenGL extends PGraphics { /** Extensions used by Processing */ static public boolean npotTexSupported; - static public boolean mipmapGeneration; + static public boolean autoMipmapGenSupported; static public boolean fboMultisampleSupported; static public boolean packedDepthStencilSupported; static public boolean blendEqSupported; @@ -121,6 +134,7 @@ public class PGraphicsOpenGL extends PGraphics { static public String OPENGL_RENDERER; static public String OPENGL_VERSION; static public String OPENGL_EXTENSIONS; + static public String GLSL_VERSION; // ........................................................ @@ -138,28 +152,28 @@ public class PGraphicsOpenGL extends PGraphics { // Shaders - static protected URL defFillShaderVertSimpleURL = PGraphicsOpenGL.class.getResource("FillShaderVertSimple.glsl"); - static protected URL defFillShaderVertTexURL = PGraphicsOpenGL.class.getResource("FillShaderVertTex.glsl"); - static protected URL defFillShaderVertLitURL = PGraphicsOpenGL.class.getResource("FillShaderVertLit.glsl"); - static protected URL defFillShaderVertFullURL = PGraphicsOpenGL.class.getResource("FillShaderVertFull.glsl"); - static protected URL defFillShaderFragNoTexURL = PGraphicsOpenGL.class.getResource("FillShaderFragNoTex.glsl"); - static protected URL defFillShaderFragTexURL = PGraphicsOpenGL.class.getResource("FillShaderFragTex.glsl"); - static protected URL defLineShaderVertURL = PGraphicsOpenGL.class.getResource("LineShaderVert.glsl"); - static protected URL defLineShaderFragURL = PGraphicsOpenGL.class.getResource("LineShaderFrag.glsl"); - static protected URL defPointShaderVertURL = PGraphicsOpenGL.class.getResource("PointShaderVert.glsl"); - static protected URL defPointShaderFragURL = PGraphicsOpenGL.class.getResource("PointShaderFrag.glsl"); + static protected URL defPolyFlatShaderVertURL = PGraphicsOpenGL.class.getResource("PolyFlatShaderVert.glsl"); + static protected URL defPolyTexShaderVertURL = PGraphicsOpenGL.class.getResource("PolyTexShaderVert.glsl"); + static protected URL defPolyLightShaderVertURL = PGraphicsOpenGL.class.getResource("PolyLightShaderVert.glsl"); + static protected URL defPolyFullShaderVertURL = PGraphicsOpenGL.class.getResource("PolyFullShaderVert.glsl"); + static protected URL defPolyNoTexShaderFragURL = PGraphicsOpenGL.class.getResource("PolyNoTexShaderFrag.glsl"); + static protected URL defPolyTexShaderFragURL = PGraphicsOpenGL.class.getResource("PolyTexShaderFrag.glsl"); + static protected URL defLineShaderVertURL = PGraphicsOpenGL.class.getResource("LineShaderVert.glsl"); + static protected URL defLineShaderFragURL = PGraphicsOpenGL.class.getResource("LineShaderFrag.glsl"); + static protected URL defPointShaderVertURL = PGraphicsOpenGL.class.getResource("PointShaderVert.glsl"); + static protected URL defPointShaderFragURL = PGraphicsOpenGL.class.getResource("PointShaderFrag.glsl"); - static protected FillShaderSimple defFillShaderSimple; - static protected FillShaderTex defFillShaderTex; - static protected FillShaderLit defFillShaderLit; - static protected FillShaderFull defFillShaderFull; + static protected PolyFlatShader defPolyFlatShader; + static protected PolyTexShader defPolyTexShader; + static protected PolyLightShader defPolyLightShader; + static protected PolyFullShader defPolyFullShader; static protected LineShader defLineShader; static protected PointShader defPointShader; - protected FillShaderSimple fillShaderSimple; - protected FillShaderTex fillShaderTex; - protected FillShaderLit fillShaderLit; - protected FillShaderFull fillShaderFull; + protected PolyFlatShader polyFlatShader; + protected PolyTexShader polyTexShader; + protected PolyLightShader polyLightShader; + protected PolyFullShader polyFullShader; protected LineShader lineShader; protected PointShader pointShader; @@ -169,11 +183,9 @@ public class PGraphicsOpenGL extends PGraphics { protected InGeometry inGeo; protected TessGeometry tessGeo; - protected int firstTexIndex; - protected int firstTexCache; - protected TexCache texCache; static protected Tessellator tessellator; - + protected TexCache texCache; + // ........................................................ // Camera: @@ -226,7 +238,7 @@ public class PGraphicsOpenGL extends PGraphics { /** Projection matrix stack **/ protected Stack projectionStack; - + // ........................................................ // Lights: @@ -369,8 +381,21 @@ public class PGraphicsOpenGL extends PGraphics { protected boolean perspectiveCorrectedLines = false; - /** Used in point tessellation. */ - final static protected int MIN_POINT_ACCURACY = 20; + /** Used in round point and ellipse tessellation. The + * number of subdivisions per round point or ellipse is + * calculated with the following formula: + * n = max(N, (TWO_PI * size / F)) + * where size is a measure of the dimensions of the circle + * when projected on screen coordinates. F just sets the + * minimum number of subdivisions, while a smaller F + * would allow to have more detailed circles. + * N = MIN_POINT_ACCURACY + * F = POINT_ACCURACY_FACTOR + */ + final static protected int MIN_POINT_ACCURACY = 20; + final static protected float POINT_ACCURACY_FACTOR = 10.0f; + + /** Used in quad point tessellation. */ final protected float[][] QUAD_POINT_SIGNS = { {-1, +1}, {-1, -1}, {+1, -1}, {+1, +1} }; @@ -381,7 +406,7 @@ public class PGraphicsOpenGL extends PGraphics { public PGraphicsOpenGL() { pgl = new PGL(this); - pg = null; + if (tessellator == null) { tessellator = new Tessellator(); @@ -391,24 +416,24 @@ public class PGraphicsOpenGL extends PGraphics { tessGeo = newTessGeometry(IMMEDIATE); texCache = newTexCache(); - glFillVertexBufferID = 0; - glFillColorBufferID = 0; - glFillNormalBufferID = 0; - glFillTexCoordBufferID = 0; - glFillAmbientBufferID = 0; - glFillSpecularBufferID = 0; - glFillEmissiveBufferID = 0; - glFillShininessBufferID = 0; - glFillIndexBufferID = 0; + glPolyVertexBufferID = 0; + glPolyColorBufferID = 0; + glPolyNormalBufferID = 0; + glPolyTexcoordBufferID = 0; + glPolyAmbientBufferID = 0; + glPolySpecularBufferID = 0; + glPolyEmissiveBufferID = 0; + glPolyShininessBufferID = 0; + glPolyIndexBufferID = 0; glLineVertexBufferID = 0; glLineColorBufferID = 0; - glLineDirWidthBufferID = 0; + glLineAttribBufferID = 0; glLineIndexBufferID = 0; glPointVertexBufferID = 0; glPointColorBufferID = 0; - glPointSizeBufferID = 0; + glPointAttribBufferID = 0; glPointIndexBufferID = 0; } @@ -498,7 +523,7 @@ public class PGraphicsOpenGL extends PGraphics { public void dispose() { // PGraphics super.dispose(); deleteFinalizedGLResources(); - deleteFillBuffers(); + deletePolyBuffers(); deleteLineBuffers(); deletePointBuffers(); } @@ -1035,142 +1060,142 @@ public class PGraphicsOpenGL extends PGraphics { // FRAME RENDERING - protected void createFillBuffers() { - if (!fillBuffersCreated || fillBuffersContextIsOutdated()) { - fillBuffersContext = pgl.getCurrentContext(); + protected void createPolyBuffers() { + if (!polyBuffersCreated || polyBuffersContextIsOutdated()) { + polyBuffersContext = pgl.getCurrentContext(); int sizef = INIT_VERTEX_BUFFER_SIZE * PGL.SIZEOF_FLOAT; int sizei = INIT_VERTEX_BUFFER_SIZE * PGL.SIZEOF_INT; int sizex = INIT_INDEX_BUFFER_SIZE * PGL.SIZEOF_INDEX; - glFillVertexBufferID = createVertexBufferObject(fillBuffersContext.code()); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillVertexBufferID); + glPolyVertexBufferID = createVertexBufferObject(polyBuffersContext.code()); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyVertexBufferID); pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 3 * sizef, null, PGL.GL_STATIC_DRAW); - glFillColorBufferID = createVertexBufferObject(fillBuffersContext.code()); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillColorBufferID); + glPolyColorBufferID = createVertexBufferObject(polyBuffersContext.code()); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyColorBufferID); pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, null, PGL.GL_STATIC_DRAW); - glFillNormalBufferID = createVertexBufferObject(fillBuffersContext.code()); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillNormalBufferID); + glPolyNormalBufferID = createVertexBufferObject(polyBuffersContext.code()); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyNormalBufferID); pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 3 * sizef, null, PGL.GL_STATIC_DRAW); - glFillTexCoordBufferID = createVertexBufferObject(fillBuffersContext.code()); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillTexCoordBufferID); + glPolyTexcoordBufferID = createVertexBufferObject(polyBuffersContext.code()); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyTexcoordBufferID); pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 2 * sizef, null, PGL.GL_STATIC_DRAW); - glFillAmbientBufferID = pg.createVertexBufferObject(fillBuffersContext.code()); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillAmbientBufferID); + glPolyAmbientBufferID = pgPrimary.createVertexBufferObject(polyBuffersContext.code()); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyAmbientBufferID); pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, null, PGL.GL_STATIC_DRAW); - glFillSpecularBufferID = pg.createVertexBufferObject(fillBuffersContext.code()); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillSpecularBufferID); + glPolySpecularBufferID = pgPrimary.createVertexBufferObject(polyBuffersContext.code()); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolySpecularBufferID); pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, null, PGL.GL_STATIC_DRAW); - glFillEmissiveBufferID = pg.createVertexBufferObject(fillBuffersContext.code()); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillEmissiveBufferID); + glPolyEmissiveBufferID = pgPrimary.createVertexBufferObject(polyBuffersContext.code()); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyEmissiveBufferID); pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, null, PGL.GL_STATIC_DRAW); - glFillShininessBufferID = pg.createVertexBufferObject(fillBuffersContext.code()); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillShininessBufferID); + glPolyShininessBufferID = pgPrimary.createVertexBufferObject(polyBuffersContext.code()); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyShininessBufferID); pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizef, null, PGL.GL_STATIC_DRAW); pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); - glFillIndexBufferID = createVertexBufferObject(fillBuffersContext.code()); - pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, glFillIndexBufferID); + glPolyIndexBufferID = createVertexBufferObject(polyBuffersContext.code()); + pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, glPolyIndexBufferID); pgl.glBufferData(PGL.GL_ELEMENT_ARRAY_BUFFER, sizex, null, PGL.GL_STATIC_DRAW); pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, 0); - fillBuffersCreated = true; + polyBuffersCreated = true; } } - protected void updateFillBuffers(boolean lit, boolean tex) { - createFillBuffers(); + protected void updatePolyBuffers(boolean lit, boolean tex) { + createPolyBuffers(); - int size = tessGeo.fillVertexCount; + int size = tessGeo.polyVertexCount; int sizef = size * PGL.SIZEOF_FLOAT; int sizei = size * PGL.SIZEOF_INT; - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillVertexBufferID); - pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 4 * sizef, FloatBuffer.wrap(tessGeo.fillVertices, 0, 4 * size), PGL.GL_STATIC_DRAW); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyVertexBufferID); + pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 4 * sizef, FloatBuffer.wrap(tessGeo.polyVertices, 0, 4 * size), PGL.GL_STATIC_DRAW); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillColorBufferID); - pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, IntBuffer.wrap(tessGeo.fillColors, 0, size), PGL.GL_STATIC_DRAW); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyColorBufferID); + pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, IntBuffer.wrap(tessGeo.polyColors, 0, size), PGL.GL_STATIC_DRAW); if (lit) { - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillNormalBufferID); - pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 3 * sizef, FloatBuffer.wrap(tessGeo.fillNormals, 0, 3 * size), PGL.GL_STATIC_DRAW); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyNormalBufferID); + pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 3 * sizef, FloatBuffer.wrap(tessGeo.polyNormals, 0, 3 * size), PGL.GL_STATIC_DRAW); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillAmbientBufferID); - pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, IntBuffer.wrap(tessGeo.fillAmbient, 0, size), PGL.GL_STATIC_DRAW); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyAmbientBufferID); + pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, IntBuffer.wrap(tessGeo.polyAmbient, 0, size), PGL.GL_STATIC_DRAW); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillSpecularBufferID); - pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, IntBuffer.wrap(tessGeo.fillSpecular, 0, size), PGL.GL_STATIC_DRAW); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolySpecularBufferID); + pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, IntBuffer.wrap(tessGeo.polySpecular, 0, size), PGL.GL_STATIC_DRAW); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillEmissiveBufferID); - pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, IntBuffer.wrap(tessGeo.fillEmissive, 0, size), PGL.GL_STATIC_DRAW); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyEmissiveBufferID); + pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, IntBuffer.wrap(tessGeo.polyEmissive, 0, size), PGL.GL_STATIC_DRAW); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillShininessBufferID); - pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizef, FloatBuffer.wrap(tessGeo.fillShininess, 0, size), PGL.GL_STATIC_DRAW); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyShininessBufferID); + pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizef, FloatBuffer.wrap(tessGeo.polyShininess, 0, size), PGL.GL_STATIC_DRAW); } if (tex) { - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillTexCoordBufferID); - pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 2 * sizef, FloatBuffer.wrap(tessGeo.fillTexcoords, 0, 2 * size), PGL.GL_STATIC_DRAW); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyTexcoordBufferID); + pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 2 * sizef, FloatBuffer.wrap(tessGeo.polyTexcoords, 0, 2 * size), PGL.GL_STATIC_DRAW); } - pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, glFillIndexBufferID); - pgl.glBufferData(PGL.GL_ELEMENT_ARRAY_BUFFER, tessGeo.fillIndexCount * PGL.SIZEOF_INDEX, - ShortBuffer.wrap(tessGeo.fillIndices, 0, tessGeo.fillIndexCount), PGL.GL_STATIC_DRAW); + pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, glPolyIndexBufferID); + pgl.glBufferData(PGL.GL_ELEMENT_ARRAY_BUFFER, tessGeo.polyIndexCount * PGL.SIZEOF_INDEX, + ShortBuffer.wrap(tessGeo.polyIndices, 0, tessGeo.polyIndexCount), PGL.GL_STATIC_DRAW); } - protected void unbindFillBuffers() { + protected void unbindPolyBuffers() { pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, 0); } - protected boolean fillBuffersContextIsOutdated() { - return !pgl.contextIsCurrent(fillBuffersContext); + protected boolean polyBuffersContextIsOutdated() { + return !pgl.contextIsCurrent(polyBuffersContext); } - protected void deleteFillBuffers() { - if (fillBuffersCreated) { - deleteVertexBufferObject(glFillVertexBufferID, fillBuffersContext.code()); - glFillVertexBufferID = 0; + protected void deletePolyBuffers() { + if (polyBuffersCreated) { + deleteVertexBufferObject(glPolyVertexBufferID, polyBuffersContext.code()); + glPolyVertexBufferID = 0; - deleteVertexBufferObject(glFillColorBufferID, fillBuffersContext.code()); - glFillColorBufferID = 0; + deleteVertexBufferObject(glPolyColorBufferID, polyBuffersContext.code()); + glPolyColorBufferID = 0; - deleteVertexBufferObject(glFillNormalBufferID, fillBuffersContext.code()); - glFillNormalBufferID = 0; + deleteVertexBufferObject(glPolyNormalBufferID, polyBuffersContext.code()); + glPolyNormalBufferID = 0; - deleteVertexBufferObject(glFillTexCoordBufferID, fillBuffersContext.code()); - glFillTexCoordBufferID = 0; + deleteVertexBufferObject(glPolyTexcoordBufferID, polyBuffersContext.code()); + glPolyTexcoordBufferID = 0; - deleteVertexBufferObject(glFillAmbientBufferID, fillBuffersContext.code()); - glFillAmbientBufferID = 0; + deleteVertexBufferObject(glPolyAmbientBufferID, polyBuffersContext.code()); + glPolyAmbientBufferID = 0; - deleteVertexBufferObject(glFillSpecularBufferID, fillBuffersContext.code()); - glFillSpecularBufferID = 0; + deleteVertexBufferObject(glPolySpecularBufferID, polyBuffersContext.code()); + glPolySpecularBufferID = 0; - deleteVertexBufferObject(glFillEmissiveBufferID, fillBuffersContext.code()); - glFillEmissiveBufferID = 0; + deleteVertexBufferObject(glPolyEmissiveBufferID, polyBuffersContext.code()); + glPolyEmissiveBufferID = 0; - deleteVertexBufferObject(glFillShininessBufferID, fillBuffersContext.code()); - glFillShininessBufferID = 0; + deleteVertexBufferObject(glPolyShininessBufferID, polyBuffersContext.code()); + glPolyShininessBufferID = 0; - deleteVertexBufferObject(glFillIndexBufferID, fillBuffersContext.code()); - glFillIndexBufferID = 0; + deleteVertexBufferObject(glPolyIndexBufferID, polyBuffersContext.code()); + glPolyIndexBufferID = 0; - fillBuffersCreated = false; + polyBuffersCreated = false; } } @@ -1192,8 +1217,8 @@ public class PGraphicsOpenGL extends PGraphics { pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glLineColorBufferID); pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, null, PGL.GL_STATIC_DRAW); - glLineDirWidthBufferID = createVertexBufferObject(lineBuffersContext.code()); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glLineDirWidthBufferID); + glLineAttribBufferID = createVertexBufferObject(lineBuffersContext.code()); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glLineAttribBufferID); pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 4 * sizef, null, PGL.GL_STATIC_DRAW); pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); @@ -1222,8 +1247,8 @@ public class PGraphicsOpenGL extends PGraphics { pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glLineColorBufferID); pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, IntBuffer.wrap(tessGeo.lineColors, 0, size), PGL.GL_STATIC_DRAW); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glLineDirWidthBufferID); - pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 4 * sizef, FloatBuffer.wrap(tessGeo.lineDirWidths, 0, 4 * size), PGL.GL_STATIC_DRAW); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glLineAttribBufferID); + pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 4 * sizef, FloatBuffer.wrap(tessGeo.lineAttribs, 0, 4 * size), PGL.GL_STATIC_DRAW); pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, glLineIndexBufferID); pgl.glBufferData(PGL.GL_ELEMENT_ARRAY_BUFFER, tessGeo.lineIndexCount * PGL.SIZEOF_INDEX, @@ -1250,8 +1275,8 @@ public class PGraphicsOpenGL extends PGraphics { deleteVertexBufferObject(glLineColorBufferID, lineBuffersContext.code()); glLineColorBufferID = 0; - deleteVertexBufferObject(glLineDirWidthBufferID, lineBuffersContext.code()); - glLineDirWidthBufferID = 0; + deleteVertexBufferObject(glLineAttribBufferID, lineBuffersContext.code()); + glLineAttribBufferID = 0; deleteVertexBufferObject(glLineIndexBufferID, lineBuffersContext.code()); glLineIndexBufferID = 0; @@ -1277,8 +1302,8 @@ public class PGraphicsOpenGL extends PGraphics { pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPointColorBufferID); pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, null, PGL.GL_STATIC_DRAW); - glPointSizeBufferID = createVertexBufferObject(pointBuffersContext.code()); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPointSizeBufferID); + glPointAttribBufferID = createVertexBufferObject(pointBuffersContext.code()); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPointAttribBufferID); pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 2 * sizef, null, PGL.GL_STATIC_DRAW); pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); @@ -1307,8 +1332,8 @@ public class PGraphicsOpenGL extends PGraphics { pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPointColorBufferID); pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, IntBuffer.wrap(tessGeo.pointColors, 0, size), PGL.GL_STATIC_DRAW); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPointSizeBufferID); - pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 2 * sizef, FloatBuffer.wrap(tessGeo.pointSizes, 0, 2 * size), PGL.GL_STATIC_DRAW); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPointAttribBufferID); + pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 2 * sizef, FloatBuffer.wrap(tessGeo.pointAttribs, 0, 2 * size), PGL.GL_STATIC_DRAW); pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, glPointIndexBufferID); pgl.glBufferData(PGL.GL_ELEMENT_ARRAY_BUFFER, tessGeo.pointIndexCount * PGL.SIZEOF_INDEX, @@ -1335,8 +1360,8 @@ public class PGraphicsOpenGL extends PGraphics { deleteVertexBufferObject(glPointColorBufferID, pointBuffersContext.code()); glPointColorBufferID = 0; - deleteVertexBufferObject(glPointSizeBufferID, pointBuffersContext.code()); - glPointSizeBufferID = 0; + deleteVertexBufferObject(glPointAttribBufferID, pointBuffersContext.code()); + glPointAttribBufferID = 0; deleteVertexBufferObject(glPointIndexBufferID, pointBuffersContext.code()); glPointIndexBufferID = 0; @@ -1372,6 +1397,16 @@ public class PGraphicsOpenGL extends PGraphics { return; } + if (pgCurrent != pgPrimary && this != pgPrimary) { + // It seems that the user is trying to start + // another beginDraw()/endDraw() block for an + // offscreen surface, still drawing on another + // offscreen surface. This situation is not + // catched by the drawing check above. + showWarning("P3D: Already called beginDraw() for another P3D object."); + return; + } + if (!glParamsRead) { getGLParameters(); } @@ -1400,13 +1435,15 @@ public class PGraphicsOpenGL extends PGraphics { } else { setFramebuffer(offscreenFramebuffer); } - pgl.updateOffscreen(pg.pgl); + pgl.updateOffscreen(pgPrimary.pgl); pgl.glDrawBuffer(PGL.GL_COLOR_ATTACHMENT0); } // We are ready to go! - report("top beginDraw()"); + + drawing = true; + pgCurrent = this; inGeo.clear(); tessGeo.clear(); @@ -1439,21 +1476,18 @@ public class PGraphicsOpenGL extends PGraphics { if (primarySurface) { int[] temp = new int[1]; pgl.glGetIntegerv(PGL.GL_SAMPLES, temp, 0); - if (antialias != temp[0] && 1 < temp[0] && 1 < antialias) { - antialias = temp[0]; + if (quality != temp[0] && 1 < temp[0] && 1 < quality) { + quality = temp[0]; } } - if (antialias < 2) { + if (quality < 2) { pgl.glDisable(PGL.GL_MULTISAMPLE); - pgl.glEnable(PGL.GL_POINT_SMOOTH); - pgl.glEnable(PGL.GL_LINE_SMOOTH); - pgl.glEnable(PGL.GL_POLYGON_SMOOTH); } else { pgl.glEnable(PGL.GL_MULTISAMPLE); - pgl.glDisable(PGL.GL_POINT_SMOOTH); - pgl.glDisable(PGL.GL_LINE_SMOOTH); - pgl.glDisable(PGL.GL_POLYGON_SMOOTH); } + pgl.glDisable(PGL.GL_POINT_SMOOTH); + pgl.glDisable(PGL.GL_LINE_SMOOTH); + pgl.glDisable(PGL.GL_POLYGON_SMOOTH); // setup opengl viewport. viewport[0] = 0; viewport[1] = 0; viewport[2] = width; viewport[3] = height; @@ -1465,8 +1499,8 @@ public class PGraphicsOpenGL extends PGraphics { if (texture != null) { // The screen texture should be deleted because it // corresponds to the old window size. - this.removeCache(pg); - this.removeParams(pg); + this.removeCache(pgPrimary); + this.removeParams(pgPrimary); texture = null; loadTexture(); } @@ -1494,9 +1528,11 @@ public class PGraphicsOpenGL extends PGraphics { calcProjmodelview(); } - noLights(); - lightFalloff(1, 0, 0); - lightSpecular(0, 0, 0); + if (is3D()) { + noLights(); + lightFalloff(1, 0, 0); + lightSpecular(0, 0, 0); + } // Because y is flipped, the vertices that should be specified by // the user in CCW order to define a front-facing facet, end up being @@ -1520,7 +1556,7 @@ public class PGraphicsOpenGL extends PGraphics { if (primarySurface) { pgl.beginOnscreenDraw(clearColorBuffer); } else { - pgl.beginOffscreenDraw(pg.clearColorBuffer); + pgl.beginOffscreenDraw(pgPrimary.clearColorBuffer); // Just in case the texture was recreated (in a resize event for example) offscreenFramebuffer.setColorBuffer(texture); @@ -1536,8 +1572,7 @@ public class PGraphicsOpenGL extends PGraphics { } else { pgl.glDepthMask(true); } - - drawing = true; + pixelsOp = OP_NONE; modified = false; @@ -1581,7 +1616,7 @@ public class PGraphicsOpenGL extends PGraphics { offscreenFramebufferMultisample.copy(offscreenFramebuffer); } - if (!pgl.initialized || !pg.pgl.initialized || parent.frameCount == 0) { + if (!pgl.initialized || !pgPrimary.pgl.initialized || parent.frameCount == 0) { // If the primary surface is re-initialized, this offscreen // surface needs to save its contents into the pixels array // so they can be restored after the FBOs are recreated. @@ -1597,13 +1632,22 @@ public class PGraphicsOpenGL extends PGraphics { popFramebuffer(); - pgl.endOffscreenDraw(pg.clearColorBuffer0); + pgl.endOffscreenDraw(pgPrimary.clearColorBuffer0); - pg.restoreGL(); + pgPrimary.restoreGL(); } - drawing = false; - + // Done! + drawing = false; + if (pgCurrent == pgPrimary) { + // Done with the main surface + pgCurrent = null; + } else { + // Done with an offscreen surface, + // going back to onscreen drawing. + pgCurrent = pgPrimary; + } + report("bot endDraw()"); } @@ -1633,11 +1677,11 @@ public class PGraphicsOpenGL extends PGraphics { } pgl.glDepthFunc(PGL.GL_LEQUAL); - if (antialias < 2) { + if (quality < 2) { pgl.glDisable(PGL.GL_MULTISAMPLE); - pgl.glEnable(PGL.GL_POINT_SMOOTH); - pgl.glEnable(PGL.GL_LINE_SMOOTH); - pgl.glEnable(PGL.GL_POLYGON_SMOOTH); +// pgl.glEnable(PGL.GL_POINT_SMOOTH); +// pgl.glEnable(PGL.GL_LINE_SMOOTH); +// pgl.glEnable(PGL.GL_POLYGON_SMOOTH); } else { pgl.glEnable(PGL.GL_MULTISAMPLE); pgl.glDisable(PGL.GL_POINT_SMOOTH); @@ -1682,7 +1726,7 @@ public class PGraphicsOpenGL extends PGraphics { if (offscreenNotCurrent) { pushFramebuffer(); setFramebuffer(offscreenFramebuffer); - pgl.updateOffscreen(pg.pgl); + pgl.updateOffscreen(pgPrimary.pgl); } pgl.glReadBuffer(PGL.GL_COLOR_ATTACHMENT0); } else { @@ -1700,7 +1744,7 @@ public class PGraphicsOpenGL extends PGraphics { } else { setFramebuffer(offscreenFramebuffer); } - pgl.updateOffscreen(pg.pgl); + pgl.updateOffscreen(pgPrimary.pgl); } pgl.glDrawBuffer(PGL.GL_COLOR_ATTACHMENT0); } @@ -1929,130 +1973,6 @@ public class PGraphicsOpenGL extends PGraphics { } - ////////////////////////////////////////////////////////////// - - // SHAPE CREATORS - - - public PShape createShape() { - return createShape(POLYGON); - } - - - public PShape createShape(int type) { - PShape3D shape = null; - if (type == PShape.GROUP) { - shape = new PShape3D(parent, PShape.GROUP); - } else if (type == PShape.PATH) { - shape = new PShape3D(parent, PShape.PATH); - } else if (type == POINTS) { - shape = new PShape3D(parent, PShape.GEOMETRY); - shape.setKind(POINTS); - } else if (type == LINES) { - shape = new PShape3D(parent, PShape.GEOMETRY); - shape.setKind(LINES); - } else if (type == TRIANGLE || type == TRIANGLES) { - shape = new PShape3D(parent, PShape.GEOMETRY); - shape.setKind(TRIANGLES); - } else if (type == TRIANGLE_FAN) { - shape = new PShape3D(parent, PShape.GEOMETRY); - shape.setKind(TRIANGLE_FAN); - } else if (type == TRIANGLE_STRIP) { - shape = new PShape3D(parent, PShape.GEOMETRY); - shape.setKind(TRIANGLE_STRIP); - } else if (type == QUAD || type == QUADS) { - shape = new PShape3D(parent, PShape.GEOMETRY); - shape.setKind(QUADS); - } else if (type == QUAD_STRIP) { - shape = new PShape3D(parent, PShape.GEOMETRY); - shape.setKind(QUAD_STRIP); - } else if (type == POLYGON) { - shape = new PShape3D(parent, PShape.GEOMETRY); - shape.setKind(POLYGON); - } - return shape; - } - - - public PShape createShape(int kind, float... p) { - PShape3D shape = null; - int len = p.length; - - if (kind == POINT) { - if (len != 2 && len != 3) { - showWarning("Wrong number of parameters"); - return null; - } - shape = new PShape3D(parent, PShape.PRIMITIVE); - shape.setKind(POINT); - } else if (kind == LINE) { - if (len != 4 && len != 6) { - showWarning("Wrong number of parameters"); - return null; - } - shape = new PShape3D(parent, PShape.PRIMITIVE); - shape.setKind(LINE); - } else if (kind == TRIANGLE) { - if (len != 6) { - showWarning("Wrong number of parameters"); - return null; - } - shape = new PShape3D(parent, PShape.PRIMITIVE); - shape.setKind(TRIANGLE); - } else if (kind == QUAD) { - if (len != 8) { - showWarning("Wrong number of parameters"); - return null; - } - shape = new PShape3D(parent, PShape.PRIMITIVE); - shape.setKind(QUAD); - } else if (kind == RECT) { - if (len != 4 && len != 5 && len != 8) { - showWarning("Wrong number of parameters"); - return null; - } - shape = new PShape3D(parent, PShape.PRIMITIVE); - shape.setKind(RECT); - } else if (kind == ELLIPSE) { - if (len != 4) { - showWarning("Wrong number of parameters"); - return null; - } - shape = new PShape3D(parent, PShape.PRIMITIVE); - shape.setKind(ELLIPSE); - } else if (kind == ARC) { - if (len != 6) { - showWarning("Wrong number of parameters"); - return null; - } - shape = new PShape3D(parent, PShape.PRIMITIVE); - shape.setKind(ARC); - } else if (kind == BOX) { - if (len != 1 && len != 3) { - showWarning("Wrong number of parameters"); - return null; - } - shape = new PShape3D(parent, PShape.PRIMITIVE); - shape.setKind(BOX); - } else if (kind == SPHERE) { - if (len != 1) { - showWarning("Wrong number of parameters"); - return null; - } - shape = new PShape3D(parent, PShape.PRIMITIVE); - shape.setKind(SPHERE); - } else { - showWarning("Unrecognized primitive type"); - } - - if (shape != null) { - shape.setParams(p); - } - - return shape; - } - - ////////////////////////////////////////////////////////////// // VERTEX SHAPES @@ -2063,7 +1983,7 @@ public class PGraphicsOpenGL extends PGraphics { inGeo.clear(); - breakShape = false; + breakShape = true; defaultEdges = true; textureImage0 = textureImage; @@ -2150,6 +2070,7 @@ public class PGraphicsOpenGL extends PGraphics { return; } openContour = true; + breakShape = true; } @@ -2158,23 +2079,22 @@ public class PGraphicsOpenGL extends PGraphics { showWarning("P3D: Need to call beginContour() first."); return; } - openContour = false; - breakShape = true; + openContour = false; } public void vertex(float x, float y) { - vertex(x, y, 0, 0, 0); + vertexImpl(x, y, 0, 0, 0); } public void vertex(float x, float y, float u, float v) { - vertex(x, y, 0, u, v); + vertexImpl(x, y, 0, u, v); } public void vertex(float x, float y, float z) { - vertex(x, y, z, 0, 0); + vertexImpl(x, y, z, 0, 0); } @@ -2292,18 +2212,19 @@ public class PGraphicsOpenGL extends PGraphics { // protected void sort() - protected void tessellate(int mode) { + protected void tessellate(int mode) { tessellator.setInGeometry(inGeo); - tessellator.setTessGeometry(tessGeo); + tessellator.setTessGeometry(tessGeo); tessellator.setFill(fill || textureImage != null); tessellator.setStroke(stroke); tessellator.setStrokeColor(strokeColor); tessellator.setStrokeWeight(strokeWeight); tessellator.setStrokeCap(strokeCap); tessellator.setStrokeJoin(strokeJoin); - - setFirstTexIndex(tessGeo.fillIndexCount, tessGeo.fillIndexCache.size - 1); - + tessellator.setTexCache(texCache, textureImage0, textureImage); + tessellator.setTransform(modelview); + tessellator.set3D(is3D()); + if (shape == POINTS) { tessellator.tessellatePoints(); } else if (shape == LINES) { @@ -2336,8 +2257,6 @@ public class PGraphicsOpenGL extends PGraphics { if (stroke && defaultEdges) inGeo.addPolygonEdges(mode == CLOSE); tessellator.tessellatePolygon(false, mode == CLOSE, normalMode == NORMAL_MODE_AUTO); } - - setLastTexIndex(tessGeo.lastFillIndex, tessGeo.fillIndexCache.size - 1); } protected void tessellate(int[] indices, int[] edges) { @@ -2349,7 +2268,7 @@ public class PGraphicsOpenGL extends PGraphics { inGeo.addEdge(i0, i1, n == 0, n == nedges - 1); } } - + tessellator.setInGeometry(inGeo); tessellator.setTessGeometry(tessGeo); tessellator.setFill(fill || textureImage != null); @@ -2358,38 +2277,23 @@ public class PGraphicsOpenGL extends PGraphics { tessellator.setStrokeWeight(strokeWeight); tessellator.setStrokeCap(strokeCap); tessellator.setStrokeJoin(strokeJoin); + tessellator.setTexCache(texCache, textureImage0, textureImage); + tessellator.setTransform(modelview); + tessellator.set3D(is3D()); - setFirstTexIndex(tessGeo.fillIndexCount, tessGeo.fillIndexCache.size - 1); - if (stroke && defaultEdges && edges == null) inGeo.addTrianglesEdges(); if (normalMode == NORMAL_MODE_AUTO) inGeo.calcTrianglesNormals(); tessellator.tessellateTriangles(indices); + } + + + public void flush() { + boolean hasPolys = 0 < tessGeo.polyVertexCount && 0 < tessGeo.polyIndexCount; + boolean hasLines = 0 < tessGeo.lineVertexCount && 0 < tessGeo.lineIndexCount; + boolean hasPoints = 0 < tessGeo.pointVertexCount && 0 < tessGeo.pointIndexCount; - setLastTexIndex(tessGeo.lastFillIndex, tessGeo.fillIndexCache.size - 1); - } - - - protected void setFirstTexIndex(int firstIndex, int firstCache) { - firstTexIndex = firstIndex; - firstTexCache = PApplet.max(0, firstCache); - } - - - protected void setLastTexIndex(int lastIndex, int lastCache) { - if (textureImage0 != textureImage || texCache.size == 0) { - texCache.addTexture(textureImage, firstTexIndex, firstTexCache, lastIndex, lastCache); - } else { - texCache.setLastIndex(lastIndex, lastCache); - } - } - - - public void flush() { - boolean hasPoints = 0 < tessGeo.pointVertexCount && 0 < tessGeo.pointIndexCount; - boolean hasLines = 0 < tessGeo.lineVertexCount && 0 < tessGeo.lineIndexCount; - boolean hasFill = 0 < tessGeo.fillVertexCount && 0 < tessGeo.fillIndexCount; boolean hasPixels = modified && pixels != null; - + if (hasPixels) { // If the user has been manipulating individual pixels, // the changes need to be copied to the screen before @@ -2398,7 +2302,7 @@ public class PGraphicsOpenGL extends PGraphics { setgetPixels = false; } - if (hasPoints || hasLines || hasFill) { + if (hasPoints || hasLines || hasPolys) { if (flushMode == FLUSH_WHEN_FULL && !hints[DISABLE_TRANSFORM_CACHE]) { // The modelview transformation has been applied already to the // tessellated vertices, so we set the OpenGL modelview matrix as @@ -2407,25 +2311,27 @@ public class PGraphicsOpenGL extends PGraphics { resetMatrix(); } - if (hasFill) { - flushFill(); + if (hasPolys) { + flushPolys(); if (raw != null) { - rawFill(); + rawPolys(); } } - if (hasPoints) { - flushPoints(); - if (raw != null) { - rawPoints(); - } - } - - if (hasLines) { - flushLines(); - if (raw != null) { - rawLines(); - } + if (is3D()) { + if (hasLines) { + flushLines(); + if (raw != null) { + rawLines(); + } + } + + if (hasPoints) { + flushPoints(); + if (raw != null) { + rawPoints(); + } + } } if (flushMode == FLUSH_WHEN_FULL && !hints[DISABLE_TRANSFORM_CACHE]) { @@ -2442,206 +2348,74 @@ public class PGraphicsOpenGL extends PGraphics { drawPixels(mx1, my1, mx2 - mx1 + 1, my2 - my1 + 1); modified = false; } - - - protected void flushPoints() { - updatePointBuffers(); - - PointShader shader = getPointShader(); - shader.start(); - - IndexCache cache = tessGeo.pointIndexCache; - for (int n = 0; n < cache.size; n++) { - int ioffset = cache.indexOffset[n]; - int icount = cache.indexCount[n]; - int voffset = cache.vertexOffset[n]; - - shader.setVertexAttribute(glPointVertexBufferID, 4, PGL.GL_FLOAT, 0, 4 * voffset * PGL.SIZEOF_FLOAT); - shader.setColorAttribute(glPointColorBufferID, 4, PGL.GL_UNSIGNED_BYTE, 0, 4 * voffset * PGL.SIZEOF_BYTE); - shader.setSizeAttribute(glPointSizeBufferID, 2, PGL.GL_FLOAT, 0, 2 * voffset * PGL.SIZEOF_FLOAT); - - pgl.glDrawElements(PGL.GL_TRIANGLES, icount, PGL.INDEX_TYPE, ioffset * PGL.SIZEOF_INDEX); - } - - shader.stop(); - unbindPointBuffers(); - } - - - // TODO: finish when points are properly re-implemented. - void rawPoints() { -// raw.colorMode(RGB); -// raw.noFill(); -// raw.beginShape(POINTS); -// -// float[] vec = tessGeo.pointVertices; -// int[] color = tessGeo.pointColors; -// float[] attribs = tessGeo.pointSizes; -// short[] indices = tessGeo.pointIndices; -// -// IndexCache cache = tessGeo.lineIndexCache; -// for (int n = 0; n < cache.size; n++) { -// int ioffset = cache.indexOffset[n]; -// int icount = cache.indexCount[n]; -// int voffset = cache.vertexOffset[n]; -// -// -// } -// -// raw.endShape(); - } - protected void flushLines() { - updateLineBuffers(); - - LineShader shader = getLineShader(); - shader.start(); - - IndexCache cache = tessGeo.lineIndexCache; - for (int n = 0; n < cache.size; n++) { - int ioffset = cache.indexOffset[n]; - int icount = cache.indexCount[n]; - int voffset = cache.vertexOffset[n]; - - shader.setVertexAttribute(glLineVertexBufferID, 4, PGL.GL_FLOAT, 0, 4 * voffset * PGL.SIZEOF_FLOAT); - shader.setColorAttribute(glLineColorBufferID, 4, PGL.GL_UNSIGNED_BYTE, 0, 4 * voffset * PGL.SIZEOF_BYTE); - shader.setDirWidthAttribute(glLineDirWidthBufferID, 4, PGL.GL_FLOAT, 0, 4 * voffset * PGL.SIZEOF_FLOAT); - - pgl.glDrawElements(PGL.GL_TRIANGLES, icount, PGL.INDEX_TYPE, ioffset * PGL.SIZEOF_INDEX); - } - - shader.stop(); - unbindLineBuffers(); - } - - - void rawLines() { - raw.colorMode(RGB); - raw.noFill(); - raw.beginShape(LINES); - - float[] vertices = tessGeo.lineVertices; - int[] color = tessGeo.lineColors; - float[] attribs = tessGeo.lineDirWidths; - short[] indices = tessGeo.lineIndices; - - IndexCache cache = tessGeo.lineIndexCache; - for (int n = 0; n < cache.size; n++) { - int ioffset = cache.indexOffset[n]; - int icount = cache.indexCount[n]; - int voffset = cache.vertexOffset[n]; - - for (int ln = ioffset / 6; ln < (ioffset + icount) / 6; ln++) { - // Each line segment is defined by six indices since its - // formed by two triangles. We only need the first and last - // vertices. - int i0 = voffset + indices[6 * ln + 0]; - int i1 = voffset + indices[6 * ln + 5]; - - float[] pt0 = {0, 0, 0, 0}; - float[] pt1 = {0, 0, 0, 0}; - int argb0 = PGL.nativeToJavaARGB(color[i0]); - int argb1 = PGL.nativeToJavaARGB(color[i1]); - float sw0 = 2 * attribs[4 * i0 + 3]; - float sw1 = 2 * attribs[4 * i1 + 3]; - - if (flushMode == FLUSH_CONTINUOUSLY || hints[DISABLE_TRANSFORM_CACHE]) { - float[] src0 = {0, 0, 0, 0}; - float[] src1 = {0, 0, 0, 0}; - PApplet.arrayCopy(vertices, 4 * i0, src0, 0, 4); - PApplet.arrayCopy(vertices, 4 * i1, src1, 0, 4); - modelview.mult(src0, pt0); - modelview.mult(src1, pt1); - } else { - PApplet.arrayCopy(vertices, 4 * i0, pt0, 0, 4); - PApplet.arrayCopy(vertices, 4 * i1, pt1, 0, 4); - } - - if (raw.is3D()) { - raw.strokeWeight(sw0); - raw.stroke(argb0); - raw.vertex(pt0[X], pt0[Y], pt0[Z]); - raw.strokeWeight(sw1); - raw.stroke(argb1); - raw.vertex(pt1[X], pt1[Y], pt1[Z]); - } else if (raw.is2D()) { - float sx0 = screenX(pt0[0], pt0[1], pt0[2], pt0[3]), sy0 = screenY(pt0[0], pt0[1], pt0[2], pt0[3]); - float sx1 = screenX(pt1[0], pt1[1], pt1[2], pt1[3]), sy1 = screenY(pt1[0], pt1[1], pt1[2], pt1[3]); - raw.strokeWeight(sw0); - raw.stroke(argb0); - raw.vertex(sx0, sy0); - raw.strokeWeight(sw1); - raw.stroke(argb1); - raw.vertex(sx1, sy1); - } - } - } - - raw.endShape(); - } - - - protected void flushFill() { - updateFillBuffers(lights, texCache.haveTexture); + protected void flushPolys() { + updatePolyBuffers(lights, texCache.hasTexture); texCache.beginRender(); for (int i = 0; i < texCache.size; i++) { PTexture tex = texCache.getTexture(i); - FillShader shader = getFillShader(lights, tex != null); - shader.start(); - + + // If the renderer is 2D, then lights should always be false, + // so no need to worry about that. + PolyShader shader = getPolyShader(lights, tex != null); + shader.start(); + int first = texCache.firstCache[i]; int last = texCache.lastCache[i]; - IndexCache cache = tessGeo.fillIndexCache; + 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]; + + shader.setVertexAttribute(glPolyVertexBufferID, 4, PGL.GL_FLOAT, 0, 4 * voffset * PGL.SIZEOF_FLOAT); + shader.setColorAttribute(glPolyColorBufferID, 4, PGL.GL_UNSIGNED_BYTE, 0, 4 * voffset * PGL.SIZEOF_BYTE); - shader.setVertexAttribute(glFillVertexBufferID, 4, PGL.GL_FLOAT, 0, 4 * voffset * PGL.SIZEOF_FLOAT); - shader.setColorAttribute(glFillColorBufferID, 4, PGL.GL_UNSIGNED_BYTE, 0, 4 * voffset * PGL.SIZEOF_BYTE); - if (lights) { - shader.setNormalAttribute(glFillNormalBufferID, 3, PGL.GL_FLOAT, 0, 3 * voffset * PGL.SIZEOF_FLOAT); - shader.setAmbientAttribute(glFillAmbientBufferID, 4, PGL.GL_UNSIGNED_BYTE, 0, 4 * voffset * PGL.SIZEOF_BYTE); - shader.setSpecularAttribute(glFillSpecularBufferID, 4, PGL.GL_UNSIGNED_BYTE, 0, 4 * voffset * PGL.SIZEOF_BYTE); - shader.setEmissiveAttribute(glFillEmissiveBufferID, 4, PGL.GL_UNSIGNED_BYTE, 0, 4 * voffset * PGL.SIZEOF_BYTE); - shader.setShininessAttribute(glFillShininessBufferID, 1, PGL.GL_FLOAT, 0, voffset * PGL.SIZEOF_FLOAT); + shader.setNormalAttribute(glPolyNormalBufferID, 3, PGL.GL_FLOAT, 0, 3 * voffset * PGL.SIZEOF_FLOAT); + shader.setAmbientAttribute(glPolyAmbientBufferID, 4, PGL.GL_UNSIGNED_BYTE, 0, 4 * voffset * PGL.SIZEOF_BYTE); + shader.setSpecularAttribute(glPolySpecularBufferID, 4, PGL.GL_UNSIGNED_BYTE, 0, 4 * voffset * PGL.SIZEOF_BYTE); + shader.setEmissiveAttribute(glPolyEmissiveBufferID, 4, PGL.GL_UNSIGNED_BYTE, 0, 4 * voffset * PGL.SIZEOF_BYTE); + shader.setShininessAttribute(glPolyShininessBufferID, 1, PGL.GL_FLOAT, 0, voffset * PGL.SIZEOF_FLOAT); } - + if (tex != null) { - shader.setTexCoordAttribute(glFillTexCoordBufferID, 2, PGL.GL_FLOAT, 0, 2 * voffset * PGL.SIZEOF_FLOAT); + shader.setTexcoordAttribute(glPolyTexcoordBufferID, 2, PGL.GL_FLOAT, 0, 2 * voffset * PGL.SIZEOF_FLOAT); shader.setTexture(tex); } - + + pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, glPolyIndexBufferID); pgl.glDrawElements(PGL.GL_TRIANGLES, icount, PGL.INDEX_TYPE, ioffset * PGL.SIZEOF_INDEX); + pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, 0); } shader.stop(); } texCache.endRender(); - unbindFillBuffers(); + unbindPolyBuffers(); } - void rawFill() { + void rawPolys() { raw.colorMode(RGB); raw.noStroke(); raw.beginShape(TRIANGLES); - float[] vertices = tessGeo.fillVertices; - int[] color = tessGeo.fillColors; - float[] uv = tessGeo.fillTexcoords; - short[] indices = tessGeo.fillIndices; + float[] vertices = tessGeo.polyVertices; + int[] color = tessGeo.polyColors; + float[] uv = tessGeo.polyTexcoords; + short[] indices = tessGeo.polyIndices; 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.fillIndexCache; + 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 : @@ -2686,9 +2460,9 @@ public class PGraphicsOpenGL extends PGraphics { 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 = screenX(pt0[0], pt0[1], pt0[2], pt0[3]), sy0 = screenY(pt0[0], pt0[1], pt0[2], pt0[3]); - float sx1 = screenX(pt1[0], pt1[1], pt1[2], pt1[3]), sy1 = screenY(pt1[0], pt1[1], pt1[2], pt1[3]); - float sx2 = screenX(pt2[0], pt2[1], pt2[2], pt2[3]), sy2 = screenY(pt2[0], pt2[1], pt2[2], pt2[3]); + float sx0 = screenXImpl(pt0[0], pt0[1], pt0[2], pt0[3]), sy0 = screenYImpl(pt0[0], pt0[1], pt0[2], pt0[3]); + float sx1 = screenXImpl(pt1[0], pt1[1], pt1[2], pt1[3]), sy1 = screenYImpl(pt1[0], pt1[1], pt1[2], pt1[3]); + float sx2 = screenXImpl(pt2[0], pt2[1], pt2[2], pt2[3]), 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); @@ -2705,9 +2479,9 @@ public class PGraphicsOpenGL extends PGraphics { raw.fill(argb2); raw.vertex(pt2[X], pt2[Y], pt2[Z]); } else if (raw.is2D()) { - float sx0 = screenX(pt0[0], pt0[1], pt0[2], pt0[3]), sy0 = screenY(pt0[0], pt0[1], pt0[2], pt0[3]); - float sx1 = screenX(pt1[0], pt1[1], pt1[2], pt1[3]), sy1 = screenY(pt1[0], pt1[1], pt1[2], pt1[3]); - float sx2 = screenX(pt2[0], pt2[1], pt2[2], pt2[3]), sy2 = screenY(pt2[0], pt2[1], pt2[2], pt2[3]); + float sx0 = screenXImpl(pt0[0], pt0[1], pt0[2], pt0[3]), sy0 = screenYImpl(pt0[0], pt0[1], pt0[2], pt0[3]); + float sx1 = screenXImpl(pt1[0], pt1[1], pt1[2], pt1[3]), sy1 = screenYImpl(pt1[0], pt1[1], pt1[2], pt1[3]); + float sx2 = screenXImpl(pt2[0], pt2[1], pt2[2], pt2[3]), sy2 = screenYImpl(pt2[0], pt2[1], pt2[2], pt2[3]); raw.fill(argb0); raw.vertex(sx0, sy0); raw.fill(argb1); @@ -2722,6 +2496,192 @@ public class PGraphicsOpenGL extends PGraphics { raw.endShape(); } + + + protected void flushLines() { + updateLineBuffers(); + + LineShader shader = getLineShader(); + shader.start(); + + IndexCache cache = tessGeo.lineIndexCache; + for (int n = 0; n < cache.size; n++) { + int ioffset = cache.indexOffset[n]; + int icount = cache.indexCount[n]; + int voffset = cache.vertexOffset[n]; + + shader.setVertexAttribute(glLineVertexBufferID, 4, PGL.GL_FLOAT, 0, 4 * voffset * PGL.SIZEOF_FLOAT); + shader.setColorAttribute(glLineColorBufferID, 4, PGL.GL_UNSIGNED_BYTE, 0, 4 * voffset * PGL.SIZEOF_BYTE); + shader.setLineAttribute(glLineAttribBufferID, 4, PGL.GL_FLOAT, 0, 4 * voffset * PGL.SIZEOF_FLOAT); + + pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, glLineIndexBufferID); + pgl.glDrawElements(PGL.GL_TRIANGLES, icount, PGL.INDEX_TYPE, ioffset * PGL.SIZEOF_INDEX); + pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, 0); + } + + shader.stop(); + unbindLineBuffers(); + } + + + void rawLines() { + raw.colorMode(RGB); + raw.noFill(); + raw.strokeCap(strokeCap); + raw.strokeJoin(strokeJoin); + raw.beginShape(LINES); + + float[] vertices = tessGeo.lineVertices; + int[] color = tessGeo.lineColors; + float[] attribs = tessGeo.lineAttribs; + short[] indices = tessGeo.lineIndices; + + IndexCache cache = tessGeo.lineIndexCache; + for (int n = 0; n < cache.size; n++) { + int ioffset = cache.indexOffset[n]; + int icount = cache.indexCount[n]; + int voffset = cache.vertexOffset[n]; + + for (int ln = ioffset / 6; ln < (ioffset + icount) / 6; ln++) { + // Each line segment is defined by six indices since its + // formed by two triangles. We only need the first and last + // vertices. + // This bunch of vertices could also be the bevel triangles, + // with we detect this situation by looking at the line weight. + int i0 = voffset + indices[6 * ln + 0]; + int i1 = voffset + indices[6 * ln + 5]; + float sw0 = 2 * attribs[4 * i0 + 3]; + float sw1 = 2 * attribs[4 * i1 + 3]; + + if (zero(sw0)) continue; // Bevel triangles, skip. + + float[] pt0 = {0, 0, 0, 0}; + float[] pt1 = {0, 0, 0, 0}; + int argb0 = PGL.nativeToJavaARGB(color[i0]); + int argb1 = PGL.nativeToJavaARGB(color[i1]); + + if (flushMode == FLUSH_CONTINUOUSLY || hints[DISABLE_TRANSFORM_CACHE]) { + float[] src0 = {0, 0, 0, 0}; + float[] src1 = {0, 0, 0, 0}; + PApplet.arrayCopy(vertices, 4 * i0, src0, 0, 4); + PApplet.arrayCopy(vertices, 4 * i1, src1, 0, 4); + modelview.mult(src0, pt0); + modelview.mult(src1, pt1); + } else { + PApplet.arrayCopy(vertices, 4 * i0, pt0, 0, 4); + PApplet.arrayCopy(vertices, 4 * i1, pt1, 0, 4); + } + + if (raw.is3D()) { + raw.strokeWeight(sw0); + raw.stroke(argb0); + raw.vertex(pt0[X], pt0[Y], pt0[Z]); + raw.strokeWeight(sw1); + raw.stroke(argb1); + raw.vertex(pt1[X], pt1[Y], pt1[Z]); + } else if (raw.is2D()) { + float sx0 = screenXImpl(pt0[0], pt0[1], pt0[2], pt0[3]), sy0 = screenYImpl(pt0[0], pt0[1], pt0[2], pt0[3]); + float sx1 = screenXImpl(pt1[0], pt1[1], pt1[2], pt1[3]), sy1 = screenYImpl(pt1[0], pt1[1], pt1[2], pt1[3]); + raw.strokeWeight(sw0); + raw.stroke(argb0); + raw.vertex(sx0, sy0); + raw.strokeWeight(sw1); + raw.stroke(argb1); + raw.vertex(sx1, sy1); + } + } + } + + raw.endShape(); + } + + + protected void flushPoints() { + updatePointBuffers(); + + PointShader shader = getPointShader(); + shader.start(); + + IndexCache cache = tessGeo.pointIndexCache; + for (int n = 0; n < cache.size; n++) { + int ioffset = cache.indexOffset[n]; + int icount = cache.indexCount[n]; + int voffset = cache.vertexOffset[n]; + + shader.setVertexAttribute(glPointVertexBufferID, 4, PGL.GL_FLOAT, 0, 4 * voffset * PGL.SIZEOF_FLOAT); + shader.setColorAttribute(glPointColorBufferID, 4, PGL.GL_UNSIGNED_BYTE, 0, 4 * voffset * PGL.SIZEOF_BYTE); + shader.setPointAttribute(glPointAttribBufferID, 2, PGL.GL_FLOAT, 0, 2 * voffset * PGL.SIZEOF_FLOAT); + + pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, glPointIndexBufferID); + pgl.glDrawElements(PGL.GL_TRIANGLES, icount, PGL.INDEX_TYPE, ioffset * PGL.SIZEOF_INDEX); + pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, 0); + } + + shader.stop(); + unbindPointBuffers(); + } + + + void rawPoints() { + raw.colorMode(RGB); + raw.noFill(); + raw.strokeCap(strokeCap); + raw.beginShape(POINTS); + + float[] vertices = tessGeo.pointVertices; + int[] color = tessGeo.pointColors; + float[] attribs = tessGeo.pointAttribs; + short[] indices = tessGeo.pointIndices; + + IndexCache cache = tessGeo.pointIndexCache; + for (int n = 0; n < cache.size; n++) { + int ioffset = cache.indexOffset[n]; + int icount = cache.indexCount[n]; + int voffset = cache.vertexOffset[n]; + + int pt = ioffset; + while (pt < (ioffset + icount) / 3) { + float size = attribs[2 * pt + 2]; + float weight; + int perim; + if (0 < size) { // round point + weight = +size / 0.5f; + perim = PApplet.max(MIN_POINT_ACCURACY, + (int) (TWO_PI * weight / POINT_ACCURACY_FACTOR)) + 1; + } else { // Square point + weight = -size / 0.5f; + perim = 5; + } + + int i0 = voffset + indices[3 * pt]; + int argb0 = PGL.nativeToJavaARGB(color[i0]); + float[] pt0 = {0, 0, 0, 0}; + + if (flushMode == FLUSH_CONTINUOUSLY || hints[DISABLE_TRANSFORM_CACHE]) { + float[] src0 = {0, 0, 0, 0}; + PApplet.arrayCopy(vertices, 4 * i0, src0, 0, 4); + modelview.mult(src0, pt0); + } else { + PApplet.arrayCopy(vertices, 4 * i0, pt0, 0, 4); + } + + if (raw.is3D()) { + raw.strokeWeight(weight); + raw.stroke(argb0); + raw.vertex(pt0[X], pt0[Y], pt0[Z]); + } else if (raw.is2D()) { + float sx0 = screenXImpl(pt0[0], pt0[1], pt0[2], pt0[3]), sy0 = screenYImpl(pt0[0], pt0[1], pt0[2], pt0[3]); + raw.strokeWeight(weight); + raw.stroke(argb0); + raw.vertex(sx0, sy0); + } + + pt += perim; + } + } + + raw.endShape(); + } ////////////////////////////////////////////////////////////// @@ -2732,15 +2692,24 @@ public class PGraphicsOpenGL extends PGraphics { public void bezierVertex(float x2, float y2, float x3, float y3, float x4, float y4) { - bezierVertex(x2, y2, 0, - x3, y3, 0, - x4, y4, 0); + bezierVertexImpl(x2, y2, 0, + x3, y3, 0, + x4, y4, 0); } public void bezierVertex(float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4) { + bezierVertexImpl(x2, y2, z2, + x3, y3, z3, + x4, y4, z4); + } + + + protected void bezierVertexImpl(float x2, float y2, float z2, + float x3, float y3, float z3, + float x4, float y4, float z4) { inGeo.setMaterial(fillColor, strokeColor, strokeWeight, ambientColor, specularColor, emissiveColor, shininess); inGeo.setNormal(normalX, normalY, normalZ); @@ -2749,18 +2718,25 @@ public class PGraphicsOpenGL extends PGraphics { x4, y4, z4, fill, stroke, bezierDetail, vertexCode(), shape); - } - + } + public void quadraticVertex(float cx, float cy, float x3, float y3) { - quadraticVertex(cx, cy, 0, - x3, y3, 0); + quadraticVertexImpl(cx, cy, 0, + x3, y3, 0); } - + public void quadraticVertex(float cx, float cy, float cz, float x3, float y3, float z3) { + quadraticVertexImpl(cx, cy, cz, + x3, y3, z3); + } + + + protected void quadraticVertexImpl(float cx, float cy, float cz, + float x3, float y3, float z3) { inGeo.setMaterial(fillColor, strokeColor, strokeWeight, ambientColor, specularColor, emissiveColor, shininess); inGeo.setNormal(normalX, normalY, normalZ); @@ -2776,11 +2752,16 @@ public class PGraphicsOpenGL extends PGraphics { public void curveVertex(float x, float y) { - curveVertex(x, y, 0); + curveVertexImpl(x, y, 0); } - + public void curveVertex(float x, float y, float z) { + curveVertexImpl(x, y, z); + } + + + protected void curveVertexImpl(float x, float y, float z) { inGeo.setMaterial(fillColor, strokeColor, strokeWeight, ambientColor, specularColor, emissiveColor, shininess); inGeo.setNormal(normalX, normalY, normalZ); @@ -2795,11 +2776,16 @@ public class PGraphicsOpenGL extends PGraphics { public void point(float x, float y) { - point(x, y, 0); + pointImpl(x, y, 0); } public void point(float x, float y, float z) { + pointImpl(x, y, z); + } + + + protected void pointImpl(float x, float y, float z) { beginShape(POINTS); defaultEdges = false; normalMode = NORMAL_MODE_SHAPE; @@ -2812,12 +2798,18 @@ public class PGraphicsOpenGL extends PGraphics { public void line(float x1, float y1, float x2, float y2) { - line(x1, y1, 0, x2, y2, 0); + lineImpl(x1, y1, 0, x2, y2, 0); } public void line(float x1, float y1, float z1, float x2, float y2, float z2) { + lineImpl(x1, y1, z1, x2, y2, z2); + } + + + protected void lineImpl(float x1, float y1, float z1, + float x2, float y2, float z2) { beginShape(LINES); defaultEdges = false; normalMode = NORMAL_MODE_SHAPE; @@ -3046,10 +3038,10 @@ public class PGraphicsOpenGL extends PGraphics { level = maxSamples; } - if (antialias != level) { - antialias = level; - if (antialias == 1) { - antialias = 0; + if (quality != level) { + quality = level; + if (quality == 1) { + quality = 0; } // This will trigger a surface restart next time // requestDraw() is called. @@ -3061,8 +3053,8 @@ public class PGraphicsOpenGL extends PGraphics { public void noSmooth() { smooth = false; - if (1 < antialias) { - antialias = 0; + if (1 < quality) { + quality = 0; // This will trigger a surface restart next time // requestDraw() is called. pgl.initialized = false; @@ -3126,6 +3118,24 @@ public class PGraphicsOpenGL extends PGraphics { popMatrix(); } } + + + ////////////////////////////////////////////////////////////// + + // SHAPE I/O + + + public PShape loadShape(String filename) { + String ext = PApplet.getExtension(filename); + if (PGraphics2D.isSupportedExtension(ext)) { + return PGraphics2D.loadShapeImpl(this, filename, ext); + } if (PGraphics3D.isSupportedExtension(ext)) { + return PGraphics3D.loadShapeImpl(this, filename, ext); + } else { + PGraphics.showWarning("Unsupported format"); + return null; + } + } ////////////////////////////////////////////////////////////// @@ -3173,14 +3183,14 @@ public class PGraphicsOpenGL extends PGraphics { * Implementation of actual drawing for a line of text. */ protected void textLineImpl(char buffer[], int start, int stop, float x, float y) { - textTex = (PFontTexture)textFont.getCache(pg); + textTex = (PFontTexture)textFont.getCache(pgPrimary); if (textTex == null) { - textTex = new PFontTexture(parent, textFont, maxTextureSize, maxTextureSize); + textTex = new PFontTexture(parent, textFont, maxTextureSize, maxTextureSize, is3D()); textFont.setCache(this, textTex); } else { if (textTex.contextIsOutdated()) { textTex = new PFontTexture(parent, textFont, PApplet.min(PGL.MAX_FONT_TEX_SIZE, maxTextureSize), - PApplet.min(PGL.MAX_FONT_TEX_SIZE, maxTextureSize)); + PApplet.min(PGL.MAX_FONT_TEX_SIZE, maxTextureSize), is3D()); textFont.setCache(this, textTex); } } @@ -3304,11 +3314,16 @@ public class PGraphicsOpenGL extends PGraphics { public void translate(float tx, float ty) { - translate(tx, ty, 0); + translateImpl(tx, ty, 0); } public void translate(float tx, float ty, float tz) { + translateImpl(tx, ty, tz); + } + + + protected void translateImpl(float tx, float ty, float tz) { if (hints[DISABLE_TRANSFORM_CACHE]) { flush(); } @@ -3334,22 +3349,22 @@ public class PGraphicsOpenGL extends PGraphics { * for the confusion. */ public void rotate(float angle) { - rotateZ(angle); + rotateImpl(angle, 0, 0, 1); } public void rotateX(float angle) { - rotate(angle, 1, 0, 0); + rotateImpl(angle, 1, 0, 0); } public void rotateY(float angle) { - rotate(angle, 0, 1, 0); + rotateImpl(angle, 0, 1, 0); } public void rotateZ(float angle) { - rotate(angle, 0, 0, 1); + rotateImpl(angle, 0, 0, 1); } @@ -3358,17 +3373,22 @@ public class PGraphicsOpenGL extends PGraphics { * takes radians (instead of degrees). */ public void rotate(float angle, float v0, float v1, float v2) { + rotateImpl(angle, v0, v1, v2); + } + + + protected void rotateImpl(float angle, float v0, float v1, float v2) { if (hints[DISABLE_TRANSFORM_CACHE]) { flush(); } float norm2 = v0 * v0 + v1 * v1 + v2 * v2; - if (norm2 < EPSILON) { + if (zero(norm2)) { // The vector is zero, cannot apply rotation. return; } - if (Math.abs(norm2 - 1) > EPSILON) { + if (diff(norm2, 1)) { // The rotation vector is not normalized. float norm = PApplet.sqrt(norm2); v0 /= norm; @@ -3378,10 +3398,10 @@ public class PGraphicsOpenGL extends PGraphics { modelview.rotate(angle, v0, v1, v2); invRotate(modelviewInv, angle, v0, v1, v2); - calcProjmodelview(); // Possibly cheaper than doing projmodelview.rotate() + calcProjmodelview(); // Possibly cheaper than doing projmodelview.rotate() } - - + + static private void invRotate(PMatrix3D matrix, float angle, float v0, float v1, float v2) { float c = PApplet.cos(-angle); float s = PApplet.sin(-angle); @@ -3398,7 +3418,7 @@ public class PGraphicsOpenGL extends PGraphics { * Same as scale(s, s, s). */ public void scale(float s) { - scale(s, s, s); + scaleImpl(s, s, s); } @@ -3406,14 +3426,21 @@ public class PGraphicsOpenGL extends PGraphics { * Same as scale(sx, sy, 1). */ public void scale(float sx, float sy) { - scale(sx, sy, 1); + scaleImpl(sx, sy, 1); } - + /** * Scale in three dimensions. */ public void scale(float sx, float sy, float sz) { + scaleImpl(sx, sy, sz); + } + + /** + * Scale in three dimensions. + */ + protected void scaleImpl(float sx, float sy, float sz) { if (hints[DISABLE_TRANSFORM_CACHE]) { flush(); } @@ -3431,19 +3458,19 @@ public class PGraphicsOpenGL extends PGraphics { public void shearX(float angle) { float t = (float) Math.tan(angle); - applyMatrix(1, t, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1); + applyMatrixImpl(1, t, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); } public void shearY(float angle) { float t = (float) Math.tan(angle); - applyMatrix(1, 0, 0, 0, - t, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1); + applyMatrixImpl(1, 0, 0, 0, + t, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); } @@ -3460,25 +3487,27 @@ public class PGraphicsOpenGL extends PGraphics { public void applyMatrix(PMatrix2D source) { - applyMatrix(source.m00, source.m01, source.m02, - source.m10, source.m11, source.m12); + applyMatrixImpl(source.m00, source.m01, 0, source.m02, + source.m10, source.m11, 0, source.m12, + 0, 0, 1, 0, + 0, 0, 0, 1); } public void applyMatrix(float n00, float n01, float n02, float n10, float n11, float n12) { - applyMatrix(n00, n01, n02, 0, - n10, n11, n12, 0, - 0, 0, 1, 0, - 0, 0, 0, 1); + applyMatrixImpl(n00, n01, 0, n02, + n10, n11, 0, n12, + 0, 0, 1, 0, + 0, 0, 0, 1); } public void applyMatrix(PMatrix3D source) { - applyMatrix(source.m00, source.m01, source.m02, source.m03, - source.m10, source.m11, source.m12, source.m13, - source.m20, source.m21, source.m22, source.m23, - source.m30, source.m31, source.m32, source.m33); + applyMatrixImpl(source.m00, source.m01, source.m02, source.m03, + source.m10, source.m11, source.m12, source.m13, + source.m20, source.m21, source.m22, source.m23, + source.m30, source.m31, source.m32, source.m33); } @@ -3489,6 +3518,17 @@ public class PGraphicsOpenGL extends PGraphics { float n10, float n11, float n12, float n13, float n20, float n21, float n22, float n23, float n30, float n31, float n32, float n33) { + applyMatrixImpl(n00, n01, n02, n03, + n10, n11, n12, n13, + n20, n21, n22, n23, + n30, n31, n32, n33); + } + + + protected void applyMatrixImpl(float n00, float n01, float n02, float n03, + float n10, float n11, float n12, float n13, + float n20, float n21, float n22, float n23, + float n30, float n31, float n32, float n33) { if (hints[DISABLE_TRANSFORM_CACHE]) { flush(); } @@ -3576,7 +3616,32 @@ public class PGraphicsOpenGL extends PGraphics { projection.set(mat); } + + ////////////////////////////////////////////////////////////// + // Some float math utilities + + + protected static boolean same(float a, float b) { + return Math.abs(a - b) < PGL.FLOAT_EPS; + } + + + protected static boolean diff(float a, float b) { + return PGL.FLOAT_EPS <= Math.abs(a - b); + } + + + protected static boolean zero(float a) { + return Math.abs(a) < PGL.FLOAT_EPS; + } + + + protected static boolean nonZero(float a) { + return PGL.FLOAT_EPS <= Math.abs(a); + } + + ////////////////////////////////////////////////////////////// // CAMERA @@ -3768,7 +3833,7 @@ public class PGraphicsOpenGL extends PGraphics { float z1 = eyeY - centerY; float z2 = eyeZ - centerZ; float mag = PApplet.sqrt(z0 * z0 + z1 * z1 + z2 * z2); - if (mag != 0) { + if (nonZero(mag)) { z0 /= mag; z1 /= mag; z2 /= mag; @@ -3795,14 +3860,14 @@ public class PGraphicsOpenGL extends PGraphics { // Cross product gives area of parallelogram, which is < 1.0 for // non-perpendicular unit-length vectors; so normalize x, y here: mag = PApplet.sqrt(x0 * x0 + x1 * x1 + x2 * x2); - if (mag != 0) { + if (nonZero(mag)) { x0 /= mag; x1 /= mag; x2 /= mag; } mag = PApplet.sqrt(y0 * y0 + y1 * y1 + y2 * y2); - if (mag != 0) { + if (nonZero(mag)) { y0 /= mag; y1 /= mag; y2 /= mag; @@ -3874,15 +3939,6 @@ public class PGraphicsOpenGL extends PGraphics { float near, float far) { // Flushing geometry with a different perspective configuration. flush(); - - float halfw = 0.5f * (right - left); - float halfh = 0.5f * (top - bottom); - - left -= halfw; - right -= halfw; - - bottom -= halfh; - top -= halfh; float x = 2.0f / (right - left); float y = 2.0f / (top - bottom); @@ -3978,50 +4034,65 @@ public class PGraphicsOpenGL extends PGraphics { public float screenX(float x, float y) { - return screenX(x, y, 0); + return screenXImpl(x, y, 0); } public float screenY(float x, float y) { - return screenY(x, y, 0); + return screenYImpl(x, y, 0); } public float screenX(float x, float y, float z) { + return screenXImpl(x, y, z); + } + + + public float screenY(float x, float y, float z) { + return screenYImpl(x, y, z); + } + + + public float screenZ(float x, float y, float z) { + return screenZImpl(x, y, z); + } + + + protected float screenXImpl(float x, float y, float z) { float ax = modelview.m00 * x + modelview.m01 * y + modelview.m02 * z + modelview.m03; float ay = modelview.m10 * x + modelview.m11 * y + modelview.m12 * z + modelview.m13; float az = modelview.m20 * x + modelview.m21 * y + modelview.m22 * z + modelview.m23; float aw = modelview.m30 * x + modelview.m31 * y + modelview.m32 * z + modelview.m33; - return screenX(ax, ay, az, aw); + return screenXImpl(ax, ay, az, aw); } - protected float screenX(float x, float y, float z, float w) { + protected float screenXImpl(float x, float y, float z, float w) { float ox = projection.m00 * x + projection.m01 * y + projection.m02 * z + projection.m03 * w; float ow = projection.m30 * x + projection.m31 * y + projection.m32 * z + projection.m33 * w; - if (ow != 0) { + if (nonZero(ow)) { ox /= ow; } float sx = width * (1 + ox) / 2.0f; return sx; } - - public float screenY(float x, float y, float z) { + + protected float screenYImpl(float x, float y, float z) { float ax = modelview.m00 * x + modelview.m01 * y + modelview.m02 * z + modelview.m03; float ay = modelview.m10 * x + modelview.m11 * y + modelview.m12 * z + modelview.m13; float az = modelview.m20 * x + modelview.m21 * y + modelview.m22 * z + modelview.m23; float aw = modelview.m30 * x + modelview.m31 * y + modelview.m32 * z + modelview.m33; - return screenY(ax, ay, az, aw); + return screenYImpl(ax, ay, az, aw); } - protected float screenY(float x, float y, float z, float w) { + protected float screenYImpl(float x, float y, float z, float w) { float oy = projection.m10 * x + projection.m11 * y + projection.m12 * z + projection.m13 * w; float ow = projection.m30 * x + projection.m31 * y + projection.m32 * z + projection.m33 * w; - if (ow != 0) { + if (nonZero(ow)) { oy /= ow; } float sy = height * (1 + oy) / 2.0f; @@ -4030,21 +4101,21 @@ public class PGraphicsOpenGL extends PGraphics { return sy; } - - public float screenZ(float x, float y, float z) { + + protected float screenZImpl(float x, float y, float z) { float ax = modelview.m00 * x + modelview.m01 * y + modelview.m02 * z + modelview.m03; float ay = modelview.m10 * x + modelview.m11 * y + modelview.m12 * z + modelview.m13; float az = modelview.m20 * x + modelview.m21 * y + modelview.m22 * z + modelview.m23; float aw = modelview.m30 * x + modelview.m31 * y + modelview.m32 * z + modelview.m33; - return screenZ(ax, ay, az, aw); + return screenZImpl(ax, ay, az, aw); } - protected float screenZ(float x, float y, float z, float w) { + protected float screenZImpl(float x, float y, float z, float w) { float oz = projection.m20 * x + projection.m21 * y + projection.m22 * z + projection.m23 * w; float ow = projection.m30 * x + projection.m31 * y + projection.m32 * z + projection.m33 * w; - if (ow != 0) { + if (nonZero(ow)) { oz /= ow; } float sz = (oz + 1) / 2.0f; @@ -4061,7 +4132,7 @@ public class PGraphicsOpenGL extends PGraphics { float ox = cameraInv.m00 * ax + cameraInv.m01 * ay + cameraInv.m02 * az + cameraInv.m03 * aw; float ow = cameraInv.m30 * ax + cameraInv.m31 * ay + cameraInv.m32 * az + cameraInv.m33 * aw; - return (ow != 0) ? ox / ow : ox; + return nonZero(ow) ? ox / ow : ox; } @@ -4074,7 +4145,7 @@ public class PGraphicsOpenGL extends PGraphics { float oy = cameraInv.m10 * ax + cameraInv.m11 * ay + cameraInv.m12 * az + cameraInv.m13 * aw; float ow = cameraInv.m30 * ax + cameraInv.m31 * ay + cameraInv.m32 * az + cameraInv.m33 * aw; - return (ow != 0) ? oy / ow : oy; + return nonZero(ow) ? oy / ow : oy; } @@ -4087,7 +4158,7 @@ public class PGraphicsOpenGL extends PGraphics { float oz = cameraInv.m20 * ax + cameraInv.m21 * ay + cameraInv.m22 * az + cameraInv.m23 * aw; float ow = cameraInv.m30 * ax + cameraInv.m31 * ay + cameraInv.m32 * az + cameraInv.m33 * aw; - return (ow != 0) ? oz / ow : oz; + return nonZero(ow) ? oz / ow : oz; } // STYLES @@ -4628,24 +4699,6 @@ public class PGraphicsOpenGL extends PGraphics { // public boolean displayable() - // public boolean dimensional() // from P3D - - - /** - * Return true if this renderer supports 2D drawing. Defaults to true. - */ - public boolean is2D() { - return true; - } - - - /** - * Return true if this renderer supports 2D drawing. Defaults to false. - */ - public boolean is3D() { - return true; - } - public boolean isGL() { return true; @@ -4687,7 +4740,7 @@ public class PGraphicsOpenGL extends PGraphics { readPixels(); if (primarySurface) { - loadTextureImpl(POINT); + loadTextureImpl(POINT, false); pixelsToTexture(); } } @@ -4740,7 +4793,7 @@ public class PGraphicsOpenGL extends PGraphics { // Copying pixel buffer to screen texture... if (primarySurface) { - loadTextureImpl(POINT); // (first making sure that the screen texture is valid). + loadTextureImpl(POINT, false); // (first making sure that the screen texture is valid). } pgl.copyToTexture(texture.glTarget, texture.glFormat, texture.glID, x, y, w, h, IntBuffer.wrap(rgbaPixels)); @@ -4802,7 +4855,7 @@ public class PGraphicsOpenGL extends PGraphics { // array, and then the pixels array into the screen texture. public void loadTexture() { if (primarySurface) { - loadTextureImpl(POINT); + loadTextureImpl(POINT, false); loadPixels(); pixelsToTexture(); } @@ -4818,14 +4871,14 @@ public class PGraphicsOpenGL extends PGraphics { } - protected void loadTextureImpl(int sampling) { + protected void loadTextureImpl(int sampling, boolean mipmap) { if (width == 0 || height == 0) return; if (texture == null || texture.contextIsOutdated()) { - PTexture.Parameters params = PTexture.newParameters(ARGB, sampling); + PTexture.Parameters params = new PTexture.Parameters(ARGB, sampling, mipmap); texture = new PTexture(parent, width, height, params); texture.setFlippedY(true); - this.setCache(pg, texture); - this.setParams(pg, params); + this.setCache(pgPrimary, texture); + this.setParams(pgPrimary, params); } } @@ -5078,380 +5131,6 @@ public class PGraphicsOpenGL extends PGraphics { // public void save(String filename) // PImage calls loadPixels() - - ////////////////////////////////////////////////////////////// - - // SHAPE I/O - - - // Stores a face from an OBJ file - protected class OBJFace { - ArrayList vertIdx; - ArrayList texIdx; - ArrayList normIdx; - int matIdx; - String name; - - OBJFace() { - vertIdx = new ArrayList(); - texIdx = new ArrayList(); - normIdx = new ArrayList(); - matIdx = -1; - name = ""; - } - } - - - // Stores a material defined in an MTL file. - protected class OBJMaterial { - String name; - PVector ka; - PVector kd; - PVector ks; - float d; - float ns; - PImage kdMap; - - OBJMaterial() { - this("default"); - } - - OBJMaterial(String name) { - this.name = name; - ka = new PVector(0.5f, 0.5f, 0.5f); - kd = new PVector(0.5f, 0.5f, 0.5f); - ks = new PVector(0.5f, 0.5f, 0.5f); - d = 1.0f; - ns = 0.0f; - kdMap = null; - } - } - - - protected String[] getSupportedShapeFormats() { - return new String[] { "obj" }; - } - - - public PShape loadShape(String filename) { - ArrayList vertices = new ArrayList(); - ArrayList normals = new ArrayList(); - ArrayList textures = new ArrayList(); - ArrayList faces = new ArrayList(); - ArrayList materials = new ArrayList(); - - BufferedReader reader = parent.createReader(filename); - parseOBJ(reader, vertices, normals, textures, faces, materials); - - int prevColorMode = pg.colorMode; - float prevColorModeX = pg.colorModeX; - float prevColorModeY = pg.colorModeY; - float prevColorModeZ = pg.colorModeZ; - float prevColorModeA = pg.colorModeA; - boolean prevStroke = pg.stroke; - int prevTextureMode = pg.textureMode; - pg.colorMode(RGB, 1); - pg.stroke = false; - pg.textureMode = NORMAL; - - - // The OBJ geometry is stored in a group shape, - // with each face in a separate child geometry - // shape. - PShape root = createShape(GROUP); - - int mtlIdxCur = -1; - OBJMaterial mtl = null; - for (int i = 0; i < faces.size(); i++) { - OBJFace face = faces.get(i); - - // Getting current material. - if (mtlIdxCur != face.matIdx) { - mtlIdxCur = PApplet.max(0, face.matIdx); // To make sure that at least we get the default material. - mtl = materials.get(mtlIdxCur); - } - - // Creating child shape for current face. - PShape child; - if (face.vertIdx.size() == 3) { - child = createShape(TRIANGLES); // Face is a triangle, so using appropriate shape kind. - } else if (face.vertIdx.size() == 4) { - child = createShape(QUADS); // Face is a quad, so using appropriate shape kind. - } else { - child = createShape(POLYGON); // Face is a general polygon - } - - // Setting material properties for the new face - child.fill(mtl.kd.x, mtl.kd.y, mtl.kd.z); - child.ambient(mtl.ka.x, mtl.ka.y, mtl.ka.z); - child.specular(mtl.ks.x, mtl.ks.y, mtl.ks.z); - child.shininess(mtl.ns); - if (mtl.kdMap != null) { - // If current material is textured, then tinting the texture using the diffuse color. - child.tint(mtl.kd.x, mtl.kd.y, mtl.kd.z, mtl.d); - } - - for (int j = 0; j < face.vertIdx.size(); j++){ - int vertIdx, normIdx; - PVector vert, norms; - - vert = norms = null; - - vertIdx = face.vertIdx.get(j).intValue() - 1; - vert = vertices.get(vertIdx); - - if (j < face.normIdx.size()) { - normIdx = face.normIdx.get(j).intValue() - 1; - if (-1 < normIdx) { - norms = normals.get(normIdx); - } - } - - if (mtl != null && mtl.kdMap != null) { - // This face is textured. - int texIdx; - PVector tex = null; - - if (j < face.texIdx.size()) { - texIdx = face.texIdx.get(j).intValue() - 1; - if (-1 < texIdx) { - tex = textures.get(texIdx); - } - } - - child.texture(mtl.kdMap); - if (norms != null) { - child.normal(norms.x, norms.y, norms.z); - } - if (tex != null) { - child.vertex(vert.x, vert.y, vert.z, tex.x, tex.y); - } else { - child.vertex(vert.x, vert.y, vert.z); - } - } else { - // This face is not textured. - if (norms != null) { - child.normal(norms.x, norms.y, norms.z); - } - child.vertex(vert.x, vert.y, vert.z); - } - } - - child.end(CLOSE); - root.addChild(child); - } - - pg.colorMode(prevColorMode, prevColorModeX, prevColorModeY, prevColorModeZ, prevColorModeA); - pg.stroke = prevStroke; - pg.textureMode = prevTextureMode; - - return root; - } - - - protected void parseOBJ(BufferedReader reader, ArrayList vertices, - ArrayList normals, - ArrayList textures, - ArrayList faces, - ArrayList materials) { - Hashtable mtlTable = new Hashtable(); - int mtlIdxCur = -1; - boolean readv, readvn, readvt; - try { - - readv = readvn = readvt = false; - String line; - String gname = "object"; - while ((line = reader.readLine()) != null) { - // Parse the line. - - // The below patch/hack comes from Carlos Tomas Marti and is a - // fix for single backslashes in Rhino obj files - - // BEGINNING OF RHINO OBJ FILES HACK - // Statements can be broken in multiple lines using '\' at the - // end of a line. - // In regular expressions, the backslash is also an escape - // character. - // The regular expression \\ matches a single backslash. This - // regular expression as a Java string, becomes "\\\\". - // That's right: 4 backslashes to match a single one. - while (line.contains("\\")) { - line = line.split("\\\\")[0]; - final String s = reader.readLine(); - if (s != null) - line += s; - } - // END OF RHINO OBJ FILES HACK - - String[] elements = line.split("\\s+"); - // if not a blank line, process the line. - if (elements.length > 0) { - if (elements[0].equals("v")) { - // vertex - PVector tempv = new PVector(Float.valueOf(elements[1]).floatValue(), - Float.valueOf(elements[2]).floatValue(), - Float.valueOf(elements[3]).floatValue()); - vertices.add(tempv); - readv = true; - } else if (elements[0].equals("vn")) { - // normal - PVector tempn = new PVector(Float.valueOf(elements[1]).floatValue(), - Float.valueOf(elements[2]).floatValue(), - Float.valueOf(elements[3]).floatValue()); - normals.add(tempn); - readvn = true; - } else if (elements[0].equals("vt")) { - // uv, inverting v to take into account Processing's invertex Y axis with - // respect to OpenGL. - PVector tempv = new PVector(Float.valueOf(elements[1]).floatValue(), - 1 - Float.valueOf(elements[2]).floatValue()); - textures.add(tempv); - readvt = true; - } else if (elements[0].equals("o")) { - // Object name is ignored, for now. - } else if (elements[0].equals("mtllib")) { - if (elements[1] != null) { - BufferedReader mreader = parent.createReader(elements[1]); - if (mreader != null) { - parseMTL(mreader, materials, mtlTable); - } - } - } else if (elements[0].equals("g")) { - gname = 1 < elements.length ? elements[1] : ""; - } else if (elements[0].equals("usemtl")) { - // Getting index of current active material (will be applied on all subsequent faces). - if (elements[1] != null) { - String mtlname = elements[1]; - if (mtlTable.containsKey(mtlname)) { - Integer tempInt = mtlTable.get(mtlname); - mtlIdxCur = tempInt.intValue(); - } else { - mtlIdxCur = -1; - } - } - } else if (elements[0].equals("f")) { - // Face setting - OBJFace face = new OBJFace(); - face.matIdx = mtlIdxCur; - face.name = gname; - - for (int i = 1; i < elements.length; i++) { - String seg = elements[i]; - - if (seg.indexOf("/") > 0) { - String[] forder = seg.split("/"); - - if (forder.length > 2) { - // Getting vertex and texture and normal indexes. - if (forder[0].length() > 0 && readv) { - face.vertIdx.add(Integer.valueOf(forder[0])); - } - - if (forder[1].length() > 0 && readvt) { - face.texIdx.add(Integer.valueOf(forder[1])); - } - - if (forder[2].length() > 0 && readvn) { - face.normIdx.add(Integer.valueOf(forder[2])); - } - } else if (forder.length > 1) { - // Getting vertex and texture/normal indexes. - if (forder[0].length() > 0 && readv) { - face.vertIdx.add(Integer.valueOf(forder[0])); - } - - if (forder[1].length() > 0) { - if (readvt) { - face.texIdx.add(Integer.valueOf(forder[1])); - } else if (readvn) { - face.normIdx.add(Integer.valueOf(forder[1])); - } - - } - - } else if (forder.length > 0) { - // Getting vertex index only. - if (forder[0].length() > 0 && readv) { - face.vertIdx.add(Integer.valueOf(forder[0])); - } - } - } else { - // Getting vertex index only. - if (seg.length() > 0 && readv) { - face.vertIdx.add(Integer.valueOf(seg)); - } - } - } - - faces.add(face); - } - } - } - - if (materials.size() == 0) { - // No materials definition so far. Adding one default material. - OBJMaterial defMtl = new OBJMaterial(); - materials.add(defMtl); - } - - } catch (Exception e) { - e.printStackTrace(); - } - } - - protected void parseMTL(BufferedReader reader, ArrayList materials, Hashtable materialsHash) { - try { - String line; - OBJMaterial currentMtl = null; - while ((line = reader.readLine()) != null) { - // Parse the line - line = line.trim(); - - String elements[] = line.split("\\s+"); - - if (elements.length > 0) { - // Extract the material data. - - if (elements[0].equals("newmtl")) { - // Starting new material. - String mtlname = elements[1]; - currentMtl = new OBJMaterial(mtlname); - materialsHash.put(mtlname, new Integer(materials.size())); - materials.add(currentMtl); - } else if (elements[0].equals("map_Kd") && elements.length > 1) { - // Loading texture map. - String texname = elements[1]; - currentMtl.kdMap = parent.loadImage(texname); - } else if (elements[0].equals("Ka") && elements.length > 3) { - // The ambient color of the material - currentMtl.ka.x = Float.valueOf(elements[1]).floatValue(); - currentMtl.ka.y = Float.valueOf(elements[2]).floatValue(); - currentMtl.ka.z = Float.valueOf(elements[3]).floatValue(); - } else if (elements[0].equals("Kd") && elements.length > 3) { - // The diffuse color of the material - currentMtl.kd.x = Float.valueOf(elements[1]).floatValue(); - currentMtl.kd.y = Float.valueOf(elements[2]).floatValue(); - currentMtl.kd.z = Float.valueOf(elements[3]).floatValue(); - } else if (elements[0].equals("Ks") && elements.length > 3) { - // The specular color weighted by the specular coefficient - currentMtl.ks.x = Float.valueOf(elements[1]).floatValue(); - currentMtl.ks.y = Float.valueOf(elements[2]).floatValue(); - currentMtl.ks.z = Float.valueOf(elements[3]).floatValue(); - } else if ((elements[0].equals("d") || elements[0].equals("Tr")) && elements.length > 1) { - // Reading the alpha transparency. - currentMtl.d = Float.valueOf(elements[1]).floatValue(); - } else if (elements[0].equals("Ns") && elements.length > 1) { - // The specular component of the Phong shading model - currentMtl.ns = Float.valueOf(elements[1]).floatValue(); - } - } - } - } catch (Exception e) { - e.printStackTrace(); - } - } - ////////////////////////////////////////////////////////////// @@ -5476,7 +5155,7 @@ public class PGraphicsOpenGL extends PGraphics { * @param img the image to have a texture metadata associated to it */ public PTexture getTexture(PImage img) { - PTexture tex = (PTexture)img.getCache(pg); + PTexture tex = (PTexture)img.getCache(pgPrimary); if (tex == null) { tex = addTexture(img); } else { @@ -5505,10 +5184,31 @@ public class PGraphicsOpenGL extends PGraphics { * @param img the image to have a texture metadata associated to it */ protected PTexture addTexture(PImage img) { - PTexture.Parameters params = (PTexture.Parameters)img.getParams(pg); + PTexture.Parameters params = (PTexture.Parameters)img.getParams(pgPrimary); if (params == null) { - params = PTexture.newParameters(); - img.setParams(pg, params); + params = new PTexture.Parameters(); + if (hints[DISABLE_TEXTURE_MIPMAPS]) { + params.mipmaps = false; + } else { + params.mipmaps = true; + } + if (textureQuality == LOW) { + params.sampling = POINT; + } else if (textureQuality == MEDIUM) { + params.sampling = LINEAR; + } else if (textureQuality == HIGH) { + params.sampling = BILINEAR; + } else if (textureQuality == BEST) { + if (params.mipmaps) { + params.sampling = TRILINEAR; + } else { + params.sampling = BILINEAR; + PGraphics.showWarning("BEST texture quality requires mipmaps, will switch to HIGH."); + } + } + params.wrapU = textureWrap; + params.wrapV = textureWrap; + img.setParams(pgPrimary, params); } if (img.parent == null) { img.parent = parent; @@ -5516,7 +5216,7 @@ public class PGraphicsOpenGL extends PGraphics { PTexture tex = new PTexture(img.parent, img.width, img.height, params); img.loadPixels(); if (img.pixels != null) tex.set(img.pixels); - img.setCache(pg, tex); + img.setCache(pgPrimary, tex); return tex; } @@ -5529,7 +5229,7 @@ public class PGraphicsOpenGL extends PGraphics { img.width = tex.width; img.height = tex.height; img.format = ARGB; - img.setCache(pg, tex); + img.setCache(pgPrimary, tex); return img; } @@ -5562,18 +5262,20 @@ public class PGraphicsOpenGL extends PGraphics { protected void initPrimary() { - pgl.initPrimarySurface(antialias); - pg = this; + pgl.initPrimarySurface(quality); + if (pgPrimary == null) { + pgPrimary = this; + } } protected void initOffscreen() { // Getting the context and capabilities from the main renderer. - pg = (PGraphicsOpenGL)parent.g; - pgl.initOffscreenSurface(pg.pgl); - pgl.updateOffscreen(pg.pgl); + pgPrimary = (PGraphicsOpenGL)parent.g; + pgl.initOffscreenSurface(pgPrimary.pgl); + pgl.updateOffscreen(pgPrimary.pgl); - loadTextureImpl(BILINEAR); + loadTextureImpl(BILINEAR, false); // In case of reinitialization (for example, when the smooth level // is changed), we make sure that all the OpenGL resources associated @@ -5585,8 +5287,8 @@ public class PGraphicsOpenGL extends PGraphics { offscreenFramebufferMultisample.release(); } - if (PGraphicsOpenGL.fboMultisampleSupported && 1 < antialias) { - offscreenFramebufferMultisample = new PFramebuffer(parent, texture.glWidth, texture.glHeight, antialias, 0, + if (PGraphicsOpenGL.fboMultisampleSupported && 1 < quality) { + offscreenFramebufferMultisample = new PFramebuffer(parent, texture.glWidth, texture.glHeight, quality, 0, depthBits, stencilBits, depthBits == 24 && stencilBits == 8 && packedDepthStencilSupported, false); @@ -5600,7 +5302,7 @@ public class PGraphicsOpenGL extends PGraphics { false, false); } else { - antialias = 0; + quality = 0; offscreenFramebuffer = new PFramebuffer(parent, texture.glWidth, texture.glHeight, 1, 1, depthBits, stencilBits, depthBits == 24 && stencilBits == 8 && packedDepthStencilSupported, false); @@ -5617,9 +5319,10 @@ public class PGraphicsOpenGL extends PGraphics { OPENGL_RENDERER = pgl.glGetString(PGL.GL_RENDERER); OPENGL_VERSION = pgl.glGetString(PGL.GL_VERSION); OPENGL_EXTENSIONS = pgl.glGetString(PGL.GL_EXTENSIONS); + GLSL_VERSION = pgl.glGetString(PGL.GL_SHADING_LANGUAGE_VERSION); npotTexSupported = -1 < OPENGL_EXTENSIONS.indexOf("texture_non_power_of_two"); - mipmapGeneration = -1 < OPENGL_EXTENSIONS.indexOf("generate_mipmap"); + autoMipmapGenSupported = -1 < OPENGL_EXTENSIONS.indexOf("generate_mipmap"); fboMultisampleSupported = -1 < OPENGL_EXTENSIONS.indexOf("framebuffer_multisample"); packedDepthStencilSupported = -1 < OPENGL_EXTENSIONS.indexOf("packed_depth_stencil"); @@ -5660,17 +5363,17 @@ public class PGraphicsOpenGL extends PGraphics { public PShader loadShader(String vertFilename, String fragFilename, int kind) { - if (kind == FILL_SHADER_SIMPLE) { - return new FillShaderSimple(parent, vertFilename, fragFilename); - } else if (kind == FILL_SHADER_LIT) { - return new FillShaderLit(parent, vertFilename, fragFilename); - } else if (kind == FILL_SHADER_TEX) { - return new FillShaderTex(parent, vertFilename, fragFilename); - } else if (kind == FILL_SHADER_FULL) { - return new FillShaderFull(parent, vertFilename, fragFilename); - } else if (kind == LINE_SHADER) { + if (kind == FLAT_SHADER) { + return new PolyFlatShader(parent, vertFilename, fragFilename); + } else if (kind == LIGHT_SHADER) { + return new PolyLightShader(parent, vertFilename, fragFilename); + } else if (kind == TEXTURE_SHADER) { + return new PolyTexShader(parent, vertFilename, fragFilename); + } else if (kind == FULL_SHADER) { + return new PolyFullShader(parent, vertFilename, fragFilename); + } else if (kind == LINE3D_SHADER) { return new LineShader(parent, vertFilename, fragFilename); - } else if (kind == POINT_SHADER) { + } else if (kind == POINT3D_SHADER) { return new PointShader(parent, vertFilename, fragFilename); } else { PGraphics.showWarning("Wrong shader type"); @@ -5681,22 +5384,22 @@ public class PGraphicsOpenGL extends PGraphics { public PShader loadShader(String fragFilename, int kind) { PShader shader; - if (kind == FILL_SHADER_SIMPLE) { - shader = new FillShaderSimple(parent); - shader.setVertexShader(defFillShaderVertSimpleURL); - } else if (kind == FILL_SHADER_LIT) { - shader = new FillShaderLit(parent); - shader.setVertexShader(defFillShaderVertLitURL); - } else if (kind == FILL_SHADER_TEX) { - shader = new FillShaderTex(parent); - shader.setVertexShader(defFillShaderVertTexURL); - } else if (kind == FILL_SHADER_FULL) { - shader = new FillShaderFull(parent); - shader.setVertexShader(defFillShaderVertFullURL); - } else if (kind == LINE_SHADER) { + if (kind == FLAT_SHADER) { + shader = new PolyFlatShader(parent); + shader.setVertexShader(defPolyFlatShaderVertURL); + } else if (kind == LIGHT_SHADER) { + shader = new PolyLightShader(parent); + shader.setVertexShader(defPolyLightShaderVertURL); + } else if (kind == TEXTURE_SHADER) { + shader = new PolyTexShader(parent); + shader.setVertexShader(defPolyTexShaderVertURL); + } else if (kind == FULL_SHADER) { + shader = new PolyFullShader(parent); + shader.setVertexShader(defPolyFullShaderVertURL); + } else if (kind == LINE3D_SHADER) { shader = new LineShader(parent); shader.setVertexShader(defLineShaderVertURL); - } else if (kind == POINT_SHADER) { + } else if (kind == POINT3D_SHADER) { shader = new PointShader(parent); shader.setVertexShader(defPointShaderVertURL); } else { @@ -5709,17 +5412,17 @@ public class PGraphicsOpenGL extends PGraphics { public void setShader(PShader shader, int kind) { - if (kind == FILL_SHADER_SIMPLE) { - fillShaderSimple = (FillShaderSimple) shader; - } else if (kind == FILL_SHADER_LIT) { - fillShaderLit = (FillShaderLit) shader; - } else if (kind == FILL_SHADER_TEX) { - fillShaderTex = (FillShaderTex) shader; - } else if (kind == FILL_SHADER_FULL) { - fillShaderFull = (FillShaderFull) shader; - } else if (kind == LINE_SHADER) { + if (kind == FLAT_SHADER) { + polyFlatShader = (PolyFlatShader) shader; + } else if (kind == LIGHT_SHADER) { + polyLightShader = (PolyLightShader) shader; + } else if (kind == TEXTURE_SHADER) { + polyTexShader = (PolyTexShader) shader; + } else if (kind == FULL_SHADER) { + polyFullShader = (PolyFullShader) shader; + } else if (kind == LINE3D_SHADER) { lineShader = (LineShader) shader; - } else if (kind == POINT_SHADER) { + } else if (kind == POINT3D_SHADER) { pointShader = (PointShader) shader; } else { PGraphics.showWarning("Wrong shader type"); @@ -5727,33 +5430,33 @@ public class PGraphicsOpenGL extends PGraphics { } - public void resetShader(int kind) { - if (kind == FILL_SHADER_SIMPLE) { - if (defFillShaderSimple == null || defFillShaderSimple.contextIsOutdated()) { - defFillShaderSimple = new FillShaderSimple(parent, defFillShaderVertSimpleURL, defFillShaderFragNoTexURL); + public void defaultShader(int kind) { + if (kind == FLAT_SHADER) { + if (defPolyFlatShader == null || defPolyFlatShader.contextIsOutdated()) { + defPolyFlatShader = new PolyFlatShader(parent, defPolyFlatShaderVertURL, defPolyNoTexShaderFragURL); } - fillShaderSimple = defFillShaderSimple; - } else if (kind == FILL_SHADER_LIT) { - if (defFillShaderLit == null || defFillShaderLit.contextIsOutdated()) { - defFillShaderLit = new FillShaderLit(parent, defFillShaderVertLitURL, defFillShaderFragNoTexURL); + polyFlatShader = defPolyFlatShader; + } else if (kind == LIGHT_SHADER) { + if (defPolyLightShader == null || defPolyLightShader.contextIsOutdated()) { + defPolyLightShader = new PolyLightShader(parent, defPolyLightShaderVertURL, defPolyNoTexShaderFragURL); } - fillShaderLit = defFillShaderLit; - } else if (kind == FILL_SHADER_TEX) { - if (defFillShaderTex == null || defFillShaderTex.contextIsOutdated()) { - defFillShaderTex = new FillShaderTex(parent, defFillShaderVertTexURL, defFillShaderFragTexURL); + polyLightShader = defPolyLightShader; + } else if (kind == TEXTURE_SHADER) { + if (defPolyTexShader == null || defPolyTexShader.contextIsOutdated()) { + defPolyTexShader = new PolyTexShader(parent, defPolyTexShaderVertURL, defPolyTexShaderFragURL); } - fillShaderTex = defFillShaderTex; - } else if (kind == FILL_SHADER_FULL) { - if (defFillShaderFull == null || defFillShaderFull.contextIsOutdated()) { - defFillShaderFull = new FillShaderFull(parent, defFillShaderVertFullURL, defFillShaderFragTexURL); + polyTexShader = defPolyTexShader; + } else if (kind == FULL_SHADER) { + if (defPolyFullShader == null || defPolyFullShader.contextIsOutdated()) { + defPolyFullShader = new PolyFullShader(parent, defPolyFullShaderVertURL, defPolyTexShaderFragURL); } - fillShaderFull = defFillShaderFull; - } else if (kind == LINE_SHADER) { + polyFullShader = defPolyFullShader; + } else if (kind == LINE3D_SHADER) { if (defLineShader == null || defLineShader.contextIsOutdated()) { defLineShader = new LineShader(parent, defLineShaderVertURL, defLineShaderFragURL); } lineShader = defLineShader; - } else if (kind == POINT_SHADER) { + } else if (kind == POINT3D_SHADER) { if (defPointShader == null || defPointShader.contextIsOutdated()) { defPointShader = new PointShader(parent, defPointShaderVertURL, defPointShaderFragURL); } @@ -5764,43 +5467,43 @@ public class PGraphicsOpenGL extends PGraphics { } - protected FillShader getFillShader(boolean lit, boolean tex) { - FillShader shader; + protected PolyShader getPolyShader(boolean lit, boolean tex) { + PolyShader shader; if (lit) { if (tex) { - if (defFillShaderFull == null || defFillShaderFull.contextIsOutdated()) { - defFillShaderFull = new FillShaderFull(parent, defFillShaderVertFullURL, defFillShaderFragTexURL); + if (defPolyFullShader == null || defPolyFullShader.contextIsOutdated()) { + defPolyFullShader = new PolyFullShader(parent, defPolyFullShaderVertURL, defPolyTexShaderFragURL); } - if (fillShaderFull == null || fillShaderFull.contextIsOutdated()) { - fillShaderFull = defFillShaderFull; + if (polyFullShader == null || polyFullShader.contextIsOutdated()) { + polyFullShader = defPolyFullShader; } - shader = fillShaderFull; + shader = polyFullShader; } else { - if (defFillShaderLit == null || defFillShaderLit.contextIsOutdated()) { - defFillShaderLit = new FillShaderLit(parent, defFillShaderVertLitURL, defFillShaderFragNoTexURL); + if (defPolyLightShader == null || defPolyLightShader.contextIsOutdated()) { + defPolyLightShader = new PolyLightShader(parent, defPolyLightShaderVertURL, defPolyNoTexShaderFragURL); } - if (fillShaderLit == null || fillShaderLit.contextIsOutdated()) { - fillShaderLit = defFillShaderLit; + if (polyLightShader == null || polyLightShader.contextIsOutdated()) { + polyLightShader = defPolyLightShader; } - shader = fillShaderLit; + shader = polyLightShader; } } else { if (tex) { - if (defFillShaderTex == null || defFillShaderTex.contextIsOutdated()) { - defFillShaderTex = new FillShaderTex(parent, defFillShaderVertTexURL, defFillShaderFragTexURL); + if (defPolyTexShader == null || defPolyTexShader.contextIsOutdated()) { + defPolyTexShader = new PolyTexShader(parent, defPolyTexShaderVertURL, defPolyTexShaderFragURL); } - if (fillShaderTex == null || fillShaderTex.contextIsOutdated()) { - fillShaderTex = defFillShaderTex; + if (polyTexShader == null || polyTexShader.contextIsOutdated()) { + polyTexShader = defPolyTexShader; } - shader = fillShaderTex; + shader = polyTexShader; } else { - if (defFillShaderSimple == null || defFillShaderSimple.contextIsOutdated()) { - defFillShaderSimple = new FillShaderSimple(parent, defFillShaderVertSimpleURL, defFillShaderFragNoTexURL); + if (defPolyFlatShader == null || defPolyFlatShader.contextIsOutdated()) { + defPolyFlatShader = new PolyFlatShader(parent, defPolyFlatShaderVertURL, defPolyNoTexShaderFragURL); } - if (fillShaderSimple == null || fillShaderSimple.contextIsOutdated()) { - fillShaderSimple = defFillShaderSimple; + if (polyFlatShader == null || polyFlatShader.contextIsOutdated()) { + polyFlatShader = defPolyFlatShader; } - shader = fillShaderSimple; + shader = polyFlatShader; } } shader.setRenderer(this); @@ -5838,22 +5541,22 @@ public class PGraphicsOpenGL extends PGraphics { } - protected class FillShader extends PShader { + protected class PolyShader extends PShader { // We need a reference to the renderer since a shader might // be called by different renderers within a single application // (the one corresponding to the main surface, or other offscreen // renderers). protected PGraphicsOpenGL renderer; - public FillShader(PApplet parent) { + public PolyShader(PApplet parent) { super(parent); } - public FillShader(PApplet parent, String vertFilename, String fragFilename) { + public PolyShader(PApplet parent, String vertFilename, String fragFilename) { super(parent, vertFilename, fragFilename); } - public FillShader(PApplet parent, URL vertURL, URL fragURL) { + public PolyShader(PApplet parent, URL vertURL, URL fragURL) { super(parent, vertURL, fragURL); } @@ -5878,26 +5581,26 @@ public class PGraphicsOpenGL extends PGraphics { public void setSpecularAttribute(int vboId, int size, int type, int stride, int offset) { } public void setEmissiveAttribute(int vboId, int size, int type, int stride, int offset) { } public void setShininessAttribute(int vboId, int size, int type, int stride, int offset) { } - public void setTexCoordAttribute(int vboId, int size, int type, int stride, int offset) { } + public void setTexcoordAttribute(int vboId, int size, int type, int stride, int offset) { } public void setTexture(PTexture tex) { } } - protected class FillShaderSimple extends FillShader { + protected class PolyFlatShader extends PolyShader { protected int projmodelviewMatrixLoc; protected int inVertexLoc; protected int inColorLoc; - public FillShaderSimple(PApplet parent) { + public PolyFlatShader(PApplet parent) { super(parent); } - public FillShaderSimple(PApplet parent, String vertFilename, String fragFilename) { + public PolyFlatShader(PApplet parent, String vertFilename, String fragFilename) { super(parent, vertFilename, fragFilename); } - public FillShaderSimple(PApplet parent, URL vertURL, URL fragURL) { + public PolyFlatShader(PApplet parent, URL vertURL, URL fragURL) { super(parent, vertURL, fragURL); } @@ -5941,7 +5644,7 @@ public class PGraphicsOpenGL extends PGraphics { } - protected class FillShaderLit extends FillShader { + protected class PolyLightShader extends PolyShader { protected int projmodelviewMatrixLoc; protected int modelviewMatrixLoc; protected int normalMatrixLoc; @@ -5964,15 +5667,15 @@ public class PGraphicsOpenGL extends PGraphics { protected int inEmissiveLoc; protected int inShineLoc; - public FillShaderLit(PApplet parent) { + public PolyLightShader(PApplet parent) { super(parent); } - public FillShaderLit(PApplet parent, String vertFilename, String fragFilename) { + public PolyLightShader(PApplet parent, String vertFilename, String fragFilename) { super(parent, vertFilename, fragFilename); } - public FillShaderLit(PApplet parent, URL vertURL, URL fragURL) { + public PolyLightShader(PApplet parent, URL vertURL, URL fragURL) { super(parent, vertURL, fragURL); } @@ -6080,7 +5783,7 @@ public class PGraphicsOpenGL extends PGraphics { } - protected class FillShaderTex extends FillShaderSimple { + protected class PolyTexShader extends PolyFlatShader { protected int inTexcoordLoc; protected int texcoordMatrixLoc; @@ -6088,15 +5791,15 @@ public class PGraphicsOpenGL extends PGraphics { protected float[] tcmat; - public FillShaderTex(PApplet parent) { + public PolyTexShader(PApplet parent) { super(parent); } - public FillShaderTex(PApplet parent, String vertFilename, String fragFilename) { + public PolyTexShader(PApplet parent, String vertFilename, String fragFilename) { super(parent, vertFilename, fragFilename); } - public FillShaderTex(PApplet parent, URL vertURL, URL fragURL) { + public PolyTexShader(PApplet parent, URL vertURL, URL fragURL) { super(parent, vertURL, fragURL); } @@ -6113,30 +5816,30 @@ public class PGraphicsOpenGL extends PGraphics { inTexcoordLoc = getAttribLocation("inTexcoord"); } - public void setTexCoordAttribute(int vboId, int size, int type, int stride, int offset) { + public void setTexcoordAttribute(int vboId, int size, int type, int stride, int offset) { setAttribute(inTexcoordLoc, vboId, size, type, false, stride, offset); } public void setTexture(PTexture tex) { float scaleu = 1; float scalev = 1; - float dispu = 0; - float dispv = 0; + float dispu = 0; + float dispv = 0; if (tex.isFlippedX()) { scaleu = -1; - dispu = 1; + dispu = 1; } if (tex.isFlippedY()) { scalev = -1; - dispv = 1; + dispv = 1; } - scaleu *= tex.maxTexCoordU; - dispu *= tex.maxTexCoordU; - scalev *= tex.maxTexCoordV; - dispv *= tex.maxTexCoordV; + scaleu *= tex.maxTexcoordU; + dispu *= tex.maxTexcoordU; + scalev *= tex.maxTexcoordV; + dispv *= tex.maxTexcoordV; if (tcmat == null) { tcmat = new float[16]; @@ -6165,7 +5868,7 @@ public class PGraphicsOpenGL extends PGraphics { } - protected class FillShaderFull extends FillShaderLit { + protected class PolyFullShader extends PolyLightShader { protected int inTexcoordLoc; protected int texcoordMatrixLoc; @@ -6173,15 +5876,15 @@ public class PGraphicsOpenGL extends PGraphics { protected float[] tcmat; - public FillShaderFull(PApplet parent) { + public PolyFullShader(PApplet parent) { super(parent); } - public FillShaderFull(PApplet parent, String vertFilename, String fragFilename) { + public PolyFullShader(PApplet parent, String vertFilename, String fragFilename) { super(parent, vertFilename, fragFilename); } - public FillShaderFull(PApplet parent, URL vertURL, URL fragURL) { + public PolyFullShader(PApplet parent, URL vertURL, URL fragURL) { super(parent, vertURL, fragURL); } @@ -6198,30 +5901,30 @@ public class PGraphicsOpenGL extends PGraphics { inTexcoordLoc = getAttribLocation("inTexcoord"); } - public void setTexCoordAttribute(int vboId, int size, int type, int stride, int offset) { + public void setTexcoordAttribute(int vboId, int size, int type, int stride, int offset) { setAttribute(inTexcoordLoc, vboId, size, type, false, stride, offset); } public void setTexture(PTexture tex) { float scaleu = 1; float scalev = 1; - float dispu = 0; - float dispv = 0; + float dispu = 0; + float dispv = 0; if (tex.isFlippedX()) { scaleu = -1; - dispu = 1; + dispu = 1; } if (tex.isFlippedY()) { scalev = -1; - dispv = 1; + dispv = 1; } - scaleu *= tex.maxTexCoordU; - dispu *= tex.maxTexCoordU; - scalev *= tex.maxTexCoordV; - dispv *= tex.maxTexCoordV; + scaleu *= tex.maxTexcoordU; + dispu *= tex.maxTexcoordU; + scalev *= tex.maxTexcoordV; + dispv *= tex.maxTexcoordV; if (tcmat == null) { tcmat = new float[16]; @@ -6261,7 +5964,7 @@ public class PGraphicsOpenGL extends PGraphics { protected int inVertexLoc; protected int inColorLoc; - protected int inDirWidthLoc; + protected int inAttribLoc; public LineShader(PApplet parent) { super(parent); @@ -6282,7 +5985,7 @@ public class PGraphicsOpenGL extends PGraphics { public void loadAttributes() { inVertexLoc = getAttribLocation("inVertex"); inColorLoc = getAttribLocation("inColor"); - inDirWidthLoc = getAttribLocation("inDirWidth"); + inAttribLoc = getAttribLocation("inLine"); } public void loadUniforms() { @@ -6306,16 +6009,16 @@ public class PGraphicsOpenGL extends PGraphics { setAttribute(inColorLoc, vboId, size, type, true, stride, offset); } - public void setDirWidthAttribute(int vboId, int size, int type, int stride, int offset) { - setAttribute(inDirWidthLoc, vboId, size, type, false, stride, offset); + public void setLineAttribute(int vboId, int size, int type, int stride, int offset) { + setAttribute(inAttribLoc, vboId, size, type, false, stride, offset); } public void start() { super.start(); - if (-1 < inVertexLoc) pgl.glEnableVertexAttribArray(inVertexLoc); - if (-1 < inColorLoc) pgl.glEnableVertexAttribArray(inColorLoc); - if (-1 < inDirWidthLoc) pgl.glEnableVertexAttribArray(inDirWidthLoc); + if (-1 < inVertexLoc) pgl.glEnableVertexAttribArray(inVertexLoc); + if (-1 < inColorLoc) pgl.glEnableVertexAttribArray(inColorLoc); + if (-1 < inAttribLoc) pgl.glEnableVertexAttribArray(inAttribLoc); if (renderer != null) { renderer.updateGLProjection(); @@ -6331,9 +6034,9 @@ public class PGraphicsOpenGL extends PGraphics { } public void stop() { - if (-1 < inVertexLoc) pgl.glDisableVertexAttribArray(inVertexLoc); - if (-1 < inColorLoc) pgl.glDisableVertexAttribArray(inColorLoc); - if (-1 < inDirWidthLoc) pgl.glDisableVertexAttribArray(inDirWidthLoc); + if (-1 < inVertexLoc) pgl.glDisableVertexAttribArray(inVertexLoc); + if (-1 < inColorLoc) pgl.glDisableVertexAttribArray(inColorLoc); + if (-1 < inAttribLoc) pgl.glDisableVertexAttribArray(inAttribLoc); pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); @@ -6350,7 +6053,7 @@ public class PGraphicsOpenGL extends PGraphics { protected int inVertexLoc; protected int inColorLoc; - protected int inSizeLoc; + protected int inPointLoc; public PointShader(PApplet parent) { super(parent); @@ -6371,7 +6074,7 @@ public class PGraphicsOpenGL extends PGraphics { public void loadAttributes() { inVertexLoc = getAttribLocation("inVertex"); inColorLoc = getAttribLocation("inColor"); - inSizeLoc = getAttribLocation("inSize"); + inPointLoc = getAttribLocation("inPoint"); } public void loadUniforms() { @@ -6392,8 +6095,8 @@ public class PGraphicsOpenGL extends PGraphics { setAttribute(inColorLoc, vboId, size, type, true, stride, offset); } - public void setSizeAttribute(int vboId, int size, int type, int stride, int offset) { - setAttribute(inSizeLoc, vboId, size, type, false, stride, offset); + public void setPointAttribute(int vboId, int size, int type, int stride, int offset) { + setAttribute(inPointLoc, vboId, size, type, false, stride, offset); } public void start() { @@ -6401,7 +6104,7 @@ public class PGraphicsOpenGL extends PGraphics { if (-1 < inVertexLoc) pgl.glEnableVertexAttribArray(inVertexLoc); if (-1 < inColorLoc) pgl.glEnableVertexAttribArray(inColorLoc); - if (-1 < inSizeLoc) pgl.glEnableVertexAttribArray(inSizeLoc); + if (-1 < inPointLoc) pgl.glEnableVertexAttribArray(inPointLoc); if (renderer != null) { renderer.updateGLProjection(); @@ -6415,7 +6118,7 @@ public class PGraphicsOpenGL extends PGraphics { public void stop() { if (-1 < inVertexLoc) pgl.glDisableVertexAttribArray(inVertexLoc); if (-1 < inColorLoc) pgl.glDisableVertexAttribArray(inColorLoc); - if (-1 < inSizeLoc) pgl.glDisableVertexAttribArray(inSizeLoc); + if (-1 < inPointLoc) pgl.glDisableVertexAttribArray(inPointLoc); pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); @@ -6464,7 +6167,7 @@ public class PGraphicsOpenGL extends PGraphics { int[] lastIndex; int[] firstCache; int[] lastCache; - boolean haveTexture; + boolean hasTexture; PTexture tex0; TexCache() { @@ -6478,13 +6181,13 @@ public class PGraphicsOpenGL extends PGraphics { firstCache = new int[PGL.DEFAULT_IN_TEXTURES]; lastCache = new int[PGL.DEFAULT_IN_TEXTURES]; size = 0; - haveTexture = false; + hasTexture = false; } void clear() { java.util.Arrays.fill(textures, 0, size, null); size = 0; - haveTexture = false; + hasTexture = false; } void dispose() { @@ -6508,7 +6211,7 @@ public class PGraphicsOpenGL extends PGraphics { PTexture tex = null; if (img != null) { - tex = pg.getTexture(img); + tex = pgPrimary.getTexture(img); if (tex != null) { tex.bind(); tex0 = tex; @@ -6523,23 +6226,23 @@ public class PGraphicsOpenGL extends PGraphics { } void endRender() { - if (haveTexture) { + if (hasTexture) { // Unbinding all the textures in the cache. for (int i = 0; i < size; i++) { PImage img = textures[i]; if (img != null) { - PTexture tex = pg.getTexture(img); + PTexture tex = pgPrimary.getTexture(img); if (tex != null) { tex.unbind(); } } - } + } // Disabling texturing for each of the targets used // by textures in the cache. for (int i = 0; i < size; i++) { PImage img = textures[i]; if (img != null) { - PTexture tex = pg.getTexture(img); + PTexture tex = pgPrimary.getTexture(img); if (tex != null) { pgl.disableTexturing(tex.glTarget); } @@ -6558,7 +6261,7 @@ public class PGraphicsOpenGL extends PGraphics { lastCache[size] = lastb; // At least one non-null texture since last reset. - haveTexture |= img != null; + hasTexture |= img != null; size++; } @@ -6716,162 +6419,7 @@ public class PGraphicsOpenGL extends PGraphics { vertexOffset = temp; } } - - // This class allows to define a multi-valued mapping - // from input to tessellated vertices. - protected class TessMap { - InGeometry in; - TessGeometry tess; - - int[][] pointIndices; - int firstPointIndex; - int[][] lineIndices0; - int[][] lineIndices1; - int firstLineIndex; - int[][] fillIndices; - float[][] fillWeights; - int firstFillIndex; - - TessMap() { - in = null; - tess = null; - pointIndices = null; - lineIndices0 = null; - lineIndices1 = null; - fillIndices = null; - fillWeights = null; - firstPointIndex = -1; - firstLineIndex = -1; - firstFillIndex = -1; - } - - void init(InGeometry in, TessGeometry tess) { - this.in = in; - this.tess = tess; - pointIndices = new int[in.vertexCount][0]; - lineIndices0 = new int[in.vertexCount][0]; - lineIndices1 = new int[in.vertexCount][0]; - fillIndices = new int[in.vertexCount][0]; - fillWeights = new float[in.vertexCount][0]; - } - - void dispose() { - in = null; - tess = null; - pointIndices = null; - lineIndices0 = null; - lineIndices1 = null; - fillIndices = null; - fillWeights = null; - } - - void compact() { - firstPointIndex = -1; - firstLineIndex = -1; - - // When the in and tess indices are in a 1-1 mapping, there - // is no need to define it using the indices and weights arrays. - // Only the first offset between the two is needed. - boolean contiguous = true; - for (int i = in.firstVertex; i <= in.lastVertex; i++) { - int[] indices = fillIndices[i]; - float[] weigths = fillWeights[i]; - if (indices.length == 1 && weigths[0] == 1) { - if (i < in.lastVertex) { - int[] indices1 = fillIndices[i + 1]; - if (indices[0] + 1 != indices1[0]) { - contiguous = false; - break; - } - } - } else { - contiguous = false; - break; - } - } - if (contiguous) { - firstFillIndex = 0 < fillIndices.length ? fillIndices[in.firstVertex][0] : 0; - fillIndices = null; - fillWeights = null; - } else { - firstFillIndex = -1; - } - } - - void addPointIndex(int inIdx, int tessIdx) { - int[] indices = pointIndices[inIdx]; - int pos; - if (indices.length == 0) { - indices = new int[1]; - pos = 0; - } else { - int len = indices.length; - indices = new int[len + 1]; - PApplet.arrayCopy(pointIndices[inIdx], indices, len); - pos = len; - } - indices[pos] = tessIdx; - pointIndices[inIdx] = indices; - } - - void addLineIndex0(int inIdx, int tessIdx) { - int[] indices = lineIndices0[inIdx]; - int pos; - if (indices.length == 0) { - indices = new int[1]; - pos = 0; - } else { - int len = indices.length; - indices = new int[len + 1]; - PApplet.arrayCopy(lineIndices0[inIdx], indices, len); - pos = len; - } - indices[pos] = tessIdx; - lineIndices0[inIdx] = indices; - } - - void addLineIndex1(int inIdx, int tessIdx) { - int[] indices = lineIndices1[inIdx]; - int pos; - if (indices.length == 0) { - indices = new int[1]; - pos = 0; - } else { - int len = indices.length; - indices = new int[len + 1]; - PApplet.arrayCopy(lineIndices1[inIdx], indices, len); - pos = len; - } - indices[pos] = tessIdx; - lineIndices1[inIdx] = indices; - } - - void addFillIndex(int inIdx, int tessIdx, float weight) { - int[] indices = fillIndices[inIdx]; - float[] weights = fillWeights[inIdx]; - int pos; - if (indices.length == 0) { - indices = new int[1]; - weights = new float[1]; - pos = 0; - } else { - int len = indices.length; - indices = new int[len + 1]; - weights = new float[len + 1]; - PApplet.arrayCopy(fillIndices[inIdx], indices, len); - PApplet.arrayCopy(fillWeights[inIdx], weights, len); - pos = len; - } - indices[pos] = tessIdx; - weights[pos] = weight; - fillIndices[inIdx] = indices; - fillWeights[inIdx] = weights; - } - - void addFillIndex(int inIdx, int tessIdx) { - addFillIndex(inIdx, tessIdx, 1); - } - } + // Holds the input vertices: xyz coordinates, fill/tint color, // normal, texture coordinates and stroke color and weight. @@ -6896,8 +6444,8 @@ public class PGraphicsOpenGL extends PGraphics { int[] colors; float[] normals; float[] texcoords; - int[] scolors; - float[] sweights; + int[] strokeColors; + float[] strokeWeights; // lines boolean[] breaks; @@ -6918,8 +6466,6 @@ public class PGraphicsOpenGL extends PGraphics { int emissiveColor; float shininessFactor; float normalX, normalY, normalZ; - - TessMap tessMap; InGeometry(int mode) { renderMode = mode; @@ -6940,12 +6486,12 @@ public class PGraphicsOpenGL extends PGraphics { } void allocate() { - vertices = new float[4 * PGL.DEFAULT_IN_VERTICES]; + vertices = new float[3 * PGL.DEFAULT_IN_VERTICES]; colors = new int[PGL.DEFAULT_IN_VERTICES]; normals = new float[3 * PGL.DEFAULT_IN_VERTICES]; texcoords = new float[2 * PGL.DEFAULT_IN_VERTICES]; - scolors = new int[PGL.DEFAULT_IN_VERTICES]; - sweights = new float[PGL.DEFAULT_IN_VERTICES]; + strokeColors = new int[PGL.DEFAULT_IN_VERTICES]; + strokeWeights = new float[PGL.DEFAULT_IN_VERTICES]; ambient = new int[PGL.DEFAULT_IN_VERTICES]; specular = new int[PGL.DEFAULT_IN_VERTICES]; emissive = new int[PGL.DEFAULT_IN_VERTICES]; @@ -6953,10 +6499,6 @@ public class PGraphicsOpenGL extends PGraphics { breaks = new boolean[PGL.DEFAULT_IN_VERTICES]; edges = new int[PGL.DEFAULT_IN_EDGES][3]; - if (renderMode == RETAINED) { - tessMap = new TessMap(); - } - clear(); } @@ -6966,21 +6508,17 @@ public class PGraphicsOpenGL extends PGraphics { colors = null; normals = null; texcoords = null; - scolors = null; - sweights = null; + strokeColors = null; + strokeWeights = null; ambient = null; specular = null; emissive = null; shininess = null; edges = null; - - if (renderMode == RETAINED) { - tessMap.dispose(); - } } void vertexCheck() { - if (vertexCount == vertices.length / 4) { + if (vertexCount == vertices.length / 3) { int newSize = vertexCount << 1; expandVertices(newSize); @@ -7004,85 +6542,61 @@ public class PGraphicsOpenGL extends PGraphics { expandEdges(newLen); } } - - - // ----------------------------------------------------------------- - // - // Tess mapping - - void initTessMap(TessGeometry tess) { - if (renderMode == RETAINED) { - tessMap.init(this, tess); - } - } - - void disposeTessMap() { - tessMap.dispose(); - } - - void compactTessMap() { - tessMap.compact(); - } - - /* - // The new API to implement later... - void addPointMapping(int firstIn, int lastIn, int firstTess, int pointSize) { - } - - void addLineMapping(int firstIn, int lastIn) { - } - - void setLineMapping(int firstIn, int lastIn, int firstTess) { - } - - void addLinearMapping(int firstIn, int lastIn, int firstTess) { -// for (int i = 0; i < nvert; i++) { -// [i0, i1] -> map = (i - i0) + firstFillVertex; -// } - } - - void addWeightedMapping(int firstIn, int lastIn) { - } - - void setWeightedMapping(int[] inIdxs, float[] inWeights, int tessIdx) { - //if vertices != null && weights != null - } - */ - + // ----------------------------------------------------------------- // // Query float getVertexX(int idx) { - return vertices[4 * idx + 0]; + return vertices[3 * idx + 0]; } float getVertexY(int idx) { - return vertices[4 * idx + 1]; + return vertices[3 * idx + 1]; } float getVertexZ(int idx) { - return vertices[4 * idx + 2]; + return vertices[3 * idx + 2]; } float getLastVertexX() { - return vertices[4 * (vertexCount - 1) + 0]; + return vertices[3 * (vertexCount - 1) + 0]; } float getLastVertexY() { - return vertices[4 * (vertexCount - 1) + 1]; + return vertices[3 * (vertexCount - 1) + 1]; } float getLastVertexZ() { - return vertices[4 * (vertexCount - 1) + 2]; + return vertices[3 * (vertexCount - 1) + 2]; } - int getNumLineVertices() { - return 4 * (lastEdge - firstEdge + 1); + int getNumEdgeVertices(boolean bevel) { + int segVert = 4 * (lastEdge - firstEdge + 1); + int bevVert = 0; + if (bevel) { + for (int i = firstEdge; i <= lastEdge; i++) { + int[] edge = edges[i]; + if (edge[2] == EDGE_MIDDLE || edge[2] == EDGE_START) { + bevVert++; + } + } + } + return segVert + bevVert; } - int getNumLineIndices() { - return 6 * (lastEdge - firstEdge + 1); + int getNumEdgeIndices(boolean bevel) { + int segInd = 6 * (lastEdge - firstEdge + 1); + int bevInd = 0; + if (bevel) { + for (int i = firstEdge; i <= lastEdge; i++) { + int[] edge = edges[i]; + if (edge[2] == EDGE_MIDDLE || edge[2] == EDGE_START) { + bevInd += 6; + } + } + } + return segInd + bevInd; } void getVertexMin(PVector v) { @@ -7121,8 +6635,8 @@ public class PGraphicsOpenGL extends PGraphics { // Expand arrays void expandVertices(int n) { - float temp[] = new float[4 * n]; - PApplet.arrayCopy(vertices, 0, temp, 0, 4 * vertexCount); + float temp[] = new float[3 * n]; + PApplet.arrayCopy(vertices, 0, temp, 0, 3 * vertexCount); vertices = temp; } @@ -7146,14 +6660,14 @@ public class PGraphicsOpenGL extends PGraphics { void expandStrokeColors(int n) { int temp[] = new int[n]; - PApplet.arrayCopy(scolors, 0, temp, 0, vertexCount); - scolors = temp; + PApplet.arrayCopy(strokeColors, 0, temp, 0, vertexCount); + strokeColors = temp; } void expandStrokeWeights(int n) { float temp[] = new float[n]; - PApplet.arrayCopy(sweights, 0, temp, 0, vertexCount); - sweights = temp; + PApplet.arrayCopy(strokeWeights, 0, temp, 0, vertexCount); + strokeWeights = temp; } void expandAmbient(int n) { @@ -7197,7 +6711,7 @@ public class PGraphicsOpenGL extends PGraphics { // Trim arrays void trim() { - if (0 < vertexCount && vertexCount < vertices.length / 4) { + if (0 < vertexCount && vertexCount < vertices.length / 3) { trimVertices(); trimColors(); trimNormals(); @@ -7217,8 +6731,8 @@ public class PGraphicsOpenGL extends PGraphics { } void trimVertices() { - float temp[] = new float[4 * vertexCount]; - PApplet.arrayCopy(vertices, 0, temp, 0, 4 * vertexCount); + float temp[] = new float[3 * vertexCount]; + PApplet.arrayCopy(vertices, 0, temp, 0, 3 * vertexCount); vertices = temp; } @@ -7242,14 +6756,14 @@ public class PGraphicsOpenGL extends PGraphics { void trimStrokeColors() { int temp[] = new int[vertexCount]; - PApplet.arrayCopy(scolors, 0, temp, 0, vertexCount); - scolors = temp; + PApplet.arrayCopy(strokeColors, 0, temp, 0, vertexCount); + strokeColors = temp; } void trimStrokeWeights() { float temp[] = new float[vertexCount]; - PApplet.arrayCopy(sweights, 0, temp, 0, vertexCount); - sweights = temp; + PApplet.arrayCopy(strokeWeights, 0, temp, 0, vertexCount); + strokeWeights = temp; } void trimAmbient() { @@ -7350,11 +6864,10 @@ public class PGraphicsOpenGL extends PGraphics { curveVertexCount = 0; - index = 4 * vertexCount; + index = 3 * vertexCount; vertices[index++] = x; vertices[index++] = y; - vertices[index++] = z; - vertices[index ] = 1; + vertices[index ] = z; colors[vertexCount] = PGL.javaToNativeARGB(fcolor); @@ -7367,8 +6880,8 @@ public class PGraphicsOpenGL extends PGraphics { texcoords[index++] = u; texcoords[index ] = v; - scolors[vertexCount] = PGL.javaToNativeARGB(scolor); - sweights[vertexCount] = sweight; + strokeColors[vertexCount] = PGL.javaToNativeARGB(scolor); + strokeWeights[vertexCount] = sweight; ambient[vertexCount] = PGL.javaToNativeARGB(am); specular[vertexCount] = PGL.javaToNativeARGB(sp); @@ -7631,7 +7144,8 @@ public class PGraphicsOpenGL extends PGraphics { int i1 = first + ln + 1; if (breaks[i0]) contour0 = i0; if (i1 == lnMax || breaks[i1]) { - // We are at the end of a contour. + // We are either at the end of a contour or at the end of the + // edge path. if (closed) { // Draw line to the first vertex of the current contour, // if the polygon is closed. @@ -7644,7 +7158,8 @@ public class PGraphicsOpenGL extends PGraphics { // We might start a new contour in the next iteration. begin = true; } else if (!breaks[i1]) { - addEdge(i0, i1, begin, false); + boolean end = i1 + 1 < lnMax && breaks[i1 + 1]; + addEdge(i0, i1, begin, end); begin = false; } } @@ -7658,17 +7173,17 @@ public class PGraphicsOpenGL extends PGraphics { void calcTriangleNormal(int i0, int i1, int i2) { int index; - index = 4 * i0; + index = 3 * i0; float x0 = vertices[index++]; float y0 = vertices[index++]; float z0 = vertices[index ]; - index = 4 * i1; + index = 3 * i1; float x1 = vertices[index++]; float y1 = vertices[index++]; float z1 = vertices[index ]; - index = 4 * i2; + index = 3 * i2; float x2 = vertices[index++]; float y2 = vertices[index++]; float z2 = vertices[index ]; @@ -7916,28 +7431,28 @@ public class PGraphicsOpenGL extends PGraphics { if (br > maxRounding) br = maxRounding; if (bl > maxRounding) bl = maxRounding; - if (tr != 0) { + if (nonZero(tr)) { addVertex(c-tr, b, VERTEX); addQuadraticVertex(c, b, 0, c, b+tr, 0, fill, stroke, detail, VERTEX); } else { addVertex(c, b, VERTEX); } - if (br != 0) { + if (nonZero(br)) { addVertex(c, d-br, VERTEX); addQuadraticVertex(c, d, 0, c-br, d, 0, fill, stroke, detail, VERTEX); } else { addVertex(c, d, VERTEX); } - if (bl != 0) { + if (nonZero(bl)) { addVertex(a+bl, d, VERTEX); addQuadraticVertex(a, d, 0, a, d-bl, 0, fill, stroke, detail, VERTEX); } else { addVertex(a, d, VERTEX); } - if (tl != 0) { + if (nonZero(tl)) { addVertex(a, b+tl, VERTEX); addQuadraticVertex(a, b, 0, a+tl, b, 0, fill, stroke, detail, VERTEX); @@ -7986,12 +7501,14 @@ public class PGraphicsOpenGL extends PGraphics { float centerX = x + radiusH; float centerY = y + radiusV; - float sx1 = screenX(x, y); - float sy1 = screenY(x, y); - float sx2 = screenX(x + w, y + h); - float sy2 = screenY(x + w, y + h); - - int accuracy = PApplet.max(MIN_POINT_ACCURACY, (int) (TWO_PI * PApplet.dist(sx1, sy1, sx2, sy2) / 20)); + // should call screenX/Y using current renderer. + float sx1 = pgCurrent.screenX(x, y); + float sy1 = pgCurrent.screenY(x, y); + float sx2 = pgCurrent.screenX(x + w, y + h); + float sy2 = pgCurrent.screenY(x + w, y + h); + + int accuracy = PApplet.max(MIN_POINT_ACCURACY, + (int) (TWO_PI * PApplet.dist(sx1, sy1, sx2, sy2) / POINT_ACCURACY_FACTOR)); float inc = (float) PGraphicsOpenGL.SINCOS_LENGTH / accuracy; if (fill) { @@ -8301,31 +7818,31 @@ public class PGraphicsOpenGL extends PGraphics { } - // Holds tessellated data for fill, line and point geometry. + // Holds tessellated data for polygon, line and point geometry. protected class TessGeometry { int renderMode; - // Tessellated fill data - int fillVertexCount; - int firstFillVertex; - int lastFillVertex; - float[] fillVertices; - int[] fillColors; - float[] fillNormals; - float[] fillTexcoords; + // Tessellated polygon data + int polyVertexCount; + int firstPolyVertex; + int lastPolyVertex; + float[] polyVertices; + int[] polyColors; + float[] polyNormals; + float[] polyTexcoords; - // Fill material properties (fillColor is used + // Polygon material properties (polyColors is used // as the diffuse color when lighting is enabled) - int[] fillAmbient; - int[] fillSpecular; - int[] fillEmissive; - float[] fillShininess; - - int fillIndexCount; - int firstFillIndex; - int lastFillIndex; - short[] fillIndices; - IndexCache fillIndexCache = new IndexCache(); + int[] polyAmbient; + int[] polySpecular; + int[] polyEmissive; + float[] polyShininess; + + int polyIndexCount; + int firstPolyIndex; + int lastPolyIndex; + short[] polyIndices; + IndexCache polyIndexCache = new IndexCache(); // Tessellated line data int lineVertexCount; @@ -8333,7 +7850,7 @@ public class PGraphicsOpenGL extends PGraphics { int lastLineVertex; float[] lineVertices; int[] lineColors; - float[] lineDirWidths; + float[] lineAttribs; int lineIndexCount; int firstLineIndex; @@ -8347,7 +7864,7 @@ public class PGraphicsOpenGL extends PGraphics { int lastPointVertex; float[] pointVertices; int[] pointColors; - float[] pointSizes; + float[] pointAttribs; int pointIndexCount; int firstPointIndex; @@ -8365,32 +7882,32 @@ public class PGraphicsOpenGL extends PGraphics { // Allocate/dispose void allocate() { - fillVertices = new float[4 * PGL.DEFAULT_TESS_VERTICES]; - fillColors = new int[PGL.DEFAULT_TESS_VERTICES]; - fillNormals = new float[3 * PGL.DEFAULT_TESS_VERTICES]; - fillTexcoords = new float[2 * PGL.DEFAULT_TESS_VERTICES]; - fillAmbient = new int[PGL.DEFAULT_TESS_VERTICES]; - fillSpecular = new int[PGL.DEFAULT_TESS_VERTICES]; - fillEmissive = new int[PGL.DEFAULT_TESS_VERTICES]; - fillShininess = new float[PGL.DEFAULT_TESS_VERTICES]; - fillIndices = new short[PGL.DEFAULT_TESS_VERTICES]; + polyVertices = new float[4 * PGL.DEFAULT_TESS_VERTICES]; + polyColors = new int[PGL.DEFAULT_TESS_VERTICES]; + polyNormals = new float[3 * PGL.DEFAULT_TESS_VERTICES]; + polyTexcoords = new float[2 * PGL.DEFAULT_TESS_VERTICES]; + polyAmbient = new int[PGL.DEFAULT_TESS_VERTICES]; + polySpecular = new int[PGL.DEFAULT_TESS_VERTICES]; + polyEmissive = new int[PGL.DEFAULT_TESS_VERTICES]; + polyShininess = new float[PGL.DEFAULT_TESS_VERTICES]; + polyIndices = new short[PGL.DEFAULT_TESS_VERTICES]; lineVertices = new float[4 * PGL.DEFAULT_TESS_VERTICES]; lineColors = new int[PGL.DEFAULT_TESS_VERTICES]; - lineDirWidths = new float[4 * PGL.DEFAULT_TESS_VERTICES]; + lineAttribs = new float[4 * PGL.DEFAULT_TESS_VERTICES]; lineIndices = new short[PGL.DEFAULT_TESS_VERTICES]; pointVertices = new float[4 * PGL.DEFAULT_TESS_VERTICES]; pointColors = new int[PGL.DEFAULT_TESS_VERTICES]; - pointSizes = new float[2 * PGL.DEFAULT_TESS_VERTICES]; + pointAttribs = new float[2 * PGL.DEFAULT_TESS_VERTICES]; pointIndices = new short[PGL.DEFAULT_TESS_VERTICES]; clear(); } void clear() { - firstFillVertex = lastFillVertex = fillVertexCount = 0; - firstFillIndex = lastFillIndex = fillIndexCount = 0; + firstPolyVertex = lastPolyVertex = polyVertexCount = 0; + firstPolyIndex = lastPolyIndex = polyIndexCount = 0; firstLineVertex = lastLineVertex = lineVertexCount = 0; firstLineIndex = lastLineIndex = lineIndexCount = 0; @@ -8398,94 +7915,94 @@ public class PGraphicsOpenGL extends PGraphics { firstPointVertex = lastPointVertex = pointVertexCount = 0; firstPointIndex = lastPointIndex = pointIndexCount = 0; - fillIndexCache.clear(); + polyIndexCache.clear(); lineIndexCache.clear(); pointIndexCache.clear(); } void dipose() { - fillVertices = null; - fillColors = null; - fillNormals = null; - fillTexcoords = null; - fillAmbient = null; - fillSpecular = null; - fillEmissive = null; - fillShininess = null; - fillIndices = null; + polyVertices = null; + polyColors = null; + polyNormals = null; + polyTexcoords = null; + polyAmbient = null; + polySpecular = null; + polyEmissive = null; + polyShininess = null; + polyIndices = null; lineVertices = null; lineColors = null; - lineDirWidths = null; + lineAttribs = null; lineIndices = null; pointVertices = null; pointColors = null; - pointSizes = null; + pointAttribs = null; pointIndices = null; } - void fillVertexCheck() { - if (fillVertexCount == fillVertices.length / 4) { - int newSize = fillVertexCount << 1; + void polyVertexCheck() { + if (polyVertexCount == polyVertices.length / 4) { + int newSize = polyVertexCount << 1; - expandFillVertices(newSize); - expandFillColors(newSize); - expandFillNormals(newSize); - expandFillTexcoords(newSize); - expandFillAmbient(newSize); - expandFillSpecular(newSize); - expandFillEmissive(newSize); - expandFillShininess(newSize); + expandPolyVertices(newSize); + expandPolyColors(newSize); + expandPolyNormals(newSize); + expandPolyTexcoords(newSize); + expandPolyAmbient(newSize); + expandPolySpecular(newSize); + expandPolyEmissive(newSize); + expandPolyShininess(newSize); } - firstFillVertex = fillVertexCount; - fillVertexCount++; - lastFillVertex = fillVertexCount - 1; + firstPolyVertex = polyVertexCount; + polyVertexCount++; + lastPolyVertex = polyVertexCount - 1; } - void fillVertexCheck(int count) { - int oldSize = fillVertices.length / 4; - if (fillVertexCount + count > oldSize) { - int newSize = expandArraySize(oldSize, fillVertexCount + count); + void polyVertexCheck(int count) { + int oldSize = polyVertices.length / 4; + if (polyVertexCount + count > oldSize) { + int newSize = expandArraySize(oldSize, polyVertexCount + count); - expandFillVertices(newSize); - expandFillColors(newSize); - expandFillNormals(newSize); - expandFillTexcoords(newSize); - expandFillAmbient(newSize); - expandFillSpecular(newSize); - expandFillEmissive(newSize); - expandFillShininess(newSize); + expandPolyVertices(newSize); + expandPolyColors(newSize); + expandPolyNormals(newSize); + expandPolyTexcoords(newSize); + expandPolyAmbient(newSize); + expandPolySpecular(newSize); + expandPolyEmissive(newSize); + expandPolyShininess(newSize); } - firstFillVertex = fillVertexCount; - fillVertexCount += count; - lastFillVertex = fillVertexCount - 1; + firstPolyVertex = polyVertexCount; + polyVertexCount += count; + lastPolyVertex = polyVertexCount - 1; } - void fillIndexCheck(int count) { - int oldSize = fillIndices.length; - if (fillIndexCount + count > oldSize) { - int newSize = expandArraySize(oldSize, fillIndexCount + count); + void polyIndexCheck(int count) { + int oldSize = polyIndices.length; + if (polyIndexCount + count > oldSize) { + int newSize = expandArraySize(oldSize, polyIndexCount + count); - expandFillIndices(newSize); + expandPolyIndices(newSize); } - firstFillIndex = fillIndexCount; - fillIndexCount += count; - lastFillIndex = fillIndexCount - 1; + firstPolyIndex = polyIndexCount; + polyIndexCount += count; + lastPolyIndex = polyIndexCount - 1; } - void fillIndexCheck() { - if (fillIndexCount == fillIndices.length) { - int newSize = fillIndexCount << 1; - expandFillIndices(newSize); + void polyIndexCheck() { + if (polyIndexCount == polyIndices.length) { + int newSize = polyIndexCount << 1; + expandPolyIndices(newSize); } - firstFillIndex = fillIndexCount; - fillIndexCount++; - lastFillIndex = fillIndexCount - 1; + firstPolyIndex = polyIndexCount; + polyIndexCount++; + lastPolyIndex = polyIndexCount - 1; } void lineVertexCheck(int count) { @@ -8495,7 +8012,7 @@ public class PGraphicsOpenGL extends PGraphics { expandLineVertices(newSize); expandLineColors(newSize); - expandLineAttributes(newSize); + expandLineAttribs(newSize); } firstLineVertex = lineVertexCount; @@ -8523,7 +8040,7 @@ public class PGraphicsOpenGL extends PGraphics { expandPointVertices(newSize); expandPointColors(newSize); - expandPointAttributes(newSize); + expandPointAttribs(newSize); } firstPointVertex = pointVertexCount; @@ -8549,17 +8066,17 @@ public class PGraphicsOpenGL extends PGraphics { // Query boolean isFull() { - return PGL.FLUSH_VERTEX_COUNT <= fillVertexCount || + return PGL.FLUSH_VERTEX_COUNT <= polyVertexCount || PGL.FLUSH_VERTEX_COUNT <= lineVertexCount || PGL.FLUSH_VERTEX_COUNT <= pointVertexCount; } - void getFillVertexMin(PVector v, int first, int last) { + void getPolyVertexMin(PVector v, int first, int last) { for (int i = first; i <= last; i++) { int index = 4 * i; - v.x = PApplet.min(v.x, fillVertices[index++]); - v.y = PApplet.min(v.y, fillVertices[index++]); - v.z = PApplet.min(v.z, fillVertices[index ]); + v.x = PApplet.min(v.x, polyVertices[index++]); + v.y = PApplet.min(v.y, polyVertices[index++]); + v.z = PApplet.min(v.z, polyVertices[index ]); } } @@ -8581,12 +8098,12 @@ public class PGraphicsOpenGL extends PGraphics { } } - void getFillVertexMax(PVector v, int first, int last) { + void getPolyVertexMax(PVector v, int first, int last) { for (int i = first; i <= last; i++) { int index = 4 * i; - v.x = PApplet.max(v.x, fillVertices[index++]); - v.y = PApplet.max(v.y, fillVertices[index++]); - v.z = PApplet.max(v.z, fillVertices[index ]); + v.x = PApplet.max(v.x, polyVertices[index++]); + v.y = PApplet.max(v.y, polyVertices[index++]); + v.z = PApplet.max(v.z, polyVertices[index ]); } } @@ -8608,12 +8125,12 @@ public class PGraphicsOpenGL extends PGraphics { } } - int getFillVertexSum(PVector v, int first, int last) { + int getPolyVertexSum(PVector v, int first, int last) { for (int i = first; i <= last; i++) { int index = 4 * i; - v.x += fillVertices[index++]; - v.y += fillVertices[index++]; - v.z += fillVertices[index ]; + v.x += polyVertices[index++]; + v.y += polyVertices[index++]; + v.z += polyVertices[index ]; } return last - first + 1; } @@ -8642,58 +8159,58 @@ public class PGraphicsOpenGL extends PGraphics { // // Expand arrays - void expandFillVertices(int n) { + void expandPolyVertices(int n) { float temp[] = new float[4 * n]; - PApplet.arrayCopy(fillVertices, 0, temp, 0, 4 * fillVertexCount); - fillVertices = temp; + PApplet.arrayCopy(polyVertices, 0, temp, 0, 4 * polyVertexCount); + polyVertices = temp; } - void expandFillColors(int n) { + void expandPolyColors(int n) { int temp[] = new int[n]; - PApplet.arrayCopy(fillColors, 0, temp, 0, fillVertexCount); - fillColors = temp; + PApplet.arrayCopy(polyColors, 0, temp, 0, polyVertexCount); + polyColors = temp; } - void expandFillNormals(int n) { + void expandPolyNormals(int n) { float temp[] = new float[3 * n]; - PApplet.arrayCopy(fillNormals, 0, temp, 0, 3 * fillVertexCount); - fillNormals = temp; + PApplet.arrayCopy(polyNormals, 0, temp, 0, 3 * polyVertexCount); + polyNormals = temp; } - void expandFillTexcoords(int n) { + void expandPolyTexcoords(int n) { float temp[] = new float[2 * n]; - PApplet.arrayCopy(fillTexcoords, 0, temp, 0, 2 * fillVertexCount); - fillTexcoords = temp; + PApplet.arrayCopy(polyTexcoords, 0, temp, 0, 2 * polyVertexCount); + polyTexcoords = temp; } - void expandFillAmbient(int n) { + void expandPolyAmbient(int n) { int temp[] = new int[n]; - PApplet.arrayCopy(fillAmbient, 0, temp, 0, fillVertexCount); - fillAmbient = temp; + PApplet.arrayCopy(polyAmbient, 0, temp, 0, polyVertexCount); + polyAmbient = temp; } - void expandFillSpecular(int n) { + void expandPolySpecular(int n) { int temp[] = new int[n]; - PApplet.arrayCopy(fillSpecular, 0, temp, 0, fillVertexCount); - fillSpecular = temp; + PApplet.arrayCopy(polySpecular, 0, temp, 0, polyVertexCount); + polySpecular = temp; } - void expandFillEmissive(int n) { + void expandPolyEmissive(int n) { int temp[] = new int[n]; - PApplet.arrayCopy(fillEmissive, 0, temp, 0, fillVertexCount); - fillEmissive = temp; + PApplet.arrayCopy(polyEmissive, 0, temp, 0, polyVertexCount); + polyEmissive = temp; } - void expandFillShininess(int n) { + void expandPolyShininess(int n) { float temp[] = new float[n]; - PApplet.arrayCopy(fillShininess, 0, temp, 0, fillVertexCount); - fillShininess = temp; + PApplet.arrayCopy(polyShininess, 0, temp, 0, polyVertexCount); + polyShininess = temp; } - - void expandFillIndices(int n) { + + void expandPolyIndices(int n) { short temp[] = new short[n]; - PApplet.arrayCopy(fillIndices, 0, temp, 0, fillIndexCount); - fillIndices = temp; + PApplet.arrayCopy(polyIndices, 0, temp, 0, polyIndexCount); + polyIndices = temp; } void expandLineVertices(int n) { @@ -8708,10 +8225,10 @@ public class PGraphicsOpenGL extends PGraphics { lineColors = temp; } - void expandLineAttributes(int n) { + void expandLineAttribs(int n) { float temp[] = new float[4 * n]; - PApplet.arrayCopy(lineDirWidths, 0, temp, 0, 4 * lineVertexCount); - lineDirWidths = temp; + PApplet.arrayCopy(lineAttribs, 0, temp, 0, 4 * lineVertexCount); + lineAttribs = temp; } void expandLineIndices(int n) { @@ -8732,10 +8249,10 @@ public class PGraphicsOpenGL extends PGraphics { pointColors = temp; } - void expandPointAttributes(int n) { + void expandPointAttribs(int n) { float temp[] = new float[2 * n]; - PApplet.arrayCopy(pointSizes, 0, temp, 0, 2 * pointVertexCount); - pointSizes = temp; + PApplet.arrayCopy(pointAttribs, 0, temp, 0, 2 * pointVertexCount); + pointAttribs = temp; } void expandPointIndices(int n) { @@ -8749,25 +8266,25 @@ public class PGraphicsOpenGL extends PGraphics { // Trim arrays void trim() { - if (0 < fillVertexCount && fillVertexCount < fillVertices.length / 4) { - trimFillVertices(); - trimFillColors(); - trimFillNormals(); - trimFillTexcoords(); - trimFillAmbient(); - trimFillSpecular(); - trimFillEmissive(); - trimFillShininess(); + if (0 < polyVertexCount && polyVertexCount < polyVertices.length / 4) { + trimPolyVertices(); + trimPolyColors(); + trimPolyNormals(); + trimPolyTexcoords(); + trimPolyAmbient(); + trimPolySpecular(); + trimPolyEmissive(); + trimPolyShininess(); } - if (0 < fillIndexCount && fillIndexCount < fillIndices.length) { - trimFillIndices(); + if (0 < polyIndexCount && polyIndexCount < polyIndices.length) { + trimPolyIndices(); } if (0 < lineVertexCount && lineVertexCount < lineVertices.length / 4) { trimLineVertices(); trimLineColors(); - trimLineAttributes(); + trimLineAttribs(); } if (0 < lineIndexCount && lineIndexCount < lineIndices.length) { @@ -8777,7 +8294,7 @@ public class PGraphicsOpenGL extends PGraphics { if (0 < pointVertexCount && pointVertexCount < pointVertices.length / 4) { trimPointVertices(); trimPointColors(); - trimPointAttributes(); + trimPointAttribs(); } if (0 < pointIndexCount && pointIndexCount < pointIndices.length) { @@ -8785,58 +8302,58 @@ public class PGraphicsOpenGL extends PGraphics { } } - void trimFillVertices() { - float temp[] = new float[4 * fillVertexCount]; - PApplet.arrayCopy(fillVertices, 0, temp, 0, 4 * fillVertexCount); - fillVertices = temp; + void trimPolyVertices() { + float temp[] = new float[4 * polyVertexCount]; + PApplet.arrayCopy(polyVertices, 0, temp, 0, 4 * polyVertexCount); + polyVertices = temp; } - void trimFillColors() { - int temp[] = new int[fillVertexCount]; - PApplet.arrayCopy(fillColors, 0, temp, 0, fillVertexCount); - fillColors = temp; + void trimPolyColors() { + int temp[] = new int[polyVertexCount]; + PApplet.arrayCopy(polyColors, 0, temp, 0, polyVertexCount); + polyColors = temp; } - void trimFillNormals() { - float temp[] = new float[3 * fillVertexCount]; - PApplet.arrayCopy(fillNormals, 0, temp, 0, 3 * fillVertexCount); - fillNormals = temp; + void trimPolyNormals() { + float temp[] = new float[3 * polyVertexCount]; + PApplet.arrayCopy(polyNormals, 0, temp, 0, 3 * polyVertexCount); + polyNormals = temp; } - void trimFillTexcoords() { - float temp[] = new float[2 * fillVertexCount]; - PApplet.arrayCopy(fillTexcoords, 0, temp, 0, 2 * fillVertexCount); - fillTexcoords = temp; + void trimPolyTexcoords() { + float temp[] = new float[2 * polyVertexCount]; + PApplet.arrayCopy(polyTexcoords, 0, temp, 0, 2 * polyVertexCount); + polyTexcoords = temp; } - void trimFillAmbient() { - int temp[] = new int[fillVertexCount]; - PApplet.arrayCopy(fillAmbient, 0, temp, 0, fillVertexCount); - fillAmbient = temp; + void trimPolyAmbient() { + int temp[] = new int[polyVertexCount]; + PApplet.arrayCopy(polyAmbient, 0, temp, 0, polyVertexCount); + polyAmbient = temp; } - void trimFillSpecular() { - int temp[] = new int[fillVertexCount]; - PApplet.arrayCopy(fillSpecular, 0, temp, 0, fillVertexCount); - fillSpecular = temp; + void trimPolySpecular() { + int temp[] = new int[polyVertexCount]; + PApplet.arrayCopy(polySpecular, 0, temp, 0, polyVertexCount); + polySpecular = temp; } - void trimFillEmissive() { - int temp[] = new int[fillVertexCount]; - PApplet.arrayCopy(fillEmissive, 0, temp, 0, fillVertexCount); - fillEmissive = temp; + void trimPolyEmissive() { + int temp[] = new int[polyVertexCount]; + PApplet.arrayCopy(polyEmissive, 0, temp, 0, polyVertexCount); + polyEmissive = temp; } - void trimFillShininess() { - float temp[] = new float[fillVertexCount]; - PApplet.arrayCopy(fillShininess, 0, temp, 0, fillVertexCount); - fillShininess = temp; + void trimPolyShininess() { + float temp[] = new float[polyVertexCount]; + PApplet.arrayCopy(polyShininess, 0, temp, 0, polyVertexCount); + polyShininess = temp; } - - void trimFillIndices() { - short temp[] = new short[fillIndexCount]; - PApplet.arrayCopy(fillIndices, 0, temp, 0, fillIndexCount); - fillIndices = temp; + + void trimPolyIndices() { + short temp[] = new short[polyIndexCount]; + PApplet.arrayCopy(polyIndices, 0, temp, 0, polyIndexCount); + polyIndices = temp; } void trimLineVertices() { @@ -8851,10 +8368,10 @@ public class PGraphicsOpenGL extends PGraphics { lineColors = temp; } - void trimLineAttributes() { + void trimLineAttribs() { float temp[] = new float[4 * lineVertexCount]; - PApplet.arrayCopy(lineDirWidths, 0, temp, 0, 4 * lineVertexCount); - lineDirWidths = temp; + PApplet.arrayCopy(lineAttribs, 0, temp, 0, 4 * lineVertexCount); + lineAttribs = temp; } void trimLineIndices() { @@ -8875,10 +8392,10 @@ public class PGraphicsOpenGL extends PGraphics { pointColors = temp; } - void trimPointAttributes() { + void trimPointAttribs() { float temp[] = new float[2 * pointVertexCount]; - PApplet.arrayCopy(pointSizes, 0, temp, 0, 2 * pointVertexCount); - pointSizes = temp; + PApplet.arrayCopy(pointAttribs, 0, temp, 0, 2 * pointVertexCount); + pointAttribs = temp; } void trimPointIndices() { @@ -8891,9 +8408,9 @@ public class PGraphicsOpenGL extends PGraphics { // // Aggregation methods - void incFillIndices(int first, int last, int inc) { + void incPolyIndices(int first, int last, int inc) { for (int i = first; i <= last; i++) { - fillIndices[i] += inc; + polyIndices[i] += inc; } } @@ -8913,23 +8430,23 @@ public class PGraphicsOpenGL extends PGraphics { // // Normal calculation - void calcFillNormal(int i0, int i1, int i2) { + void calcPolyNormal(int i0, int i1, int i2) { int index; index = 4 * i0; - float x0 = fillVertices[index++]; - float y0 = fillVertices[index++]; - float z0 = fillVertices[index ]; + float x0 = polyVertices[index++]; + float y0 = polyVertices[index++]; + float z0 = polyVertices[index ]; index = 4 * i1; - float x1 = fillVertices[index++]; - float y1 = fillVertices[index++]; - float z1 = fillVertices[index ]; + float x1 = polyVertices[index++]; + float y1 = polyVertices[index++]; + float z1 = polyVertices[index ]; index = 4 * i2; - float x2 = fillVertices[index++]; - float y2 = fillVertices[index++]; - float z2 = fillVertices[index ]; + float x2 = polyVertices[index++]; + float y2 = polyVertices[index++]; + float z2 = polyVertices[index ]; float v12x = x2 - x1; float v12y = y2 - y1; @@ -8948,71 +8465,99 @@ public class PGraphicsOpenGL extends PGraphics { nz /= d; index = 3 * i0; - fillNormals[index++] = nx; - fillNormals[index++] = ny; - fillNormals[index ] = nz; + polyNormals[index++] = nx; + polyNormals[index++] = ny; + polyNormals[index ] = nz; index = 3 * i1; - fillNormals[index++] = nx; - fillNormals[index++] = ny; - fillNormals[index ] = nz; + polyNormals[index++] = nx; + polyNormals[index++] = ny; + polyNormals[index ] = nz; index = 3 * i2; - fillNormals[index++] = nx; - fillNormals[index++] = ny; - fillNormals[index ] = nz; + polyNormals[index++] = nx; + polyNormals[index++] = ny; + polyNormals[index ] = nz; } // ----------------------------------------------------------------- // // Add point geometry - void putPointVertex(InGeometry in, int inIdx, int tessIdx) { + // Sets point vertex with index tessIdx using the data from input vertex inIdx. + void setPointVertex(int tessIdx, InGeometry in, int inIdx) { int index; - index = 4 * inIdx; + index = 3 * inIdx; float x = in.vertices[index++]; float y = in.vertices[index++]; - float z = in.vertices[index++]; - float w = in.vertices[index ]; + float z = in.vertices[index ]; if (renderMode == IMMEDIATE && flushMode == FLUSH_WHEN_FULL && !hints[DISABLE_TRANSFORM_CACHE]) { PMatrix3D mm = modelview; index = 4 * tessIdx; - pointVertices[index++] = x * mm.m00 + y * mm.m01 + z * mm.m02 + w * mm.m03; - pointVertices[index++] = x * mm.m10 + y * mm.m11 + z * mm.m12 + w * mm.m13; - pointVertices[index++] = x * mm.m20 + y * mm.m21 + z * mm.m22 + w * mm.m23; - pointVertices[index ] = x * mm.m30 + y * mm.m31 + z * mm.m32 + w * mm.m33; + pointVertices[index++] = x * mm.m00 + y * mm.m01 + z * mm.m02 + mm.m03; + pointVertices[index++] = x * mm.m10 + y * mm.m11 + z * mm.m12 + mm.m13; + pointVertices[index++] = x * mm.m20 + y * mm.m21 + z * mm.m22 + mm.m23; + pointVertices[index ] = x * mm.m30 + y * mm.m31 + z * mm.m32 + mm.m33; } else { index = 4 * tessIdx; pointVertices[index++] = x; pointVertices[index++] = y; pointVertices[index++] = z; - pointVertices[index ] = w; + pointVertices[index ] = 1; } - pointColors[tessIdx] = in.scolors[inIdx]; - - if (renderMode == RETAINED) { - in.tessMap.addPointIndex(inIdx, tessIdx); - } + pointColors[tessIdx] = in.strokeColors[inIdx]; } // ----------------------------------------------------------------- // // Add line geometry - void putLineVertex(InGeometry in, int inIdx0, int inIdx1, int tessIdx, int rgba, float weight) { + void setLineVertex(int tessIdx, InGeometry in, int inIdx0, int rgba) { int index; - index = 4 * inIdx0; + index = 3 * inIdx0; float x0 = in.vertices[index++]; float y0 = in.vertices[index++]; - float z0 = in.vertices[index++]; - float w0 = in.vertices[index ]; + float z0 = in.vertices[index ]; + + if (renderMode == IMMEDIATE && flushMode == FLUSH_WHEN_FULL && !hints[DISABLE_TRANSFORM_CACHE]) { + PMatrix3D mm = modelview; - index = 4 * inIdx1; + index = 4 * tessIdx; + lineVertices[index++] = x0 * mm.m00 + y0 * mm.m01 + z0 * mm.m02 + mm.m03; + lineVertices[index++] = x0 * mm.m10 + y0 * mm.m11 + z0 * mm.m12 + mm.m13; + lineVertices[index++] = x0 * mm.m20 + y0 * mm.m21 + z0 * mm.m22 + mm.m23; + lineVertices[index ] = x0 * mm.m30 + y0 * mm.m31 + z0 * mm.m32 + mm.m33; + } else { + index = 4 * tessIdx; + lineVertices[index++] = x0; + lineVertices[index++] = y0; + lineVertices[index++] = z0; + lineVertices[index ] = 1; + } + + lineColors[tessIdx] = rgba; + index = 4 * tessIdx; + lineAttribs[index++] = 0; + lineAttribs[index++] = 0; + lineAttribs[index++] = 0; + lineAttribs[index ] = 0; + } + + // Sets line vertex with index tessIdx using the data from input vertices inIdx0 and inIdx1. + void setLineVertex(int tessIdx, InGeometry in, int inIdx0, int inIdx1, int rgba, float weight) { + int index; + + index = 3 * inIdx0; + float x0 = in.vertices[index++]; + float y0 = in.vertices[index++]; + float z0 = in.vertices[index ]; + + index = 3 * inIdx1; float x1 = in.vertices[index++]; float y1 = in.vertices[index++]; float z1 = in.vertices[index ]; @@ -9021,114 +8566,151 @@ public class PGraphicsOpenGL extends PGraphics { PMatrix3D mm = modelview; index = 4 * tessIdx; - lineVertices[index++] = x0 * mm.m00 + y0 * mm.m01 + z0 * mm.m02 + w0 * mm.m03; - lineVertices[index++] = x0 * mm.m10 + y0 * mm.m11 + z0 * mm.m12 + w0 * mm.m13; - lineVertices[index++] = x0 * mm.m20 + y0 * mm.m21 + z0 * mm.m22 + w0 * mm.m23; - lineVertices[index ] = x0 * mm.m30 + y0 * mm.m31 + z0 * mm.m32 + w0 * mm.m33; + lineVertices[index++] = x0 * mm.m00 + y0 * mm.m01 + z0 * mm.m02 + mm.m03; + lineVertices[index++] = x0 * mm.m10 + y0 * mm.m11 + z0 * mm.m12 + mm.m13; + lineVertices[index++] = x0 * mm.m20 + y0 * mm.m21 + z0 * mm.m22 + mm.m23; + lineVertices[index ] = x0 * mm.m30 + y0 * mm.m31 + z0 * mm.m32 + mm.m33; index = 4 * tessIdx; - lineDirWidths[index++] = x1 * mm.m00 + y1 * mm.m01 + z1 * mm.m02 + mm.m03; - lineDirWidths[index++] = x1 * mm.m10 + y1 * mm.m11 + z1 * mm.m12 + mm.m13; - lineDirWidths[index ] = x1 * mm.m20 + y1 * mm.m21 + z1 * mm.m22 + mm.m23; + lineAttribs[index++] = x1 * mm.m00 + y1 * mm.m01 + z1 * mm.m02 + mm.m03; + lineAttribs[index++] = x1 * mm.m10 + y1 * mm.m11 + z1 * mm.m12 + mm.m13; + lineAttribs[index ] = x1 * mm.m20 + y1 * mm.m21 + z1 * mm.m22 + mm.m23; } else { index = 4 * tessIdx; lineVertices[index++] = x0; lineVertices[index++] = y0; lineVertices[index++] = z0; - lineVertices[index ] = w0; + lineVertices[index ] = 1; index = 4 * tessIdx; - lineDirWidths[index++] = x1; - lineDirWidths[index++] = y1; - lineDirWidths[index ] = z1; + lineAttribs[index++] = x1; + lineAttribs[index++] = y1; + lineAttribs[index ] = z1; } - lineColors[tessIdx] = rgba; - lineDirWidths[4 * tessIdx + 3] = weight; - - if (renderMode == RETAINED) { - in.tessMap.addLineIndex0(inIdx0, tessIdx); - in.tessMap.addLineIndex1(inIdx1, tessIdx); - } + lineColors[tessIdx] = rgba; + lineAttribs[4 * tessIdx + 3] = weight; } // ----------------------------------------------------------------- // - // Add fill geometry + // Add poly geometry - void addFillVertex(float x, float y, float z, float w, + void setPolyVertex(int tessIdx, float x, float y, float z, int rgba) { + setPolyVertex(tessIdx, x, y, z, + rgba, + 0, 0, 1, + 0, 0, + 0, 0, 0, 0); + } + + void setPolyVertex(int tessIdx, float x, float y, float z, int rgba, float nx, float ny, float nz, float u, float v, - int am, int sp, int em, float shine, - InGeometry in, int[] vertices, float[] weights) { - fillVertexCheck(); + int am, int sp, int em, float shine) { + int index; + + if (renderMode == IMMEDIATE && flushMode == FLUSH_WHEN_FULL && !hints[DISABLE_TRANSFORM_CACHE]) { + PMatrix3D mm = modelview; + PMatrix3D nm = modelviewInv; + + index = 4 * tessIdx; + polyVertices[index++] = x * mm.m00 + y * mm.m01 + z * mm.m02 + mm.m03; + polyVertices[index++] = x * mm.m10 + y * mm.m11 + z * mm.m12 + mm.m13; + polyVertices[index++] = x * mm.m20 + y * mm.m21 + z * mm.m22 + mm.m23; + polyVertices[index ] = x * mm.m30 + y * mm.m31 + z * mm.m32 + mm.m33; + + index = 3 * tessIdx; + polyNormals[index++] = nx * nm.m00 + ny * nm.m10 + nz * nm.m20; + polyNormals[index++] = nx * nm.m01 + ny * nm.m11 + nz * nm.m21; + polyNormals[index ] = nx * nm.m02 + ny * nm.m12 + nz * nm.m22; + } else { + index = 4 * tessIdx; + polyVertices[index++] = x; + polyVertices[index++] = y; + polyVertices[index++] = z; + polyVertices[index ] = 1; + + index = 3 * tessIdx; + polyNormals[index++] = nx; + polyNormals[index++] = ny; + polyNormals[index ] = nz; + } + + polyColors[tessIdx] = rgba; + + index = 2 * tessIdx; + polyTexcoords[index++] = u; + polyTexcoords[index ] = v; + + polyAmbient[tessIdx] = am; + polySpecular[tessIdx] = sp; + polyEmissive[tessIdx] = em; + polyShininess[tessIdx] = shine; + } + + void addPolyVertex(float x, float y, float z, + int rgba, + float nx, float ny, float nz, + float u, float v, + int am, int sp, int em, float shine) { + polyVertexCheck(); int index; - int count = fillVertexCount - 1; + int count = polyVertexCount - 1; if (renderMode == IMMEDIATE && flushMode == FLUSH_WHEN_FULL && !hints[DISABLE_TRANSFORM_CACHE]) { PMatrix3D mm = modelview; PMatrix3D nm = modelviewInv; index = 4 * count; - fillVertices[index++] = x * mm.m00 + y * mm.m01 + z * mm.m02 + w * mm.m03; - fillVertices[index++] = x * mm.m10 + y * mm.m11 + z * mm.m12 + w * mm.m13; - fillVertices[index++] = x * mm.m20 + y * mm.m21 + z * mm.m22 + w * mm.m23; - fillVertices[index ] = x * mm.m30 + y * mm.m31 + z * mm.m32 + w * mm.m33; + polyVertices[index++] = x * mm.m00 + y * mm.m01 + z * mm.m02 + mm.m03; + polyVertices[index++] = x * mm.m10 + y * mm.m11 + z * mm.m12 + mm.m13; + polyVertices[index++] = x * mm.m20 + y * mm.m21 + z * mm.m22 + mm.m23; + polyVertices[index ] = x * mm.m30 + y * mm.m31 + z * mm.m32 + mm.m33; index = 3 * count; - fillNormals[index++] = nx * nm.m00 + ny * nm.m10 + nz * nm.m20; - fillNormals[index++] = nx * nm.m01 + ny * nm.m11 + nz * nm.m21; - fillNormals[index ] = nx * nm.m02 + ny * nm.m12 + nz * nm.m22; + polyNormals[index++] = nx * nm.m00 + ny * nm.m10 + nz * nm.m20; + polyNormals[index++] = nx * nm.m01 + ny * nm.m11 + nz * nm.m21; + polyNormals[index ] = nx * nm.m02 + ny * nm.m12 + nz * nm.m22; } else { index = 4 * count; - fillVertices[index++] = x; - fillVertices[index++] = y; - fillVertices[index++] = z; - fillVertices[index ] = w; + polyVertices[index++] = x; + polyVertices[index++] = y; + polyVertices[index++] = z; + polyVertices[index ] = 1; index = 3 * count; - fillNormals[index++] = nx; - fillNormals[index++] = ny; - fillNormals[index ] = nz; + polyNormals[index++] = nx; + polyNormals[index++] = ny; + polyNormals[index ] = nz; } - - fillColors[count] = rgba; + + polyColors[count] = rgba; index = 2 * count; - fillTexcoords[index++] = u; - fillTexcoords[index ] = v; + polyTexcoords[index++] = u; + polyTexcoords[index ] = v; - fillAmbient[count] = am; - fillSpecular[count] = sp; - fillEmissive[count] = em; - fillShininess[count] = shine; - -// if (renderMode == RETAINED) { -// in.setWeightedMapping(vertices, weights, count); -// } - - if (renderMode == RETAINED && vertices != null && weights != null) { - int len = vertices.length; - for (int i = 0; i < len; i++) { - in.tessMap.addFillIndex(vertices[i], count, weights[i]); - } - } + polyAmbient[count] = am; + polySpecular[count] = sp; + polyEmissive[count] = em; + polyShininess[count] = shine; } - void addFillVertices(InGeometry in) { - addFillVertices(in, in.firstVertex, in.lastVertex); + void addPolyVertices(InGeometry in) { + addPolyVertices(in, in.firstVertex, in.lastVertex); } - void addFillVertex(InGeometry in, int i) { - addFillVertices(in, i, i); + void addPolyVertex(InGeometry in, int i) { + addPolyVertices(in, i, i); } - void addFillVertices(InGeometry in, int i0, int i1) { + void addPolyVertices(InGeometry in, int i0, int i1) { int index; int nvert = i1 - i0 + 1; - fillVertexCheck(nvert); + polyVertexCheck(nvert); if (renderMode == IMMEDIATE && flushMode == FLUSH_WHEN_FULL && !hints[DISABLE_TRANSFORM_CACHE]) { PMatrix3D mm = modelview; @@ -9136,13 +8718,12 @@ public class PGraphicsOpenGL extends PGraphics { for (int i = 0; i < nvert; i++) { int inIdx = i0 + i; - int tessIdx = firstFillVertex + i; + int tessIdx = firstPolyVertex + i; - index = 4 * inIdx; + index = 3 * inIdx; float x = in.vertices[index++]; float y = in.vertices[index++]; - float z = in.vertices[index++]; - float w = in.vertices[index ]; + float z = in.vertices[index ]; index = 3 * inIdx; float nx = in.normals[index++]; @@ -9150,15 +8731,15 @@ public class PGraphicsOpenGL extends PGraphics { float nz = in.normals[index ]; index = 4 * tessIdx; - fillVertices[index++] = x * mm.m00 + y * mm.m01 + z * mm.m02 + w * mm.m03; - fillVertices[index++] = x * mm.m10 + y * mm.m11 + z * mm.m12 + w * mm.m13; - fillVertices[index++] = x * mm.m20 + y * mm.m21 + z * mm.m22 + w * mm.m23; - fillVertices[index ] = x * mm.m30 + y * mm.m31 + z * mm.m32 + w * mm.m33; + polyVertices[index++] = x * mm.m00 + y * mm.m01 + z * mm.m02 + mm.m03; + polyVertices[index++] = x * mm.m10 + y * mm.m11 + z * mm.m12 + mm.m13; + polyVertices[index++] = x * mm.m20 + y * mm.m21 + z * mm.m22 + mm.m23; + polyVertices[index ] = x * mm.m30 + y * mm.m31 + z * mm.m32 + mm.m33; index = 3 * tessIdx; - fillNormals[index++] = nx * nm.m00 + ny * nm.m10 + nz * nm.m20; - fillNormals[index++] = nx * nm.m01 + ny * nm.m11 + nz * nm.m21; - fillNormals[index ] = nx * nm.m02 + ny * nm.m12 + nz * nm.m22; + polyNormals[index++] = nx * nm.m00 + ny * nm.m10 + nz * nm.m20; + polyNormals[index++] = nx * nm.m01 + ny * nm.m11 + nz * nm.m21; + polyNormals[index ] = nx * nm.m02 + ny * nm.m12 + nz * nm.m22; } } else { if (nvert <= PGL.MIN_ARRAYCOPY_SIZE) { @@ -9166,13 +8747,12 @@ public class PGraphicsOpenGL extends PGraphics { // few vertices... for (int i = 0; i < nvert; i++) { int inIdx = i0 + i; - int tessIdx = firstFillVertex + i; + int tessIdx = firstPolyVertex + i; - index = 4 * inIdx; + index = 3 * inIdx; float x = in.vertices[index++]; float y = in.vertices[index++]; - float z = in.vertices[index++]; - float w = in.vertices[index ]; + float z = in.vertices[index ]; index = 3 * inIdx; float nx = in.normals[index++]; @@ -9180,73 +8760,66 @@ public class PGraphicsOpenGL extends PGraphics { float nz = in.normals[index ]; index = 4 * tessIdx; - fillVertices[index++] = x; - fillVertices[index++] = y; - fillVertices[index++] = z; - fillVertices[index ] = w; + polyVertices[index++] = x; + polyVertices[index++] = y; + polyVertices[index++] = z; + polyVertices[index ] = 1; index = 3 * tessIdx; - fillNormals[index++] = nx; - fillNormals[index++] = ny; - fillNormals[index ] = nz; + polyNormals[index++] = nx; + polyNormals[index++] = ny; + polyNormals[index ] = nz; } } else { - PApplet.arrayCopy(in.vertices, 4 * i0, fillVertices, 4 * firstFillVertex, 4 * nvert); - PApplet.arrayCopy(in.normals, 3 * i0, fillNormals, 3 * firstFillVertex, 3 * nvert); + for (int i = 0; i < nvert; i++) { + int inIdx = i0 + i; + int tessIdx = firstPolyVertex + i; + PApplet.arrayCopy(in.vertices, 3 * inIdx, polyVertices, 4 * tessIdx, 3); + polyVertices[4 * tessIdx + 3] = 1; + } + PApplet.arrayCopy(in.normals, 3 * i0, polyNormals, 3 * firstPolyVertex, 3 * nvert); } } if (nvert <= PGL.MIN_ARRAYCOPY_SIZE) { for (int i = 0; i < nvert; i++) { int inIdx = i0 + i; - int tessIdx = firstFillVertex + i; + int tessIdx = firstPolyVertex + i; index = 2 * inIdx; float u = in.texcoords[index++]; float v = in.texcoords[index ]; - - fillColors[tessIdx] = in.colors[inIdx]; + + polyColors[tessIdx] = in.colors[inIdx]; index = 2 * tessIdx; - fillTexcoords[index++] = u; - fillTexcoords[index ] = v; + polyTexcoords[index++] = u; + polyTexcoords[index ] = v; - fillAmbient[tessIdx] = in.ambient[inIdx]; - fillSpecular[tessIdx] = in.specular[inIdx]; - fillEmissive[tessIdx] = in.emissive[inIdx]; - fillShininess[tessIdx] = in.shininess[inIdx]; + polyAmbient[tessIdx] = in.ambient[inIdx]; + polySpecular[tessIdx] = in.specular[inIdx]; + polyEmissive[tessIdx] = in.emissive[inIdx]; + polyShininess[tessIdx] = in.shininess[inIdx]; } } else { - PApplet.arrayCopy(in.colors, i0, fillColors, firstFillVertex, nvert); - PApplet.arrayCopy(in.texcoords, 2 * i0, fillTexcoords, 2 * firstFillVertex, 2 * nvert); - PApplet.arrayCopy(in.ambient, i0, fillAmbient, firstFillVertex, nvert); - PApplet.arrayCopy(in.specular, i0, fillSpecular, firstFillVertex, nvert); - PApplet.arrayCopy(in.emissive, i0, fillEmissive, firstFillVertex, nvert); - PApplet.arrayCopy(in.shininess, i0, fillShininess, firstFillVertex, nvert); - } - -// if (renderMode == RETAINED) { -// in.addLinearMapping(i0, i1, firstFillVertex); -// } - - if (renderMode == RETAINED) { - for (int i = 0; i < nvert; i++) { - int inIdx = i0 + i; - int tessIdx = firstFillVertex + i; - in.tessMap.addFillIndex(inIdx, tessIdx); - } - } + PApplet.arrayCopy(in.colors, i0, polyColors, firstPolyVertex, nvert); + PApplet.arrayCopy(in.texcoords, 2 * i0, polyTexcoords, 2 * firstPolyVertex, 2 * nvert); + PApplet.arrayCopy(in.ambient, i0, polyAmbient, firstPolyVertex, nvert); + PApplet.arrayCopy(in.specular, i0, polySpecular, firstPolyVertex, nvert); + PApplet.arrayCopy(in.emissive, i0, polyEmissive, firstPolyVertex, nvert); + PApplet.arrayCopy(in.shininess, i0, polyShininess, firstPolyVertex, nvert); + } } // ----------------------------------------------------------------- // // Matrix transformations - void applyMatrixOnFillGeometry(PMatrix tr, int first, int last) { + void applyMatrixOnPolyGeometry(PMatrix tr, int first, int last) { if (tr instanceof PMatrix2D) { - applyMatrixOnFillGeometry((PMatrix2D) tr, first, last); + applyMatrixOnPolyGeometry((PMatrix2D) tr, first, last); } else if (tr instanceof PMatrix3D) { - applyMatrixOnFillGeometry((PMatrix3D) tr, first, last); + applyMatrixOnPolyGeometry((PMatrix3D) tr, first, last); } } @@ -9266,26 +8839,26 @@ public class PGraphicsOpenGL extends PGraphics { } } - void applyMatrixOnFillGeometry(PMatrix2D tr, int first, int last) { + void applyMatrixOnPolyGeometry(PMatrix2D tr, int first, int last) { if (first < last) { int index; for (int i = first; i <= last; i++) { index = 4 * i; - float x = fillVertices[index++]; - float y = fillVertices[index ]; + float x = polyVertices[index++]; + float y = polyVertices[index ]; index = 3 * i; - float nx = fillNormals[index++]; - float ny = fillNormals[index ]; + float nx = polyNormals[index++]; + float ny = polyNormals[index ]; index = 4 * i; - fillVertices[index++] = x * tr.m00 + y * tr.m01 + tr.m02; - fillVertices[index ] = x * tr.m10 + y * tr.m11 + tr.m12; + polyVertices[index++] = x * tr.m00 + y * tr.m01 + tr.m02; + polyVertices[index ] = x * tr.m10 + y * tr.m11 + tr.m12; index = 3 * i; - fillNormals[index++] = nx * tr.m00 + ny * tr.m01; - fillNormals[index ] = nx * tr.m10 + ny * tr.m11; + polyNormals[index++] = nx * tr.m00 + ny * tr.m01; + polyNormals[index ] = nx * tr.m10 + ny * tr.m11; } } } @@ -9300,16 +8873,16 @@ public class PGraphicsOpenGL extends PGraphics { float y = lineVertices[index ]; index = 4 * i; - float xa = lineDirWidths[index++]; - float ya = lineDirWidths[index ]; + float xa = lineAttribs[index++]; + float ya = lineAttribs[index ]; index = 4 * i; lineVertices[index++] = x * tr.m00 + y * tr.m01 + tr.m02; lineVertices[index ] = x * tr.m10 + y * tr.m11 + tr.m12; index = 4 * i; - lineDirWidths[index++] = xa * tr.m00 + ya * tr.m01 + tr.m02; - lineDirWidths[index ] = xa * tr.m10 + ya * tr.m11 + tr.m12; + lineAttribs[index++] = xa * tr.m00 + ya * tr.m01 + tr.m02; + lineAttribs[index ] = xa * tr.m10 + ya * tr.m11 + tr.m12; } } } @@ -9330,32 +8903,32 @@ public class PGraphicsOpenGL extends PGraphics { } } - void applyMatrixOnFillGeometry(PMatrix3D tr, int first, int last) { + void applyMatrixOnPolyGeometry(PMatrix3D tr, int first, int last) { if (first < last) { int index; for (int i = first; i <= last; i++) { index = 4 * i; - float x = fillVertices[index++]; - float y = fillVertices[index++]; - float z = fillVertices[index++]; - float w = fillVertices[index ]; + float x = polyVertices[index++]; + float y = polyVertices[index++]; + float z = polyVertices[index++]; + float w = polyVertices[index ]; index = 3 * i; - float nx = fillNormals[index++]; - float ny = fillNormals[index++]; - float nz = fillNormals[index ]; + float nx = polyNormals[index++]; + float ny = polyNormals[index++]; + float nz = polyNormals[index ]; index = 4 * i; - fillVertices[index++] = x * tr.m00 + y * tr.m01 + z * tr.m02 + w * tr.m03; - fillVertices[index++] = x * tr.m10 + y * tr.m11 + z * tr.m12 + w * tr.m13; - fillVertices[index++] = x * tr.m20 + y * tr.m21 + z * tr.m22 + w * tr.m23; - fillVertices[index ] = x * tr.m30 + y * tr.m31 + z * tr.m32 + w * tr.m33; + polyVertices[index++] = x * tr.m00 + y * tr.m01 + z * tr.m02 + w * tr.m03; + polyVertices[index++] = x * tr.m10 + y * tr.m11 + z * tr.m12 + w * tr.m13; + polyVertices[index++] = x * tr.m20 + y * tr.m21 + z * tr.m22 + w * tr.m23; + polyVertices[index ] = x * tr.m30 + y * tr.m31 + z * tr.m32 + w * tr.m33; index = 3 * i; - fillNormals[index++] = nx * tr.m00 + ny * tr.m01 + nz * tr.m02; - fillNormals[index++] = nx * tr.m10 + ny * tr.m11 + nz * tr.m12; - fillNormals[index ] = nx * tr.m20 + ny * tr.m21 + nz * tr.m22; + polyNormals[index++] = nx * tr.m00 + ny * tr.m01 + nz * tr.m02; + polyNormals[index++] = nx * tr.m10 + ny * tr.m11 + nz * tr.m12; + polyNormals[index ] = nx * tr.m20 + ny * tr.m21 + nz * tr.m22; } } } @@ -9372,9 +8945,9 @@ public class PGraphicsOpenGL extends PGraphics { float w = lineVertices[index ]; index = 4 * i; - float xa = lineDirWidths[index++]; - float ya = lineDirWidths[index++]; - float za = lineDirWidths[index ]; + float xa = lineAttribs[index++]; + float ya = lineAttribs[index++]; + float za = lineAttribs[index ]; index = 4 * i; lineVertices[index++] = x * tr.m00 + y * tr.m01 + z * tr.m02 + w * tr.m03; @@ -9383,9 +8956,9 @@ public class PGraphicsOpenGL extends PGraphics { lineVertices[index ] = x * tr.m30 + y * tr.m31 + z * tr.m32 + w * tr.m33; index = 4 * i; - lineDirWidths[index++] = xa * tr.m00 + ya * tr.m01 + za * tr.m02 + tr.m03; - lineDirWidths[index++] = xa * tr.m10 + ya * tr.m11 + za * tr.m12 + tr.m13; - lineDirWidths[index ] = xa * tr.m20 + ya * tr.m21 + za * tr.m22 + tr.m23; + lineAttribs[index++] = xa * tr.m00 + ya * tr.m01 + za * tr.m02 + tr.m03; + lineAttribs[index++] = xa * tr.m10 + ya * tr.m11 + za * tr.m12 + tr.m13; + lineAttribs[index ] = xa * tr.m20 + ya * tr.m21 + za * tr.m22 + tr.m23; } } } @@ -9415,6 +8988,12 @@ public class PGraphicsOpenGL extends PGraphics { protected class Tessellator { InGeometry in; TessGeometry tess; + TexCache texCache; + PImage prevTexImage; + PImage newTexImage; + int firstTexIndex; + int firstTexCache; + PGL.Tessellator gluTess; TessellatorCallback callback; @@ -9424,32 +9003,40 @@ public class PGraphicsOpenGL extends PGraphics { float strokeWeight; int strokeJoin; int strokeCap; + boolean accurate2DStrokes; + + PMatrix transform; + boolean is2D, is3D; int[] rawIndices; int rawSize; + int firstPolyIndexCache; + int lastPolyIndexCache; + int firstLineIndexCache; + int lastLineIndexCache; int firstPointIndexCache; int lastPointIndexCache; - int firstLineIndexCache; - int lastLineIndexCache; - int firstFillIndexCache; - int lastFillIndexCache; Tessellator() { callback = new TessellatorCallback(); gluTess = pgl.createTessellator(callback); rawIndices = new int[512]; + accurate2DStrokes = true; + transform = null; + is2D = false; + is3D = true; } void setInGeometry(InGeometry in) { this.in = in; - + + firstPolyIndexCache = -1; + lastPolyIndexCache = -1; + firstLineIndexCache = -1; + lastLineIndexCache = -1; firstPointIndexCache = -1; lastPointIndexCache = -1; - firstLineIndexCache = -1; - lastLineIndexCache = -1; - firstFillIndexCache = -1; - lastFillIndexCache = -1; } void setTessGeometry(TessGeometry tess) { @@ -9479,7 +9066,31 @@ public class PGraphicsOpenGL extends PGraphics { void setStrokeCap(int strokeCap) { this.strokeCap = strokeCap; } + + void setAccurate2DStrokes(boolean accurate) { + this.accurate2DStrokes = accurate; + } + + void setTexCache(TexCache texCache, PImage prevTexImage, PImage newTexImage) { + this.texCache = texCache; + this.prevTexImage = prevTexImage; + this.newTexImage = newTexImage; + } + void set3D(boolean value) { + if (value) { + this.is2D = false; + this.is3D = true; + } else { + this.is2D = true; + this.is3D = false; + } + } + + void setTransform(PMatrix transform) { + this.transform = transform; + } + // ----------------------------------------------------------------- // // Point tessellation @@ -9494,366 +9105,599 @@ public class PGraphicsOpenGL extends PGraphics { void tessellateRoundPoints() { int nInVert = in.lastVertex - in.firstVertex + 1; - if (stroke && 1 <= nInVert) { - int perim = PApplet.max(MIN_POINT_ACCURACY, (int) (TWO_PI * strokeWeight / 20)); - int nPtVert = perim + 1; + // Each point generates a separate triangle fan. + // The number of triangles of each fan depends on the + // stroke weight of the point. + int nPtVert = PApplet.max(MIN_POINT_ACCURACY, + (int) (TWO_PI * strokeWeight / POINT_ACCURACY_FACTOR)) + 1; if (PGL.MAX_VERTEX_INDEX1 <= nPtVert) { throw new RuntimeException("P3D: error in point tessellation."); } - - // Each point generates a separate triangle fan. - // The number of triangles of each fan depends on the - // stroke weight of the point. int nvertTot = nPtVert * nInVert; int nindTot = 3 * (nPtVert - 1) * nInVert; - - tess.pointVertexCheck(nvertTot); - tess.pointIndexCheck(nindTot); - int vertIdx = tess.firstPointVertex; - int attribIdx = tess.firstPointVertex; - int indIdx = tess.firstPointIndex; - IndexCache cache = tess.pointIndexCache; - int index = in.renderMode == RETAINED ? cache.addNew() : cache.getLast(); - firstPointIndexCache = index; - for (int i = in.firstVertex; i <= in.lastVertex; i++) { - // Creating the triangle fan for each input vertex. - - int count = cache.vertexCount[index]; - if (PGL.MAX_VERTEX_INDEX1 <= count + nPtVert) { - // We need to start a new index block for this point. - index = cache.addNew(); - count = 0; - } - - // All the tessellated vertices are identical to the center point - for (int k = 0; k < nPtVert; k++) { - tess.putPointVertex(in, i, vertIdx); - vertIdx++; - } - - // The attributes for each tessellated vertex are the displacement along - // the circle perimeter. The point shader will read these attributes and - // displace the vertices in screen coordinates so the circles are always - // camera facing (bilboards) - tess.pointSizes[2 * attribIdx + 0] = 0; - tess.pointSizes[2 * attribIdx + 1] = 0; - attribIdx++; - float val = 0; - float inc = (float) SINCOS_LENGTH / perim; - for (int k = 0; k < perim; k++) { - tess.pointSizes[2 * attribIdx + 0] = 0.5f * cosLUT[(int) val] * strokeWeight; - tess.pointSizes[2 * attribIdx + 1] = 0.5f * sinLUT[(int) val] * strokeWeight; - val = (val + inc) % SINCOS_LENGTH; - attribIdx++; - } - - // Adding vert0 to take into account the triangles of all - // the preceding points. - for (int k = 1; k < nPtVert - 1; k++) { - tess.pointIndices[indIdx++] = (short) (count + 0); - tess.pointIndices[indIdx++] = (short) (count + k); - tess.pointIndices[indIdx++] = (short) (count + k + 1); - } - // Final triangle between the last and first point: - tess.pointIndices[indIdx++] = (short) (count + 0); - tess.pointIndices[indIdx++] = (short) (count + 1); - tess.pointIndices[indIdx++] = (short) (count + nPtVert - 1); - - cache.incCounts(index, 3 * (nPtVert - 1), nPtVert); + if (is3D) { + tessellateRoundPoints3D(nvertTot, nindTot, nPtVert); + } else if (is2D) { + beginNoTex(); + tessellateRoundPoints2D(nvertTot, nindTot, nPtVert); + endNoTex(); } - lastPointIndexCache = index; - -// NEW TESSMAP API -// if (tess.renderMode == RETAINED) { -// in.addPointMapping(in.firstVertex, in.lastVertex, tess.firstPointVertex, nPtVert); -// } } } + + void tessellateRoundPoints3D(int nvertTot, int nindTot, int nPtVert) { + int perim = nPtVert - 1; + tess.pointVertexCheck(nvertTot); + tess.pointIndexCheck(nindTot); + int vertIdx = tess.firstPointVertex; + int attribIdx = tess.firstPointVertex; + int indIdx = tess.firstPointIndex; + IndexCache cache = tess.pointIndexCache; + int index = in.renderMode == RETAINED ? cache.addNew() : cache.getLast(); + firstPointIndexCache = index; + for (int i = in.firstVertex; i <= in.lastVertex; i++) { + // Creating the triangle fan for each input vertex. + + int count = cache.vertexCount[index]; + if (PGL.MAX_VERTEX_INDEX1 <= count + nPtVert) { + // We need to start a new index block for this point. + index = cache.addNew(); + count = 0; + } + // All the tessellated vertices are identical to the center point + for (int k = 0; k < nPtVert; k++) { + tess.setPointVertex(vertIdx, in, i); + vertIdx++; + } + + // The attributes for each tessellated vertex are the displacement along + // the circle perimeter. The point shader will read these attributes and + // displace the vertices in screen coordinates so the circles are always + // camera facing (bilboards) + tess.pointAttribs[2 * attribIdx + 0] = 0; + tess.pointAttribs[2 * attribIdx + 1] = 0; + attribIdx++; + float val = 0; + float inc = (float) SINCOS_LENGTH / perim; + for (int k = 0; k < perim; k++) { + tess.pointAttribs[2 * attribIdx + 0] = 0.5f * cosLUT[(int) val] * strokeWeight; + tess.pointAttribs[2 * attribIdx + 1] = 0.5f * sinLUT[(int) val] * strokeWeight; + val = (val + inc) % SINCOS_LENGTH; + attribIdx++; + } + + // Adding vert0 to take into account the triangles of all + // the preceding points. + for (int k = 1; k < nPtVert - 1; k++) { + tess.pointIndices[indIdx++] = (short) (count + 0); + tess.pointIndices[indIdx++] = (short) (count + k); + tess.pointIndices[indIdx++] = (short) (count + k + 1); + } + // Final triangle between the last and first point: + tess.pointIndices[indIdx++] = (short) (count + 0); + tess.pointIndices[indIdx++] = (short) (count + 1); + tess.pointIndices[indIdx++] = (short) (count + nPtVert - 1); + + cache.incCounts(index, 3 * (nPtVert - 1), nPtVert); + } + lastPointIndexCache = index; + } + + void tessellateRoundPoints2D(int nvertTot, int nindTot, int nPtVert) { + int perim = nPtVert - 1; + tess.polyVertexCheck(nvertTot); + tess.polyIndexCheck(nindTot); + int vertIdx = tess.firstPolyVertex; + int indIdx = tess.firstPolyIndex; + IndexCache cache = tess.polyIndexCache; + int index = in.renderMode == RETAINED ? cache.addNew() : cache.getLast(); + firstPointIndexCache = index; + for (int i = in.firstVertex; i <= in.lastVertex; i++) { + int count = cache.vertexCount[index]; + if (PGL.MAX_VERTEX_INDEX1 <= count + nPtVert) { + // We need to start a new index block for this point. + index = cache.addNew(); + count = 0; + } + + float x0 = in.vertices[3 * i + 0]; + float y0 = in.vertices[3 * i + 1]; + int rgba = in.strokeColors[i]; + + float val = 0; + float inc = (float) SINCOS_LENGTH / perim; + tess.setPolyVertex(vertIdx, x0, y0, 0, rgba); + vertIdx++; + for (int k = 0; k < perim; k++) { + tess.setPolyVertex(vertIdx, x0 + 0.5f * cosLUT[(int) val] * strokeWeight, + y0 + 0.5f * sinLUT[(int) val] * strokeWeight, 0, rgba); + vertIdx++; + val = (val + inc) % SINCOS_LENGTH; + } + + // Adding vert0 to take into account the triangles of all + // the preceding points. + for (int k = 1; k < nPtVert - 1; k++) { + tess.polyIndices[indIdx++] = (short) (count + 0); + tess.polyIndices[indIdx++] = (short) (count + k); + tess.polyIndices[indIdx++] = (short) (count + k + 1); + } + // Final triangle between the last and first point: + tess.polyIndices[indIdx++] = (short) (count + 0); + tess.polyIndices[indIdx++] = (short) (count + 1); + tess.polyIndices[indIdx++] = (short) (count + nPtVert - 1); + + cache.incCounts(index, 3 * (nPtVert - 1), nPtVert); + } + lastPointIndexCache = lastPolyIndexCache = index; + } + void tessellateSquarePoints() { int nInVert = in.lastVertex - in.firstVertex + 1; - - if (stroke && 1 <= nInVert) { - // Each point generates a separate quad. - int quadCount = nInVert; - + if (stroke && 1 <= nInVert) { + int quadCount = nInVert; // Each point generates a separate quad. // Each quad is formed by 5 vertices, the center one // is the input vertex, and the other 4 define the // corners (so, a triangle fan again). int nvertTot = 5 * quadCount; // So the quad is formed by 4 triangles, each requires // 3 indices. - int nindTot = 12 * quadCount; - - tess.pointVertexCheck(nvertTot); - tess.pointIndexCheck(nindTot); - int vertIdx = tess.firstPointVertex; - int attribIdx = tess.firstPointVertex; - int indIdx = tess.firstPointIndex; - IndexCache cache = tess.pointIndexCache; - int index = in.renderMode == RETAINED ? cache.addNew() : cache.getLast(); - firstPointIndexCache = index; - for (int i = in.firstVertex; i <= in.lastVertex; i++) { - int nvert = 5; - int count = cache.vertexCount[index]; - if (PGL.MAX_VERTEX_INDEX1 <= count + nvert) { - // We need to start a new index block for this point. - index = cache.addNew(); - count = 0; - } - - for (int k = 0; k < nvert; k++) { - tess.putPointVertex(in, i, vertIdx); - vertIdx++; - } - - // The attributes for each tessellated vertex are the displacement along - // the quad corners. The point shader will read these attributes and - // displace the vertices in screen coordinates so the quads are always - // camera facing (bilboards) - tess.pointSizes[2 * attribIdx + 0] = 0; - tess.pointSizes[2 * attribIdx + 1] = 0; - attribIdx++; - for (int k = 0; k < 4; k++) { - tess.pointSizes[2 * attribIdx + 0] = 0.5f * QUAD_POINT_SIGNS[k][0] * strokeWeight; - tess.pointSizes[2 * attribIdx + 1] = 0.5f * QUAD_POINT_SIGNS[k][1] * strokeWeight; - attribIdx++; - } - - // Adding firstVert to take into account the triangles of all - // the preceding points. - for (int k = 1; k < nvert - 1; k++) { - tess.pointIndices[indIdx++] = (short) (count + 0); - tess.pointIndices[indIdx++] = (short) (count + k); - tess.pointIndices[indIdx++] = (short) (count + k + 1); - } - // Final triangle between the last and first point: - tess.pointIndices[indIdx++] = (short) (count + 0); - tess.pointIndices[indIdx++] = (short) (count + 1); - tess.pointIndices[indIdx++] = (short) (count + nvert - 1); - - cache.incCounts(index, 12, 5); + int nindTot = 12 * quadCount; + if (is3D) { + tessellateSquarePoints3D(nvertTot, nindTot); + } else if (is2D) { + beginNoTex(); + tessellateSquarePoints2D(nvertTot, nindTot); + endNoTex(); } - lastPointIndexCache = index; - -// NEW TESSMAP API -// if (tess.renderMode == RETAINED) { -// in.addPointMapping(in.firstVertex, in.lastVertex, tess.firstPointVertex, 5); -// } } } + + void tessellateSquarePoints3D(int nvertTot, int nindTot) { + tess.pointVertexCheck(nvertTot); + tess.pointIndexCheck(nindTot); + int vertIdx = tess.firstPointVertex; + int attribIdx = tess.firstPointVertex; + int indIdx = tess.firstPointIndex; + IndexCache cache = tess.pointIndexCache; + int index = in.renderMode == RETAINED ? cache.addNew() : cache.getLast(); + firstPointIndexCache = index; + for (int i = in.firstVertex; i <= in.lastVertex; i++) { + int nvert = 5; + int count = cache.vertexCount[index]; + if (PGL.MAX_VERTEX_INDEX1 <= count + nvert) { + // We need to start a new index block for this point. + index = cache.addNew(); + count = 0; + } + + for (int k = 0; k < nvert; k++) { + tess.setPointVertex(vertIdx, in, i); + vertIdx++; + } + // The attributes for each tessellated vertex are the displacement along + // the quad corners. The point shader will read these attributes and + // displace the vertices in screen coordinates so the quads are always + // camera facing (bilboards) + tess.pointAttribs[2 * attribIdx + 0] = 0; + tess.pointAttribs[2 * attribIdx + 1] = 0; + attribIdx++; + for (int k = 0; k < 4; k++) { + tess.pointAttribs[2 * attribIdx + 0] = 0.5f * QUAD_POINT_SIGNS[k][0] * strokeWeight; + tess.pointAttribs[2 * attribIdx + 1] = 0.5f * QUAD_POINT_SIGNS[k][1] * strokeWeight; + attribIdx++; + } + + // Adding firstVert to take into account the triangles of all + // the preceding points. + for (int k = 1; k < nvert - 1; k++) { + tess.pointIndices[indIdx++] = (short) (count + 0); + tess.pointIndices[indIdx++] = (short) (count + k); + tess.pointIndices[indIdx++] = (short) (count + k + 1); + } + // Final triangle between the last and first point: + tess.pointIndices[indIdx++] = (short) (count + 0); + tess.pointIndices[indIdx++] = (short) (count + 1); + tess.pointIndices[indIdx++] = (short) (count + nvert - 1); + + cache.incCounts(index, 12, 5); + } + lastPointIndexCache = index; + } + + void tessellateSquarePoints2D(int nvertTot, int nindTot) { + tess.polyVertexCheck(nvertTot); + tess.polyIndexCheck(nindTot); + int vertIdx = tess.firstPolyVertex; + int indIdx = tess.firstPolyIndex; + IndexCache cache = tess.polyIndexCache; + int index = in.renderMode == RETAINED ? cache.addNew() : cache.getLast(); + firstPointIndexCache = index; + for (int i = in.firstVertex; i <= in.lastVertex; i++) { + int nvert = 5; + int count = cache.vertexCount[index]; + if (PGL.MAX_VERTEX_INDEX1 <= count + nvert) { + // We need to start a new index block for this point. + index = cache.addNew(); + count = 0; + } + + float x0 = in.vertices[3 * i + 0]; + float y0 = in.vertices[3 * i + 1]; + int rgba = in.strokeColors[i]; + + tess.setPolyVertex(vertIdx, x0, y0, 0, rgba); + vertIdx++; + for (int k = 0; k < nvert - 1; k++) { + tess.setPolyVertex(vertIdx, x0 + 0.5f * QUAD_POINT_SIGNS[k][0] * strokeWeight, + y0 + 0.5f * QUAD_POINT_SIGNS[k][1] * strokeWeight, 0, rgba); + vertIdx++; + } + + for (int k = 1; k < nvert - 1; k++) { + tess.polyIndices[indIdx++] = (short) (count + 0); + tess.polyIndices[indIdx++] = (short) (count + k); + tess.polyIndices[indIdx++] = (short) (count + k + 1); + } + // Final triangle between the last and first point: + tess.polyIndices[indIdx++] = (short) (count + 0); + tess.polyIndices[indIdx++] = (short) (count + 1); + tess.polyIndices[indIdx++] = (short) (count + nvert - 1); + + cache.incCounts(index, 12, 5); + } + lastPointIndexCache = lastPolyIndexCache = index; + } + // ----------------------------------------------------------------- // // Line tessellation void tessellateLines() { - int nInVert = in.lastVertex - in.firstVertex + 1; - + int nInVert = in.lastVertex - in.firstVertex + 1; if (stroke && 2 <= nInVert) { - int lineCount = nInVert / 2; - int first = in.firstVertex; - if (is3D()) { - // Lines are made up of 4 vertices defining the quad. - // Each vertex has its own offset representing the stroke weight. - int nvert = lineCount * 4; - // Each stroke line has 4 vertices, defining 2 triangles, which - // require 3 indices to specify their connectivities. - int nind = lineCount * 2 * 3; - - tess.lineVertexCheck(nvert); - tess.lineIndexCheck(nind); - int index = in.renderMode == RETAINED ? tess.lineIndexCache.addNew() : tess.lineIndexCache.getLast(); - firstLineIndexCache = index; - for (int ln = 0; ln < lineCount; ln++) { - int i0 = first + 2 * ln + 0; - int i1 = first + 2 * ln + 1; - index = addLine(i0, i1, index, false); - } - lastLineIndexCache = index; - -// NEW TESSMAP API -// if (tess.renderMode == RETAINED) { -// addLineMapping(in.firstVertex, in.lastVertex); -// } - } else { - // 2D renderer, the stroke geometry is stored in the fill array for accurate depth sorting - LinePath path = new LinePath(LinePath.WIND_NON_ZERO); - for (int ln = 0; ln < lineCount; ln++) { - int i0 = first + 2 * ln + 0; - int i1 = first + 2 * ln + 1; - path.moveTo(inGeo.vertices[4 * i0 + 0], inGeo.vertices[4 * i0 + 1]); - path.lineTo(inGeo.vertices[4 * i1 + 0], inGeo.vertices[4 * i1 + 1]); - } - tessellateLinePath(path); + // Each individual line is formed by two consecutive input vertices. + int lineCount = nInVert / 2; + if (is3D) { + tessellateLines3D(lineCount); + } else if (is2D) { + beginNoTex(); + tessellateLines2D(lineCount); + endNoTex(); } } } - void tessellateLineStrip() { - int nInVert = in.lastVertex - in.firstVertex + 1; - int lineCount = nInVert - 1; + void tessellateLines3D(int lineCount) { + // Lines are made up of 4 vertices defining the quad. + int nvert = lineCount * 4; + // Each stroke line has 4 vertices, defining 2 triangles, which + // require 3 indices to specify their connectivities. + int nind = lineCount * 2 * 3; - if (stroke && 2 <= nInVert) { - if (is3D()) { - int nvert = lineCount * 4; - int nind = lineCount * 2 * 3; - tess.lineVertexCheck(nvert); - tess.lineIndexCheck(nind); - int index = in.renderMode == RETAINED ? tess.lineIndexCache.addNew() : tess.lineIndexCache.getLast(); - firstLineIndexCache = index; - int i0 = in.firstVertex; - for (int ln = 0; ln < lineCount; ln++) { - int i1 = in.firstVertex + ln + 1; - index = addLine(i0, i1, index, false); - i0 = i1; - } - lastLineIndexCache = index; - -// NEW TESSMAP API -// if (tess.renderMode == RETAINED) { -// addLineMapping(in.firstVertex, in.lastVertex); -// } - } else { - // 2D renderer, the stroke geometry is stored in the fill array for accurate depth sorting - int first = in.firstVertex; - LinePath path = new LinePath(LinePath.WIND_NON_ZERO); - path.moveTo(inGeo.vertices[4 * first + 0], inGeo.vertices[4 * first + 1]); - for (int ln = 0; ln < lineCount; ln++) { - int i1 = first + ln + 1; - path.lineTo(inGeo.vertices[4 * i1 + 0], inGeo.vertices[4 * i1 + 1]); - } - tessellateLinePath(path); - } + int first = in.firstVertex; + tess.lineVertexCheck(nvert); + tess.lineIndexCheck(nind); + int index = in.renderMode == RETAINED ? tess.lineIndexCache.addNew() : tess.lineIndexCache.getLast(); + firstLineIndexCache = index; + for (int ln = 0; ln < lineCount; ln++) { + int i0 = first + 2 * ln + 0; + int i1 = first + 2 * ln + 1; + index = addLine3D(i0, i1, index, null, false); } + lastLineIndexCache = index; } + void tessellateLines2D(int lineCount) { + int nvert = lineCount * 4; + int nind = lineCount * 2 * 3; + + int first = in.firstVertex; + if (noCapsJoins(nvert)) { + tess.polyVertexCheck(nvert); + tess.polyIndexCheck(nind); + int index = in.renderMode == RETAINED ? tess.polyIndexCache.addNew() : tess.polyIndexCache.getLast(); + firstLineIndexCache = index; + if (firstPolyIndexCache == -1) firstPolyIndexCache = index; // If the geometry has no fill, needs the first poly index. + for (int ln = 0; ln < lineCount; ln++) { + int i0 = first + 2 * ln + 0; + int i1 = first + 2 * ln + 1; + index = addLine2D(i0, i1, index, false); + } + lastLineIndexCache = lastPolyIndexCache = index; + } else { // full stroking algorithm + LinePath path = new LinePath(LinePath.WIND_NON_ZERO); + for (int ln = 0; ln < lineCount; ln++) { + int i0 = first + 2 * ln + 0; + int i1 = first + 2 * ln + 1; + path.moveTo(in.vertices[3 * i0 + 0], in.vertices[3 * i0 + 1]); + path.lineTo(in.vertices[3 * i1 + 0], in.vertices[3 * i1 + 1]); + } + tessellateLinePath(path); + } + } + + void tessellateLineStrip() { + int nInVert = in.lastVertex - in.firstVertex + 1; + if (stroke && 2 <= nInVert) { + int lineCount = nInVert - 1; + if (is3D) { + tessellateLineStrip3D(lineCount); + } else if (is2D) { + beginNoTex(); + tessellateLineStrip2D(lineCount); + endNoTex(); + } + } + } + + void tessellateLineStrip3D(int lineCount) { + int nvert = lineCount * 4 + (lineCount - 1); // (lineCount - 1) for the bevel triangles + int nind = lineCount * 2 * 3 + (lineCount - 1) * 2 * 3; // same thing + + tess.lineVertexCheck(nvert); + tess.lineIndexCheck(nind); + int index = in.renderMode == RETAINED ? tess.lineIndexCache.addNew() : tess.lineIndexCache.getLast(); + firstLineIndexCache = index; + int i0 = in.firstVertex; + short[] lastInd = {-1, -1}; + for (int ln = 0; ln < lineCount; ln++) { + int i1 = in.firstVertex + ln + 1; + index = addLine3D(i0, i1, index, lastInd, false); + i0 = i1; + } + lastLineIndexCache = index; + } + + void tessellateLineStrip2D(int lineCount) { + int nvert = lineCount * 4; + int nind = lineCount * 2 * 3; + + if (noCapsJoins(nvert)) { + tess.polyVertexCheck(nvert); + tess.polyIndexCheck(nind); + int index = in.renderMode == RETAINED ? tess.polyIndexCache.addNew() : tess.polyIndexCache.getLast(); + firstLineIndexCache = index; + if (firstPolyIndexCache == -1) firstPolyIndexCache = index; // If the geometry has no fill, needs the first poly index. + int i0 = in.firstVertex; + for (int ln = 0; ln < lineCount; ln++) { + int i1 = in.firstVertex + ln + 1; + index = addLine2D(i0, i1, index, false); + i0 = i1; + } + lastLineIndexCache = lastPolyIndexCache = index; + } else { // full stroking algorithm + int first = in.firstVertex; + LinePath path = new LinePath(LinePath.WIND_NON_ZERO); + path.moveTo(in.vertices[3 * first + 0], in.vertices[3 * first + 1]); + for (int ln = 0; ln < lineCount; ln++) { + int i1 = first + ln + 1; + path.lineTo(in.vertices[3 * i1 + 0], in.vertices[3 * i1 + 1]); + } + tessellateLinePath(path); + } + } + void tessellateLineLoop() { int nInVert = in.lastVertex - in.firstVertex + 1; - if (stroke && 2 <= nInVert) { int lineCount = nInVert; - if (is3D()) { - int nvert = lineCount * 4; - int nind = lineCount * 2 * 3; - tess.lineVertexCheck(nvert); - tess.lineIndexCheck(nind); - int index = in.renderMode == RETAINED ? tess.lineIndexCache.addNew() : tess.lineIndexCache.getLast(); - firstLineIndexCache = index; - int i0 = in.firstVertex; - for (int ln = 0; ln < lineCount - 1; ln++) { - int i1 = in.firstVertex + ln + 1; - index = addLine(i0, i1, index, false); - i0 = i1; - } - index = addLine(in.lastVertex, in.firstVertex, index, false); - lastLineIndexCache = index; - -// NEW TESSMAP API -// if (tess.renderMode == RETAINED) { -// addLineMapping(in.firstVertex, in.lastVertex); -// } - } else { - // 2D renderer, the stroke geometry is stored in the fill array for accurate depth sorting - int first = in.firstVertex; - LinePath path = new LinePath(LinePath.WIND_NON_ZERO); - path.moveTo(inGeo.vertices[4 * first + 0], inGeo.vertices[4 * first + 1]); - for (int ln = 0; ln < lineCount - 1; ln++) { - int i1 = first + ln + 1; - path.lineTo(inGeo.vertices[4 * i1 + 0], inGeo.vertices[4 * i1 + 1]); - } - path.closePath(); - tessellateLinePath(path); - } - } + if (is3D) { + tessellateLineLoop3D(lineCount); + } else if (is2D) { + beginNoTex(); + tessellateLineLoop2D(lineCount); + endNoTex(); + } + } + } + + void tessellateLineLoop3D(int lineCount) { + // This calculation doesn't add the bevel join between + // the first and last vertex, need to fix. + int nvert = lineCount * 4 + (lineCount - 1); + int nind = lineCount * 2 * 3 + (lineCount - 1) * 2 * 3; + tess.lineVertexCheck(nvert); + tess.lineIndexCheck(nind); + int index = in.renderMode == RETAINED ? tess.lineIndexCache.addNew() : tess.lineIndexCache.getLast(); + firstLineIndexCache = index; + int i0 = in.firstVertex; + short[] lastInd = {-1, -1}; + for (int ln = 0; ln < lineCount - 1; ln++) { + int i1 = in.firstVertex + ln + 1; + index = addLine3D(i0, i1, index, lastInd, false); + i0 = i1; + } + index = addLine3D(in.lastVertex, in.firstVertex, index, lastInd, false); + lastLineIndexCache = index; + } + + void tessellateLineLoop2D(int lineCount) { + int nvert = lineCount * 4; + int nind = lineCount * 2 * 3; + + if (noCapsJoins(nvert)) { + tess.polyVertexCheck(nvert); + tess.polyIndexCheck(nind); + int index = in.renderMode == RETAINED ? tess.polyIndexCache.addNew() : tess.polyIndexCache.getLast(); + firstLineIndexCache = index; + if (firstPolyIndexCache == -1) firstPolyIndexCache = index; // If the geometry has no fill, needs the first poly index. + int i0 = in.firstVertex; + for (int ln = 0; ln < lineCount - 1; ln++) { + int i1 = in.firstVertex + ln + 1; + index = addLine2D(i0, i1, index, false); + i0 = i1; + } + index = addLine2D(in.lastVertex, in.firstVertex, index, false); + lastLineIndexCache = lastPolyIndexCache = index; + } else { // full stroking algorithm + int first = in.firstVertex; + LinePath path = new LinePath(LinePath.WIND_NON_ZERO); + path.moveTo(in.vertices[3 * first + 0], in.vertices[3 * first + 1]); + for (int ln = 0; ln < lineCount - 1; ln++) { + int i1 = first + ln + 1; + path.lineTo(in.vertices[3 * i1 + 0], in.vertices[3 * i1 + 1]); + } + path.closePath(); + tessellateLinePath(path); + } } void tessellateEdges() { - if (stroke) { - if (is3D()) { - int nInVert = in.getNumLineVertices(); - int nInInd = in.getNumLineIndices(); - - tess.lineVertexCheck(nInVert); - tess.lineIndexCheck(nInInd); - int index = in.renderMode == RETAINED ? tess.lineIndexCache.addNew() : tess.lineIndexCache.getLast(); - firstLineIndexCache = index; - for (int i = in.firstEdge; i <= in.lastEdge; i++) { - int[] edge = in.edges[i]; - index = addLine(edge[0], edge[1], index, true); - } - lastLineIndexCache = index; - -// NEW TESSMAP API -// if (tess.renderMode == RETAINED) { -// addLineMapping(in.firstVertex, in.lastVertex); -// } - } else { - // 2D renderer, the stroke geometry is stored in the fill array for accurate depth sorting - LinePath path = new LinePath(LinePath.WIND_NON_ZERO); - for (int i = in.firstEdge; i <= in.lastEdge; i++) { - int[] edge = in.edges[i]; - int i0 = edge[0]; - int i1 = edge[1]; - switch (edge[2]) { - case EDGE_MIDDLE: - path.lineTo(inGeo.vertices[4 * i1 + 0], inGeo.vertices[4 * i1 + 1]); - break; - case EDGE_START: - path.moveTo(inGeo.vertices[4 * i0 + 0], inGeo.vertices[4 * i0 + 1]); - path.lineTo(inGeo.vertices[4 * i1 + 0], inGeo.vertices[4 * i1 + 1]); - break; - case EDGE_STOP: - path.lineTo(inGeo.vertices[4 * i1 + 0], inGeo.vertices[4 * i1 + 1]); - path.closePath(); - break; - case EDGE_SINGLE: - path.moveTo(inGeo.vertices[4 * i0 + 0], inGeo.vertices[4 * i0 + 1]); - path.lineTo(inGeo.vertices[4 * i1 + 0], inGeo.vertices[4 * i1 + 1]); - path.closePath(); - break; - } - } - tessellateLinePath(path); + if (stroke) { + if (is3D) { + tessellateEdges3D(); + } else if (is2D) { + beginNoTex(); + tessellateEdges2D(); + endNoTex(); } } - - - -// tessGeo.firstLineIndex = tessGeo.fillIndexCount; -// tessGeo.addFillVertices(inGeo.getNumLineVertices()); -// tessGeo.addFillIndices(inGeo.getNumLineIndices()); -// tessGeo.lastLineIndex = tessGeo.fillIndexCount - 1; -// int vcount = tessGeo.firstFillVertex; -// int icount = tessGeo.firstFillIndex; -// for (int i = inGeo.firstEdge; i <= inGeo.lastEdge; i++) { -// int[] edge = inGeo.edges[i]; -// addLineToFill(edge[0], edge[1], vcount, icount); vcount += 4; icount += 6; -// } - - // Not using the fancy path tessellation in 2D because it slows down things - // significantly (it also calls the GLU tessellator). - // It generates the right caps and joins, though. - -// GeneralPath path = new GeneralPath(GeneralPath.WIND_NON_ZERO); -// for (int i = inGeo.firstEdge; i <= inGeo.lastEdge; i++) { -// int[] edge = inGeo.edges[i]; -// if (startEdge(edge[2])) path.moveTo(inGeo.getVertexX(edge[0]), inGeo.getVertexY(edge[0])); -// path.lineTo(inGeo.getVertexX(edge[1]), inGeo.getVertexY(edge[1])); -// if (endEdge(edge[2])) path.closePath(); -// } -// tessGeo.firstLineIndex = tessGeo.fillIndexCount; -// tessellatePath(path); -// tessGeo.lastLineIndex = tessGeo.fillIndexCount - 1; } - + + void tessellateEdges3D() { + // This calculation doesn't add the bevel join between + // the first and last vertex, need to fix. + int nInVert = in.getNumEdgeVertices(true); + int nInInd = in.getNumEdgeIndices(true); + + tess.lineVertexCheck(nInVert); + tess.lineIndexCheck(nInInd); + int index = in.renderMode == RETAINED ? tess.lineIndexCache.addNew() : tess.lineIndexCache.getLast(); + firstLineIndexCache = index; + short[] lastInd = {-1, -1}; + for (int i = in.firstEdge; i <= in.lastEdge; i++) { + int[] edge = in.edges[i]; + int i0 = edge[0]; + int i1 = edge[1]; + index = addLine3D(i0, i1, index, lastInd, true); + if (edge[2] == EDGE_STOP || edge[2] == EDGE_SINGLE) { + // No join with next line segment. + lastInd[0] = lastInd[1] = -1; + } + } + lastLineIndexCache = index; + } + + void tessellateEdges2D() { + int nInVert = in.getNumEdgeVertices(false); + if (noCapsJoins(nInVert)) { + int nInInd = in.getNumEdgeIndices(false); + + tess.polyVertexCheck(nInVert); + tess.polyIndexCheck(nInInd); + int index = in.renderMode == RETAINED ? tess.polyIndexCache.addNew() : tess.polyIndexCache.getLast(); + firstLineIndexCache = index; + if (firstPolyIndexCache == -1) firstPolyIndexCache = index; // If the geometry has no fill, needs the first poly index. + for (int i = in.firstEdge; i <= in.lastEdge; i++) { + int[] edge = in.edges[i]; + int i0 = edge[0]; + int i1 = edge[1]; + index = addLine2D(i0, i1, index, true); + } + lastLineIndexCache = lastPolyIndexCache = index; + } else { // full stroking algorithm + LinePath path = new LinePath(LinePath.WIND_NON_ZERO); + for (int i = in.firstEdge; i <= in.lastEdge; i++) { + int[] edge = in.edges[i]; + int i0 = edge[0]; + int i1 = edge[1]; + switch (edge[2]) { + case EDGE_MIDDLE: + path.lineTo(in.vertices[3 * i1 + 0], in.vertices[3 * i1 + 1]); + break; + case EDGE_START: + path.moveTo(in.vertices[3 * i0 + 0], in.vertices[3 * i0 + 1]); + path.lineTo(in.vertices[3 * i1 + 0], in.vertices[3 * i1 + 1]); + break; + case EDGE_STOP: + path.lineTo(in.vertices[3 * i1 + 0], in.vertices[3 * i1 + 1]); + path.closePath(); + break; + case EDGE_SINGLE: + path.moveTo(in.vertices[3 * i0 + 0], in.vertices[3 * i0 + 1]); + path.lineTo(in.vertices[3 * i1 + 0], in.vertices[3 * i1 + 1]); + path.closePath(); + break; + } + } + tessellateLinePath(path); + } + } + // Adding the data that defines a quad starting at vertex i0 and // ending at i1. - int addLine(int i0, int i1, int index, boolean constStroke) { + int addLine3D(int i0, int i1, int index, short[] lastInd, boolean constStroke) { IndexCache cache = tess.lineIndexCache; int count = cache.vertexCount[index]; + boolean addBevel = lastInd != null && -1 < lastInd[0] && -1 < lastInd[1]; + if (PGL.MAX_VERTEX_INDEX1 <= count + 4 + (addBevel ? 1 : 0)) { + // We need to start a new index block for this line. + index = cache.addNew(); + count = 0; + } + int iidx = cache.indexOffset[index] + cache.indexCount[index]; + int vidx = cache.vertexOffset[index] + cache.vertexCount[index]; + int color, color0; + float weight; + + color0 = color = constStroke ? strokeColor : in.strokeColors[i0]; + weight = constStroke ? strokeWeight : in.strokeWeights[i0]; + + tess.setLineVertex(vidx++, in, i0, i1, color, +weight/2); + tess.lineIndices[iidx++] = (short) (count + 0); + + tess.setLineVertex(vidx++, in, i0, i1, color, -weight/2); + tess.lineIndices[iidx++] = (short) (count + 1); + + color = constStroke ? strokeColor : in.strokeColors[i1]; + weight = constStroke ? strokeWeight : in.strokeWeights[i1]; + + tess.setLineVertex(vidx++, in, i1, i0, color, -weight/2); + tess.lineIndices[iidx++] = (short) (count + 2); + + // Starting a new triangle re-using prev vertices. + tess.lineIndices[iidx++] = (short) (count + 2); + tess.lineIndices[iidx++] = (short) (count + 1); + + tess.setLineVertex(vidx++, in, i1, i0, color, +weight/2); + tess.lineIndices[iidx++] = (short) (count + 3); + + cache.incCounts(index, 6, 4); + + if (lastInd != null) { + if (-1 < lastInd[0] && -1 < lastInd[1]) { + // Adding bevel triangles + tess.setLineVertex(vidx, in, i0, color0); + + tess.lineIndices[iidx++] = (short) (count + 4); + tess.lineIndices[iidx++] = lastInd[0]; + tess.lineIndices[iidx++] = (short) (count + 0); + + tess.lineIndices[iidx++] = (short) (count + 4); + tess.lineIndices[iidx++] = lastInd[1]; + tess.lineIndices[iidx ] = (short) (count + 1); + + cache.incCounts(index, 6, 1); + } + + // Vertices for next bevel + lastInd[0] = (short) (count + 2); + lastInd[1] = (short) (count + 3); + } + return index; + } + + // Adding the data that defines a quad starting at vertex i0 and + // ending at i1, in the case of pure 2D renderers (line geometry + // is added to the poly arrays). + int addLine2D(int i0, int i1, int index, boolean constStroke) { + IndexCache cache = tess.polyIndexCache; + int count = cache.vertexCount[index]; if (PGL.MAX_VERTEX_INDEX1 <= count + 4) { // We need to start a new index block for this line. index = cache.addNew(); @@ -9861,48 +9705,90 @@ public class PGraphicsOpenGL extends PGraphics { } int iidx = cache.indexOffset[index] + cache.indexCount[index]; int vidx = cache.vertexOffset[index] + cache.vertexCount[index]; - int color; - float weight; - color = constStroke ? strokeColor : in.scolors[i0]; - weight = constStroke ? strokeWeight : in.sweights[i0]; + int color = constStroke ? strokeColor : in.strokeColors[i0]; + float weight = constStroke ? strokeWeight : in.strokeWeights[i0]; - tess.putLineVertex(in, i0, i1, vidx, color, +weight/2); - tess.lineIndices[iidx++] = (short) (count + 0); + float x0 = in.vertices[3 * i0 + 0]; + float y0 = in.vertices[3 * i0 + 1]; - vidx++; - tess.putLineVertex(in, i0, i1, vidx, color, -weight/2); - tess.lineIndices[iidx++] = (short) (count + 1); + float x1 = in.vertices[3 * i1 + 0]; + float y1 = in.vertices[3 * i1 + 1]; + + // Calculating direction and normal of the line. + float dirx = x1 - x0; + float diry = y1 - y0; + float llen = PApplet.sqrt(dirx * dirx + diry * diry); + float normx = 0, normy = 0; + if (nonZero(llen)) { + normx = -diry / llen; + normy = +dirx / llen; + } - color = constStroke ? strokeColor : in.scolors[i1]; - weight = constStroke ? strokeWeight : in.sweights[i1]; + tess.setPolyVertex(vidx++, x0 + normx * weight/2, y0 + normy * weight/2, 0, color); + tess.polyIndices[iidx++] = (short) (count + 0); - vidx++; - tess.putLineVertex(in, i1, i0, vidx, color, -weight/2); - tess.lineIndices[iidx++] = (short) (count + 2); + tess.setPolyVertex(vidx++, x0 - normx * weight/2, y0 - normy * weight/2, 0, color); + tess.polyIndices[iidx++] = (short) (count + 1); + + if (!constStroke) { + color = in.strokeColors[i1]; + weight = in.strokeWeights[i1]; + } + + tess.setPolyVertex(vidx++, x1 - normx * weight/2, y1 - normy * weight/2, 0, color); + tess.polyIndices[iidx++] = (short) (count + 2); // Starting a new triangle re-using prev vertices. - tess.lineIndices[iidx++] = (short) (count + 2); - tess.lineIndices[iidx++] = (short) (count + 1); + tess.polyIndices[iidx++] = (short) (count + 2); + tess.polyIndices[iidx++] = (short) (count + 0); - vidx++; - tess.putLineVertex(in, i1, i0, vidx, color, +weight/2); - tess.lineIndices[iidx++] = (short) (count + 3); - -// NEW TESSMAP API -// if (tess.renderMode == RETAINED) { -// in.setLineMapping(i0, i1, vidx - 4); -// } + tess.setPolyVertex(vidx++, x1 + normx * weight/2, y1 + normy * weight/2, 0, color); + tess.polyIndices[iidx++] = (short) (count + 3); cache.incCounts(index, 6, 4); return index; + } + + boolean noCapsJoins(int nInVert) { + if (!accurate2DStrokes) { + return true; + } else if (PGL.MAX_CAPS_JOINS_LENGTH <= nInVert) { + // The line path is too long, so it could make the GLU tess + // to run out of memory, so full caps and joins are disabled. + return true; + } else { + // We first calculate the (volumetric) scaling factor that is associated + // to the current transformation matrix, which is given by the absolute + // value of its determinant: + float scaleFactor = 1; + + if (transform != null) { + if (transform instanceof PMatrix2D) { + PMatrix2D tr = (PMatrix2D)transform; + float areaScaleFactor = Math.abs(tr.m00 * tr.m11 - tr.m01 * tr.m10); + scaleFactor = (float) Math.sqrt(areaScaleFactor); + } else if (transform instanceof PMatrix3D) { + PMatrix3D tr = (PMatrix3D)transform; + float volumeScaleFactor = Math.abs(tr.m00 * (tr.m11 * tr.m22 - tr.m12 * tr.m21) + + tr.m01 * (tr.m12 * tr.m20 - tr.m10 * tr.m22) + + tr.m02 * (tr.m10 * tr.m21 - tr.m11 * tr.m20)); + scaleFactor = (float) Math.pow(volumeScaleFactor, 1.0f / 3.0f); + } + } + + // The stroke weight is scaled so it correspons to the current + // "zoom level" being applied on the geometry due to scaling: + return scaleFactor * strokeWeight < PGL.MIN_CAPS_JOINS_WEIGHT; + } } // ----------------------------------------------------------------- // - // Fill primitives tessellation + // Polygon primitives tessellation void tessellateTriangles() { + setFirstTexIndex(tess.polyIndexCount, tess.polyIndexCache.size - 1); int nInVert = in.lastVertex - in.firstVertex + 1; if (fill && 3 <= nInVert) { int nInInd = nInVert; @@ -9913,10 +9799,12 @@ public class PGraphicsOpenGL extends PGraphics { } partitionRawIndices(); } + setLastTexIndex(tess.lastPolyIndex, tess.polyIndexCache.size - 1); tessellateEdges(); } void tessellateTriangles(int[] indices) { + setFirstTexIndex(tess.polyIndexCount, tess.polyIndexCache.size - 1); int nInVert = in.lastVertex - in.firstVertex + 1; if (fill && 3 <= nInVert) { int nInInd = indices.length; @@ -9924,10 +9812,12 @@ public class PGraphicsOpenGL extends PGraphics { PApplet.arrayCopy(indices, rawIndices, nInInd); partitionRawIndices(); } + setLastTexIndex(tess.lastPolyIndex, tess.polyIndexCache.size - 1); tessellateEdges(); } void tessellateTriangleFan() { + setFirstTexIndex(tess.polyIndexCount, tess.polyIndexCache.size - 1); int nInVert = in.lastVertex - in.firstVertex + 1; if (fill && 3 <= nInVert) { int nInInd = 3 * (nInVert - 2); @@ -9940,10 +9830,12 @@ public class PGraphicsOpenGL extends PGraphics { } partitionRawIndices(); } + setLastTexIndex(tess.lastPolyIndex, tess.polyIndexCache.size - 1); tessellateEdges(); } void tessellateTriangleStrip() { + setFirstTexIndex(tess.polyIndexCount, tess.polyIndexCache.size - 1); int nInVert = in.lastVertex - in.firstVertex + 1; if (fill && 3 <= nInVert) { int nInInd = 3 * (nInVert - 2); @@ -9961,10 +9853,12 @@ public class PGraphicsOpenGL extends PGraphics { } partitionRawIndices(); } + setLastTexIndex(tess.lastPolyIndex, tess.polyIndexCache.size - 1); tessellateEdges(); } void tessellateQuads() { + setFirstTexIndex(tess.polyIndexCount, tess.polyIndexCache.size - 1); int nInVert = in.lastVertex - in.firstVertex + 1; if (fill && 4 <= nInVert) { int quadCount = nInVert / 4; @@ -9987,10 +9881,12 @@ public class PGraphicsOpenGL extends PGraphics { } partitionRawIndices(); } + setLastTexIndex(tess.lastPolyIndex, tess.polyIndexCache.size - 1); tessellateEdges(); } void tessellateQuadStrip() { + setFirstTexIndex(tess.polyIndexCount, tess.polyIndexCache.size - 1); int nInVert = in.lastVertex - in.firstVertex + 1; if (fill && 4 <= nInVert) { int quadCount = nInVert / 2 - 1; @@ -10013,6 +9909,7 @@ public class PGraphicsOpenGL extends PGraphics { } partitionRawIndices(); } + setLastTexIndex(tess.lastPolyIndex, tess.polyIndexCache.size - 1); tessellateEdges(); } @@ -10027,20 +9924,20 @@ public class PGraphicsOpenGL extends PGraphics { // these vertices need to be duplicated at the end of the corresponding // region in the vertex array. void partitionRawIndices() { - tess.fillIndexCheck(rawSize); - int offset = tess.firstFillIndex; + tess.polyIndexCheck(rawSize); + int offset = tess.firstPolyIndex; int inInd0 = 0, inInd1 = 0; int inMaxVert0 = in.firstVertex, inMaxVert1 = in.firstVertex; int inMaxRel = 0; Set inDupSet = null; - IndexCache cache = tess.fillIndexCache; + IndexCache cache = tess.polyIndexCache; // In retained mode, each shape has with its own cache item, since // they should always be available to be rendererd individually, even // if contained in a larger hierarchy. int index = in.renderMode == RETAINED ? cache.addNew() : cache.getLast(); - firstFillIndexCache = index; + firstPolyIndexCache = index; int trCount = rawSize / 3; for (int tr = 0; tr < trCount; tr++) { @@ -10074,9 +9971,9 @@ public class PGraphicsOpenGL extends PGraphics { ri2 = ii2; } else ri2 = count + ii2; - tess.fillIndices[offset + 3 * tr + 0] = (short) ri0; - tess.fillIndices[offset + 3 * tr + 1] = (short) ri1; - tess.fillIndices[offset + 3 * tr + 2] = (short) ri2; + tess.polyIndices[offset + 3 * tr + 0] = (short) ri0; + tess.polyIndices[offset + 3 * tr + 1] = (short) ri1; + tess.polyIndices[offset + 3 * tr + 2] = (short) ri2; inInd1 = 3 * tr + 2; inMaxVert1 = PApplet.max(i0, i1, i2); @@ -10091,7 +9988,7 @@ public class PGraphicsOpenGL extends PGraphics { // so we need to wrap-up things anyways. // So, copy vertices in current region first - tess.addFillVertices(in, inMaxVert0, inMaxVert1); + tess.addPolyVertices(in, inMaxVert0, inMaxVert1); if (0 < dup) { // Adjusting the negative indices so they correspond to vertices added @@ -10099,22 +9996,22 @@ public class PGraphicsOpenGL extends PGraphics { ArrayList inDupList = new ArrayList(inDupSet); Collections.sort(inDupList); for (int i = inInd0; i <= inInd1; i++) { - int ri = tess.fillIndices[i]; + int ri = tess.polyIndices[i]; if (ri < 0) { - tess.fillIndices[i] = (short) (inMaxRel + 1 + inDupList.indexOf(ri)); + tess.polyIndices[i] = (short) (inMaxRel + 1 + inDupList.indexOf(ri)); } } // Copy duplicated vertices from previous regions last for (int i = 0; i < inDupList.size(); i++) { int ri = inDupList.get(i); - tess.addFillVertex(in, ri + inMaxVert0); + tess.addPolyVertex(in, ri + inMaxVert0); } } // Increment counts: cache.incCounts(index, inInd1 - inInd0 + 1, inMaxVert1 - inMaxVert0 + 1 + dup); - lastFillIndexCache = index; + lastPolyIndexCache = index; index = -1; inMaxRel = 0; @@ -10140,23 +10037,47 @@ public class PGraphicsOpenGL extends PGraphics { rawIndices = temp; } + void setFirstTexIndex(int firstIndex, int firstCache) { + if (texCache != null) { + firstTexIndex = firstIndex; + firstTexCache = PApplet.max(0, firstCache); + } + } + + void setLastTexIndex(int lastIndex, int lastCache) { + if (texCache != null) { + if (prevTexImage != newTexImage || texCache.size == 0) { + texCache.addTexture(newTexImage, firstTexIndex, firstTexCache, lastIndex, lastCache); + } else { + texCache.setLastIndex(lastIndex, lastCache); + } + } + } + + void beginNoTex() { + prevTexImage = newTexImage; + newTexImage = null; + setFirstTexIndex(tess.polyIndexCount, tess.polyIndexCache.size - 1); + } + + void endNoTex() { + setLastTexIndex(tess.lastPolyIndex, tess.polyIndexCache.size - 1); + } + // ----------------------------------------------------------------- // // Polygon tessellation void tessellatePolygon(boolean solid, boolean closed, boolean calcNormals) { + setFirstTexIndex(tess.polyIndexCount, tess.polyIndexCache.size - 1); + int nInVert = in.lastVertex - in.firstVertex + 1; -// NEW TESSMAP API -// if (tess.renderMode == RETAINED) { -// in.addWeightedMapping(in.firstVertex, in.lastVertex); -// } - - firstFillIndexCache = Integer.MAX_VALUE; - - callback.calcNormals = calcNormals; - if (fill && 3 <= nInVert) { + firstPolyIndexCache = -1; + + callback.init(in.renderMode == RETAINED, false, calcNormals); + gluTess.beginPolygon(); if (solid) { @@ -10199,12 +10120,11 @@ public class PGraphicsOpenGL extends PGraphics { int eb = (in.emissive[i] >> 0) & 0xFF; // Vertex data includes coordinates, colors, normals, texture coordinates, and material properties. - double[] vertex = new double[] { in.vertices [4 * i + 0], in.vertices [4 * i + 1], in.vertices[4 * i + 2], + double[] vertex = new double[] { in.vertices [3 * i + 0], in.vertices [3 * i + 1], in.vertices[3 * i + 2], fa, fr, fg, fb, in.normals [3 * i + 0], in.normals [3 * i + 1], in.normals [3 * i + 2], in.texcoords[2 * i + 0], in.texcoords[2 * i + 1], - aa, ar, ag, ab, sa, sr, sg, sb, ea, er, eg, eb, - in.shininess[i], i, 1.0 }; + aa, ar, ag, ab, sa, sr, sg, sb, ea, er, eg, eb, in.shininess[i]}; gluTess.addVertex(vertex); } @@ -10213,15 +10133,15 @@ public class PGraphicsOpenGL extends PGraphics { gluTess.endPolygon(); } + setLastTexIndex(tess.lastPolyIndex, tess.polyIndexCache.size - 1); tessellateEdges(); } // Tessellates the path given as parameter. This will work only in 2D. // Based on the opengl stroke hack described here: // http://wiki.processing.org/w/Stroke_attributes_in_OpenGL - public void tessellateLinePath(LinePath path) { - firstFillIndexCache = Integer.MAX_VALUE; - callback.calcNormals = true; + public void tessellateLinePath(LinePath path) { + callback.init(in.renderMode == RETAINED, true, false); int cap = strokeCap == ROUND ? LinePath.CAP_ROUND : strokeCap == PROJECT ? LinePath.CAP_SQUARE : @@ -10271,8 +10191,7 @@ public class PGraphicsOpenGL extends PGraphics { sa, sr, sg, sb, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1.0 }; // what about i!!!!!!!!!!! + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; gluTess.addVertex(vertex); @@ -10301,17 +10220,30 @@ public class PGraphicsOpenGL extends PGraphics { // to eventually come up with an optimized GLU tessellator in native code. protected class TessellatorCallback implements PGL.TessellatorCallback { boolean calcNormals; + boolean strokeTess; IndexCache cache; int cacheIndex; int vertFirst; int vertCount; int primitive; + public void init(boolean addCache, boolean strokeTess, boolean calcNorm) { + this.strokeTess = strokeTess; + this.calcNormals = calcNorm; + + cache = tess.polyIndexCache; + if (addCache) { + cache.addNew(); + } + } + public void begin(int type) { - cache = tess.fillIndexCache; - cacheIndex = in.renderMode == RETAINED ? cache.addNew() : cache.getLast(); - if (cacheIndex < firstFillIndexCache) { - firstFillIndexCache = cacheIndex; + cacheIndex = cache.getLast(); + if (firstPolyIndexCache == -1) { + firstPolyIndexCache = cacheIndex; + } + if (strokeTess && firstLineIndexCache == -1) { + firstLineIndexCache = cacheIndex; } vertFirst = cache.vertexCount[cacheIndex]; @@ -10386,16 +10318,19 @@ public class PGraphicsOpenGL extends PGraphics { } cache.incCounts(cacheIndex, indCount, vertCount); - lastFillIndexCache = cacheIndex; + lastPolyIndexCache = cacheIndex; + if (strokeTess) { + lastLineIndexCache = cacheIndex; + } } protected void addIndex(int tessIdx) { - tess.fillIndexCheck(); - tess.fillIndices[tess.fillIndexCount - 1] = (short) (vertFirst + tessIdx); + tess.polyIndexCheck(); + tess.polyIndices[tess.polyIndexCount - 1] = (short) (vertFirst + tessIdx); } protected void calcTriNormal(int tessIdx0, int tessIdx1, int tessIdx2) { - tess.calcFillNormal(vertFirst + tessIdx0, vertFirst + tessIdx1, vertFirst + tessIdx2); + tess.calcPolyNormal(vertFirst + tessIdx0, vertFirst + tessIdx1, vertFirst + tessIdx2); } public void vertex(Object data) { @@ -10407,32 +10342,18 @@ public class PGraphicsOpenGL extends PGraphics { } if (vertCount < PGL.MAX_VERTEX_INDEX1) { - // Combining individual rgba components back into int color values int fcolor = ((int) d[ 3] << 24) | ((int) d[ 4] << 16) | ((int) d[ 5] << 8) | (int) d[ 6]; int acolor = ((int) d[12] << 24) | ((int) d[13] << 16) | ((int) d[14] << 8) | (int) d[15]; int scolor = ((int) d[16] << 24) | ((int) d[17] << 16) | ((int) d[18] << 8) | (int) d[19]; int ecolor = ((int) d[20] << 24) | ((int) d[21] << 16) | ((int) d[22] << 8) | (int) d[23]; - int[] vertices = null; - float[] weights = null; - int nvert = (l - 25) / 2; - if (0 < nvert) { - vertices = new int[nvert]; - weights = new float[nvert]; - for (int n = 0; n < nvert; n++) { - vertices[n] = (int) d[25 + 2 * n + 0]; - weights[n] = (float) d[25 + 2 * n + 1]; - } - } - - tess.addFillVertex((float) d[ 0], (float) d[ 1], (float) d[ 2], 1, + tess.addPolyVertex((float) d[ 0], (float) d[ 1], (float) d[ 2], fcolor, (float) d[ 7], (float) d[ 8], (float) d[ 9], (float) d[10], (float) d[11], acolor, scolor, ecolor, - (float) d[24], - in, vertices, weights); + (float) d[24]); vertCount++; } else { @@ -10482,16 +10403,6 @@ public class PGraphicsOpenGL extends PGraphics { } } - // Adding the indices and weights of the 4 input vertices - // used to construct this combined vertex. - for (int j = 0; j < 4; j++) { - double[] vertData = (double[])data[j]; - if (vertData != null) { - vertex[25 + 2 * j + 0] = vertData[25]; - vertex[25 + 2 * j + 1] = weight[j]; - } - } - // Normalizing normal vector, since the weighted // combination of normal vectors is not necessarily // normal. diff --git a/android/core/src/processing/opengl/PShader.java b/android/core/src/processing/opengl/PShader.java index 54b6c2282..42c951eba 100644 --- a/android/core/src/processing/opengl/PShader.java +++ b/android/core/src/processing/opengl/PShader.java @@ -3,12 +3,12 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2011 Andres Colubri - Copyright (c) 2010 Ben Fry and Casey Reas + Copyright (c) 2011-12 Ben Fry and Casey Reas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public - License version 2.1 as published by the Free Software Foundation. + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -19,17 +19,14 @@ Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ +*/ package processing.opengl; +import processing.core.*; import java.io.IOException; import java.net.URL; -import processing.core.PApplet; -import processing.core.PGraphics; - - /** * This class encapsulates a GLSL shader program, including a vertex * and a fragment shader. Originally based in the code by JohnG @@ -54,6 +51,8 @@ public class PShader { protected int vertexShader; protected int fragmentShader; + protected boolean active; + public PShader() { parent = null; pg = null; @@ -67,7 +66,9 @@ public class PShader { programObject = 0; vertexShader = 0; - fragmentShader = 0; + fragmentShader = 0; + + active = false; } public PShader(PApplet parent) { @@ -154,6 +155,7 @@ public class PShader { public void start() { init(); pgl.glUseProgram(programObject); + active = true; } @@ -162,9 +164,18 @@ public class PShader { */ public void stop() { pgl.glUseProgram(0); + active = false; } + /** + * Returns true if the shader is running, false otherwise. + */ + public boolean active() { + return active; + } + + /** * Returns the ID location of the attribute parameter given its name. * diff --git a/android/core/src/processing/opengl/PShape2D.java b/android/core/src/processing/opengl/PShape2D.java new file mode 100644 index 000000000..2d32cb2ec --- /dev/null +++ b/android/core/src/processing/opengl/PShape2D.java @@ -0,0 +1,171 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2012 Ben Fry and Casey Reas + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, version 2.1. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.opengl; + +import processing.core.PApplet; +import processing.core.PGraphics; +import processing.core.PShape; + +public class PShape2D extends PShapeOpenGL { + + public PShape2D(PApplet parent, int family) { + super(parent, family); + } + + public boolean is2D() { + return true; + } + + public boolean is3D() { + return false; + } + + + //////////////////////////////////////////////////////////////////////// + // + // Shape copy + + + static public PShape2D createShape(PApplet parent, PShape src) { + PShape2D dest = null; + if (src.getFamily() == GROUP) { + dest = PGraphics2D.createShapeImpl(parent, GROUP); + PShape2D.copyGroup(parent, src, dest); + } else if (src.getFamily() == PRIMITIVE) { + dest = PGraphics2D.createShapeImpl(parent, src.getKind(), src.getParams()); + PShape.copyPrimitive(src, dest); + } else if (src.getFamily() == GEOMETRY) { + dest = PGraphics2D.createShapeImpl(parent, src.getKind()); + PShape.copyGeometry(src, dest); + } else if (src.getFamily() == PATH) { + dest = PGraphics2D.createShapeImpl(parent, PATH); + PShape.copyPath(src, dest); + } + dest.setName(src.getName()); + return dest; + } + + + static public void copyGroup(PApplet parent, PShape src, PShape dest) { + copyMatrix(src, dest); + copyStyles(src, dest); + copyImage(src, dest); + + for (int i = 0; i < src.getChildCount(); i++) { + PShape c = PShape2D.createShape(parent, src.getChild(i)); + dest.addChild(c); + } + } + + + /////////////////////////////////////////////////////////// + + // + + // Drawing methods + + public void vertex(float x, float y, float z) { + PGraphics.showDepthWarningXYZ("vertex"); + } + + public void vertex(float x, float y, float z, float u, float v) { + PGraphics.showDepthWarningXYZ("vertex"); + } + + /////////////////////////////////////////////////////////// + + // + + // Bezier curves + + public void bezierVertex(float x2, float y2, float z2, + float x3, float y3, float z3, + float x4, float y4, float z4) { + PGraphics.showDepthWarningXYZ("bezierVertex"); + } + + public void quadraticVertex(float x2, float y2, float z2, + float x4, float y4, float z4) { + PGraphics.showDepthWarningXYZ("quadVertex"); + } + + public void curveVertex(float x, float y, float z) { + PGraphics.showDepthWarningXYZ("curveVertex"); + } + + /////////////////////////////////////////////////////////// + + // + + // Geometric transformations + + public void translate(float tx, float ty, float tz) { + PGraphics.showVariationWarning("translate"); + } + + public void rotateX(float angle) { + PGraphics.showDepthWarning("rotateX"); + } + + public void rotateY(float angle) { + PGraphics.showDepthWarning("rotateY"); + } + + public void rotateZ(float angle) { + PGraphics.showDepthWarning("rotateZ"); + } + + public void rotate(float angle, float vx, float vy, float vz) { + PGraphics.showVariationWarning("rotate"); + } + + public void applyMatrix(float n00, float n01, float n02, float n03, + float n10, float n11, float n12, float n13, + float n20, float n21, float n22, float n23, + float n30, float n31, float n32, float n33) { + PGraphics.showVariationWarning("applyMatrix"); + } + + public void scale(float sx, float sy, float sz) { + PGraphics.showDepthWarningXYZ("scale"); + } + + /////////////////////////////////////////////////////////// + + // + + // Setters/getters of individual vertices + + public float getVertexZ(int index) { + PGraphics.showDepthWarningXYZ("getVertexZ"); + return 0; + } + + public void setVertex(int index, float x, float y) { + super.setVertex(index, x, y, 0); + } + + public void setVertex(int index, float x, float y, float z) { + PGraphics.showDepthWarningXYZ("setVertex"); + } +} diff --git a/android/core/src/processing/opengl/PShape3D.java b/android/core/src/processing/opengl/PShape3D.java index 0ffb18e4c..e46f23ed0 100644 --- a/android/core/src/processing/opengl/PShape3D.java +++ b/android/core/src/processing/opengl/PShape3D.java @@ -3,11 +3,11 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2010 Ben Fry and Casey Reas + Copyright (c) 2012 Ben Fry and Casey Reas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public - License version 2.1 as published by the Free Software Foundation. + License as published by the Free Software Foundation, version 2.1. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -22,4215 +22,58 @@ package processing.opengl; -import processing.core.*; -import processing.opengl.PGraphicsOpenGL.*; +import processing.core.PApplet; +import processing.core.PShape; -import java.nio.FloatBuffer; -import java.nio.IntBuffer; -import java.nio.ShortBuffer; -import java.util.Arrays; -import java.util.HashSet; +public class PShape3D extends PShapeOpenGL { - -/** - * This class holds a 3D model composed of vertices, normals, colors (per vertex) and - * texture coordinates (also per vertex). All this data is stored in Vertex Buffer Objects - * (VBO) in GPU memory for very fast access. - * OBJ loading implemented using code from Saito's OBJLoader library (http://code.google.com/p/saitoobjloader/) - * and OBJReader from Ahmet Kizilay (http://www.openprocessing.org/visuals/?visualID=191). - * By Andres Colubri - * - * - * Other formats to consider: - * AMF: http://en.wikipedia.org/wiki/Additive_Manufacturing_File_Format - * STL: http://en.wikipedia.org/wiki/STL_(file_format) - * OFF: http://people.sc.fsu.edu/~jburkardt/data/off/off.html(file_format) - * DXF: http://en.wikipedia.org/wiki/AutoCAD_DXF - */ -public class PShape3D extends PShape { - static protected final int TRANSLATE = 0; - static protected final int ROTATE = 1; - static protected final int SCALE = 2; - static protected final int MATRIX = 3; - - protected PGraphicsOpenGL pg; - protected PGL pgl; - protected PGL.Context context; // The context that created this shape. - - protected PShape3D root; - - // ........................................................ - - // Input, tessellated geometry - - protected InGeometry inGeo; - protected TessGeometry tessGeo; - protected Tessellator tessellator; - - // ........................................................ - - // Texturing - - protected HashSet textures; - protected PImage texture; - - // ........................................................ - - // OpenGL buffers - - public int glFillVertexBufferID; - public int glFillColorBufferID; - public int glFillNormalBufferID; - public int glFillTexCoordBufferID; - public int glFillAmbientBufferID; - public int glFillSpecularBufferID; - public int glFillEmissiveBufferID; - public int glFillShininessBufferID; - public int glFillIndexBufferID; - - public int glLineVertexBufferID; - public int glLineColorBufferID; - public int glLineDirWidthBufferID; - public int glLineIndexBufferID; - - public int glPointVertexBufferID; - public int glPointColorBufferID; - public int glPointSizeBufferID; - public int glPointIndexBufferID; - - // ........................................................ - - // Offsets for geometry aggregation and update. - - protected int fillVertCopyOffset; - protected int fillIndCopyOffset; - protected int lineVertCopyOffset; - protected int lineIndCopyOffset; - protected int pointVertCopyOffset; - protected int pointIndCopyOffset; - - protected int fillIndexOffset; - protected int fillVertexOffset; - protected int fillVertexAbs; - protected int fillVertexRel; - - protected int lineIndexOffset; - protected int lineVertexOffset; - protected int lineVertexAbs; - protected int lineVertexRel; - - protected int pointIndexOffset; - protected int pointVertexOffset; - protected int pointVertexAbs; - protected int pointVertexRel; - - int firstPointIndexCache; - int lastPointIndexCache; - int firstLineIndexCache; - int lastLineIndexCache; - int firstFillIndexCache; - int lastFillIndexCache; - - protected int firstFillVertex; - protected int lastFillVertex; - protected int firstLineVertex; - protected int lastLineVertex; - protected int firstPointVertex; - protected int lastPointVertex; - - // ........................................................ - - // Geometric transformations. - - protected PMatrix transform; - - // ........................................................ - - // State/rendering flags - - protected boolean tessellated; - protected boolean needBufferInit; - - protected boolean isSolid; - protected boolean isClosed; - - protected boolean openContour = false; - protected boolean breakShape = false; - protected boolean shapeEnded = false; - - protected boolean haveFill; - protected boolean haveLines; - protected boolean havePoints; - - // ........................................................ - - // Modes inherited from renderer - - protected int textureMode; - protected int rectMode; - protected int ellipseMode; - protected int shapeMode; - protected int imageMode; - - // ........................................................ - - // Bezier and Catmull-Rom curves - - protected int bezierDetail = 20; - protected int curveDetail = 20; - protected float curveTightness = 0; - - // ........................................................ - - // Normals - - protected float normalX, normalY, normalZ; - - // normal calculated per triangle - static protected final int NORMAL_MODE_AUTO = 0; - // one normal manually specified per shape - static protected final int NORMAL_MODE_SHAPE = 1; - // normals specified for each shape vertex - static protected final int NORMAL_MODE_VERTEX = 2; - - // Current mode for normals, one of AUTO, SHAPE, or VERTEX - protected int normalMode; - - // ........................................................ - - // Modification variables (used only by the root shape) - - protected boolean modified; - - protected boolean modifiedFillVertices; - protected boolean modifiedFillColors; - protected boolean modifiedFillNormals; - protected boolean modifiedFillTexCoords; - protected boolean modifiedFillAmbient; - protected boolean modifiedFillSpecular; - protected boolean modifiedFillEmissive; - protected boolean modifiedFillShininess; - - protected boolean modifiedLineVertices; - protected boolean modifiedLineColors; - protected boolean modifiedLineAttributes; - - protected boolean modifiedPointVertices; - protected boolean modifiedPointColors; - protected boolean modifiedPointAttributes; - - protected int firstModifiedFillVertex; - protected int lastModifiedFillVertex; - protected int firstModifiedFillColor; - protected int lastModifiedFillColor; - protected int firstModifiedFillNormal; - protected int lastModifiedFillNormal; - protected int firstModifiedFillTexCoord; - protected int lastModifiedFillTexCoord; - protected int firstModifiedFillAmbient; - protected int lastModifiedFillAmbient; - protected int firstModifiedFillSpecular; - protected int lastModifiedFillSpecular; - protected int firstModifiedFillEmissive; - protected int lastModifiedFillEmissive; - protected int firstModifiedFillShininess; - protected int lastModifiedFillShininess; - - protected int firstModifiedLineVertex; - protected int lastModifiedLineVertex; - protected int firstModifiedLineColor; - protected int lastModifiedLineColor; - protected int firstModifiedLineAttribute; - protected int lastModifiedLineAttribute; - - protected int firstModifiedPointVertex; - protected int lastModifiedPointVertex; - protected int firstModifiedPointColor; - protected int lastModifiedPointColor; - protected int firstModifiedPointAttribute; - protected int lastModifiedPointAttribute; - - public PShape3D(PApplet parent, int family) { - pg = (PGraphicsOpenGL)parent.g; - pgl = pg.pgl; - context = pgl.createEmptyContext(); - - glFillVertexBufferID = 0; - glFillColorBufferID = 0; - glFillNormalBufferID = 0; - glFillTexCoordBufferID = 0; - glFillAmbientBufferID = 0; - glFillSpecularBufferID = 0; - glFillEmissiveBufferID = 0; - glFillShininessBufferID = 0; - glFillIndexBufferID = 0; - - glLineVertexBufferID = 0; - glLineColorBufferID = 0; - glLineDirWidthBufferID = 0; - glLineIndexBufferID = 0; - - glPointVertexBufferID = 0; - glPointColorBufferID = 0; - glPointSizeBufferID = 0; - glPointIndexBufferID = 0; - - this.tessellator = PGraphicsOpenGL.tessellator; - this.family = family; - this.root = this; - this.parent = null; - this.tessellated = false; - - if (family == GEOMETRY || family == PRIMITIVE || family == PATH) { - inGeo = pg.newInGeometry(PGraphicsOpenGL.RETAINED); - } - - // Modes are retrieved from the current values in the renderer. - textureMode = pg.textureMode; - rectMode = pg.rectMode; - ellipseMode = pg.ellipseMode; - shapeMode = pg.shapeMode; - imageMode = pg.imageMode; - - colorMode(pg.colorMode, pg.colorModeX, pg.colorModeY, pg.colorModeZ, pg.colorModeA); - - // Initial values for fill, stroke and tint colors are also imported from the renderer. - // This is particular relevant for primitive shapes, since is not possible to set - // their color separately when creating them, and their input vertices are actually - // generated at rendering time, by which the color configuration of the renderer might - // have changed. - fill = pg.fill; - fillColor = pg.fillColor; - - stroke = pg.stroke; - strokeColor = pg.strokeColor; - strokeWeight = pg.strokeWeight; - - tint = pg.tint; - tintColor = pg.tintColor; - - ambientColor = pg.ambientColor; - specularColor = pg.specularColor; - emissiveColor = pg.emissiveColor; - shininess = pg.shininess; - - normalX = normalY = 0; - normalZ = 1; - - normalMode = NORMAL_MODE_AUTO; - - if (family == GROUP) { - // GROUP shapes are always marked as ended. - shapeEnded = true; - } - } - - - public void addChild(PShape child) { - if (child instanceof PShape3D) { - if (family == GROUP) { - PShape3D c3d = (PShape3D)child; - - super.addChild(c3d); - c3d.updateRoot(root); - root.tessellated = false; - tessellated = false; - - if (c3d.texture != null) { - addTexture(c3d.texture); - } - } else { - PGraphics.showWarning("Cannot add child shape to non-group shape."); - } - } else { - PGraphics.showWarning("Shape must be 3D to be added to the group."); - } - } - - - public void updateRoot(PShape root) { - this.root = (PShape3D) root; - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D)children[i]; - child.updateRoot(root); - } - } - } - - - protected void finalize() throws Throwable { - try { - finalizeFillBuffers(); - finalizeLineBuffers(); - finalizePointBuffers(); - } finally { - super.finalize(); - } - } - - - protected void finalizeFillBuffers() { - if (glFillVertexBufferID != 0) { - pg.finalizeVertexBufferObject(glFillVertexBufferID, context.code()); - } - - if (glFillColorBufferID != 0) { - pg.finalizeVertexBufferObject(glFillColorBufferID, context.code()); - } - - if (glFillNormalBufferID != 0) { - pg.finalizeVertexBufferObject(glFillNormalBufferID, context.code()); - } - - if (glFillTexCoordBufferID != 0) { - pg.finalizeVertexBufferObject(glFillTexCoordBufferID, context.code()); - } - - if (glFillAmbientBufferID != 0) { - pg.finalizeVertexBufferObject(glFillAmbientBufferID, context.code()); - } - - if (glFillSpecularBufferID != 0) { - pg.finalizeVertexBufferObject(glFillSpecularBufferID, context.code()); - } - - if (glFillEmissiveBufferID != 0) { - pg.finalizeVertexBufferObject(glFillEmissiveBufferID, context.code()); - } - - if (glFillShininessBufferID != 0) { - pg.finalizeVertexBufferObject(glFillShininessBufferID, context.code()); - } - - if (glFillIndexBufferID != 0) { - pg.finalizeVertexBufferObject(glFillIndexBufferID, context.code()); - } - } - - - protected void finalizeLineBuffers() { - if (glLineVertexBufferID != 0) { - pg.finalizeVertexBufferObject(glLineVertexBufferID, context.code()); - } - - if (glLineColorBufferID != 0) { - pg.finalizeVertexBufferObject(glLineColorBufferID, context.code()); - } - - if (glLineDirWidthBufferID != 0) { - pg.finalizeVertexBufferObject(glLineDirWidthBufferID, context.code()); - } - - if (glLineIndexBufferID != 0) { - pg.finalizeVertexBufferObject(glLineIndexBufferID, context.code()); - } - } - - - protected void finalizePointBuffers() { - if (glPointVertexBufferID != 0) { - pg.finalizeVertexBufferObject(glPointVertexBufferID, context.code()); - } - - if (glPointColorBufferID != 0) { - pg.finalizeVertexBufferObject(glPointColorBufferID, context.code()); - } - - if (glPointSizeBufferID != 0) { - pg.finalizeVertexBufferObject(glPointSizeBufferID, context.code()); - } - - if (glPointIndexBufferID != 0) { - pg.finalizeVertexBufferObject(glPointIndexBufferID, context.code()); - } - } - - - /////////////////////////////////////////////////////////// - - // - - // Query methods - - - public float getWidth() { - PVector min = new PVector(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY); - PVector max = new PVector(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY); - if (shapeEnded) { - getVertexMin(min); - getVertexMax(max); - } - width = max.x - min.x; - return width; - } - - - public float getHeight() { - PVector min = new PVector(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY); - PVector max = new PVector(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY); - if (shapeEnded) { - getVertexMin(min); - getVertexMax(max); - } - height = max.y - min.y; - return height; - } - - - public float getDepth() { - PVector min = new PVector(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY); - PVector max = new PVector(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY); - if (shapeEnded) { - getVertexMin(min); - getVertexMax(max); - } - depth = max.z - min.z; - return depth; - } - - - public PVector getCenter() { - PVector center = new PVector(); - int count = 0; - if (shapeEnded) { - count = getVertexSum(center, count); - if (0 < count) { - center.x /= count; - center.y /= count; - center.z /= count; - } - } - return center; - } - - - protected void getVertexMin(PVector min) { - updateTessellation(); - - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.getVertexMin(min); - } - } else { - if (haveFill) tessGeo.getFillVertexMin(min, firstFillVertex, lastFillVertex); - if (haveLines) tessGeo.getLineVertexMin(min, firstLineVertex, lastLineVertex); - if (havePoints) tessGeo.getPointVertexMin(min, firstPointVertex, lastPointVertex); - } - } - - - protected void getVertexMax(PVector max) { - updateTessellation(); - - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.getVertexMax(max); - } - } else { - if (haveFill) tessGeo.getFillVertexMax(max, firstFillVertex, lastFillVertex); - if (haveLines) tessGeo.getLineVertexMax(max, firstLineVertex, lastLineVertex); - if (havePoints) tessGeo.getPointVertexMax(max, firstPointVertex, lastPointVertex); - } - } - - - protected int getVertexSum(PVector sum, int count) { - updateTessellation(); - - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - count += child.getVertexSum(sum, count); - } - } else { - if (haveFill) count += tessGeo.getFillVertexSum(sum, firstFillVertex, lastFillVertex); - if (haveLines) count += tessGeo.getLineVertexSum(sum, firstLineVertex, lastLineVertex); - if (havePoints) count += tessGeo.getPointVertexSum(sum, firstPointVertex, lastPointVertex); - } - return count; - } - - - /////////////////////////////////////////////////////////// - - // - - // Drawing methods - - - public void textureMode(int mode) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.textureMode(mode); - } - } else { - textureMode = mode; - } - } - - - public void texture(PImage tex) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.texture(tex); - } - } else { - PImage tex0 = texture; - texture = tex; - if (tex0 != tex && parent != null) { - ((PShape3D)parent).removeTexture(tex); - } - if (parent != null) { - ((PShape3D)parent).addTexture(texture); - } - } - } - - - public void noTexture() { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.noTexture(); - } - } else { - PImage tex0 = texture; - texture = null; - if (tex0 != null && parent != null) { - ((PShape3D)parent).removeTexture(tex0); - } - } - } - - - protected void addTexture(PImage tex) { - if (textures == null) { - textures = new HashSet(); - } - textures.add(tex); - if (parent != null) { - ((PShape3D)parent).addTexture(tex); - } - } - - - protected void removeTexture(PImage tex) { - if (textures != null) { - boolean childHasIt = false; - - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - if (child.hasTexture(tex)) { - childHasIt = true; - break; - } - } - - if (!childHasIt) { - textures.remove(tex); - if (textures.size() == 0) { - textures = null; - } - } - } - } - - - protected boolean hasTexture(PImage tex) { - if (family == GROUP) { - return textures != null && textures.contains(tex); - } else { - return texture == tex; - } - } - - - public void solid(boolean solid) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.solid(solid); - } - } else { - isSolid = solid; - } - } - - - public void beginContour() { - if (family == GROUP) { - PGraphics.showWarning("Cannot begin contour in GROUP shapes"); - return; - } - - if (openContour) { - PGraphics.showWarning("P3D: Already called beginContour()."); - return; - } - openContour = true; - } - - - public void endContour() { - if (family == GROUP) { - PGraphics.showWarning("Cannot end contour in GROUP shapes"); - return; - } - - if (!openContour) { - PGraphics.showWarning("P3D: Need to call beginContour() first."); - return; - } - openContour = false; - breakShape = true; - } - - - public void vertex(float x, float y) { - vertex(x, y, 0, 0, 0); - } - - - public void vertex(float x, float y, float u, float v) { - vertex(x, y, 0, u, v); - } - - - public void vertex(float x, float y, float z) { - vertex(x, y, z, 0, 0); - } - - - public void vertex(float x, float y, float z, float u, float v) { - vertexImpl(x, y, z, u, v); - } - - - protected void vertexImpl(float x, float y, float z, float u, float v) { - if (family == GROUP) { - PGraphics.showWarning("Cannot add vertices to GROUP shape"); - return; - } - - boolean textured = texture != null; - int fcolor = 0x00; - if (fill || textured) { - if (!textured) { - fcolor = fillColor; - } else { - if (tint) { - fcolor = tintColor; - } else { - fcolor = 0xffFFFFFF; - } - } - } - - if (texture != null && textureMode == IMAGE) { - u = PApplet.min(1, u / texture.width); - v = PApplet.min(1, v / texture.height); - } - - int scolor = 0x00; - float sweight = 0; - if (stroke) { - scolor = strokeColor; - sweight = strokeWeight; - } - - inGeo.addVertex(x, y, z, - fcolor, - normalX, normalY, normalZ, - u, v, - scolor, sweight, - ambientColor, specularColor, emissiveColor, shininess, - vertexCode()); - - root.tessellated = false; - tessellated = false; - } - - - protected int vertexCode() { - int code = VERTEX; - if (breakShape) { - code = BREAK; - breakShape = false; - } - return code; - } - - - public void normal(float nx, float ny, float nz) { - if (family == GROUP) { - PGraphics.showWarning("Cannot set normal in GROUP shape"); - return; - } - - normalX = nx; - normalY = ny; - normalZ = nz; - - // if drawing a shape and the normal hasn't been set yet, - // then we need to set the normals for each vertex so far - if (normalMode == NORMAL_MODE_AUTO) { - // One normal per begin/end shape - normalMode = NORMAL_MODE_SHAPE; - } else if (normalMode == NORMAL_MODE_SHAPE) { - // a separate normal for each vertex - normalMode = NORMAL_MODE_VERTEX; - } - } - - - public void end() { - end(OPEN); - } - - - public void end(int mode) { - if (family == GROUP) { - PGraphics.showWarning("Cannot end GROUP shape"); - return; - } - - // Input arrays are trimmed since they are expanded by doubling their old size, - // which might lead to arrays larger than the vertex counts. - inGeo.trim(); - - isClosed = mode == CLOSE; - root.tessellated = false; - tessellated = false; - shapeEnded = true; - } - - - public void setParams(float[] source) { - if (family != PRIMITIVE) { - PGraphics.showWarning("Parameters can only be set to PRIMITIVE shapes"); - return; - } - - super.setParams(source); - root.tessellated = false; - tessellated = false; - shapeEnded = true; - } - - - public void setPath(int vcount, float[][] verts, int ccount, int[] codes) { - if (family != PATH) { - PGraphics.showWarning("Vertex coordinates and codes can only be set to PATH shapes"); - return; - } - - super.setPath(vcount, verts, ccount, codes); - root.tessellated = false; - tessellated = false; - shapeEnded = true; - } - - - ////////////////////////////////////////////////////////////// - - // Stroke cap/join/weight set/update - - - public void strokeWeight(float weight) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.strokeWeight(weight); - } - } else { - float prevStrokeWeight = strokeWeight; - strokeWeight = weight; - updateStrokeWeight(prevStrokeWeight); - } - } - - - protected void updateStrokeWeight(float prevStrokeWeight) { - if (shapeEnded && tessellated && (haveLines || havePoints)) { - float resizeFactor = strokeWeight / prevStrokeWeight; - Arrays.fill(inGeo.sweights, 0, inGeo.vertexCount, strokeWeight); - - if (haveLines) { - for (int i = firstLineVertex; i <= lastLineVertex; i++) { - tessGeo.lineDirWidths[4 * i + 3] *= resizeFactor; - } - root.setModifiedLineAttributes(firstLineVertex, lastLineVertex); - } - - - if (havePoints) { - for (int i = firstPointVertex; i <= lastPointVertex; i++) { - tessGeo.pointSizes[2 * i + 0] *= resizeFactor; - tessGeo.pointSizes[2 * i + 1] *= resizeFactor; - } - root.setModifiedPointAttributes(firstPointVertex, lastPointVertex); - } - } - } - - - public void strokeJoin(int join) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.strokeJoin(join); - } - } else { - strokeJoin = join; - } - } - - - public void strokeCap(int cap) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.strokeCap(cap); - } - } else { - strokeCap = cap; - } - } - - - ////////////////////////////////////////////////////////////// - - // Fill set/update - - - public void noFill() { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.noFill(); - } - } else { - fill = false; - fillColor = 0x0; - updateFillColor(); - } - } - - - public void fill(int rgb) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.fill(rgb); - } - } else { - colorCalc(rgb); - fillFromCalc(); - } - } - - - public void fill(int rgb, float alpha) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.fill(rgb, alpha); - } - } else { - colorCalc(rgb, alpha); - fillFromCalc(); - } - } - - - public void fill(float gray) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.fill(gray); - } - } else { - colorCalc(gray); - fillFromCalc(); - } - } - - - public void fill(float gray, float alpha) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.fill(gray, alpha); - } - } else { - colorCalc(gray, alpha); - fillFromCalc(); - } - } - - - public void fill(float x, float y, float z) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.fill(x, y, z); - } - } else { - colorCalc(x, y, z); - fillFromCalc(); - } - } - - - public void fill(float x, float y, float z, float a) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.fill(x, y, z, a); - } - } else { - colorCalc(x, y, z, a); - fillFromCalc(); - } - } - - - protected void fillFromCalc() { - fill = true; - fillColor = calcColor; - updateFillColor(); - } - - - protected void updateFillColor() { - if (shapeEnded && tessellated && haveFill && texture == null) { - Arrays.fill(inGeo.colors, 0, inGeo.vertexCount, PGL.javaToNativeARGB(fillColor)); - Arrays.fill(tessGeo.fillColors, firstFillVertex, lastFillVertex + 1, PGL.javaToNativeARGB(fillColor)); - root.setModifiedFillColors(firstFillVertex, lastFillVertex); - } - } - - - ////////////////////////////////////////////////////////////// - - // Stroke (color) set/update - - - public void noStroke() { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.noStroke(); - } - } else { - stroke = false; - strokeColor = 0x0; - updateStrokeColor(); - } - } - - - public void stroke(int rgb) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.stroke(rgb); - } - } else { - colorCalc(rgb); - strokeFromCalc(); - } - } - - - public void stroke(int rgb, float alpha) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.stroke(rgb, alpha); - } - } else { - colorCalc(rgb, alpha); - strokeFromCalc(); - } - } - - - public void stroke(float gray) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.stroke(gray); - } - } else { - colorCalc(gray); - strokeFromCalc(); - } - } - - - public void stroke(float gray, float alpha) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.stroke(gray, alpha); - } - } else { - colorCalc(gray, alpha); - strokeFromCalc(); - } - } - - - public void stroke(float x, float y, float z) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.stroke(x, y, z); - } - } else { - colorCalc(x, y, z); - strokeFromCalc(); - } - } - - - public void stroke(float x, float y, float z, float alpha) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.stroke(x, y, z, alpha); - } - } else { - colorCalc(x, y, z, alpha); - strokeFromCalc(); - } - } - - - protected void strokeFromCalc() { - stroke = true; - strokeColor = calcColor; - updateStrokeColor(); - } - - - protected void updateStrokeColor() { - if (shapeEnded && tessellated && (haveLines || havePoints)) { - Arrays.fill(inGeo.scolors, 0, inGeo.vertexCount, PGL.javaToNativeARGB(strokeColor)); - if (haveLines) { - Arrays.fill(tessGeo.lineColors, firstLineVertex, lastLineVertex + 1, PGL.javaToNativeARGB(strokeColor)); - root.setModifiedLineColors(firstLineVertex, lastLineVertex); - } - if (havePoints) { - Arrays.fill(tessGeo.pointColors, firstPointVertex, lastPointVertex + 1, PGL.javaToNativeARGB(strokeColor)); - root.setModifiedPointColors(firstPointVertex, lastPointVertex); - } - } - } - - - ////////////////////////////////////////////////////////////// - - // Tint set/update - - - public void noTint() { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.noTint(); - } - } else { - tint = false; - tintColor = 0x0; - updateTintColor(); - } - } - - - public void tint(int rgb) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.tint(rgb); - } - } else { - colorCalc(rgb); - tintFromCalc(); - } - } - - - public void tint(int rgb, float alpha) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.tint(rgb, alpha); - } - } else { - colorCalc(rgb, alpha); - tintFromCalc(); - } - } - - - public void tint(float gray) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.tint(gray); - } - } else { - colorCalc(gray); - tintFromCalc(); - } - } - - - public void tint(float gray, float alpha) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.tint(gray, alpha); - } - } else { - colorCalc(gray, alpha); - tintFromCalc(); - } - } - - - public void tint(float x, float y, float z) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.tint(x, y, z); - } - } else { - colorCalc(x, y, z); - tintFromCalc(); - } - } - - - public void tint(float x, float y, float z, float alpha) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.tint(x, y, z, alpha); - } - } else { - colorCalc(x, y, z, alpha); - tintFromCalc(); - } - } - - - protected void tintFromCalc() { - tint = true; - tintColor = calcColor; - updateTintColor(); - } - - - protected void updateTintColor() { - if (shapeEnded && tessellated && haveFill && texture != null) { - Arrays.fill(inGeo.colors, 0, inGeo.vertexCount, PGL.javaToNativeARGB(tintColor)); - Arrays.fill(tessGeo.fillColors, firstFillVertex, lastFillVertex + 1, PGL.javaToNativeARGB(tintColor)); - root.setModifiedFillColors(firstFillVertex, lastFillVertex); - } - } - - - ////////////////////////////////////////////////////////////// - - // Ambient set/update - - - public void ambient(int rgb) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.ambient(rgb); - } - } else { - colorCalc(rgb); - ambientFromCalc(); - } - } - - - public void ambient(float gray) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.ambient(gray); - } - } else { - colorCalc(gray); - ambientFromCalc(); - } - } - - - public void ambient(float x, float y, float z) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.ambient(x, y, z); - } - } else { - colorCalc(x, y, z); - ambientFromCalc(); - } - } - - - protected void ambientFromCalc() { - ambientColor = calcColor; - updateAmbientColor(); - } - - - protected void updateAmbientColor() { - if (shapeEnded && tessellated && haveFill) { - Arrays.fill(inGeo.ambient, 0, inGeo.vertexCount, PGL.javaToNativeARGB(ambientColor)); - Arrays.fill(tessGeo.fillAmbient, firstFillVertex, lastFillVertex = 1, PGL.javaToNativeARGB(ambientColor)); - root.setModifiedFillAmbient(firstFillVertex, lastFillVertex); - } - } - - - ////////////////////////////////////////////////////////////// - - // Specular set/update - - - public void specular(int rgb) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.specular(rgb); - } - } else { - colorCalc(rgb); - specularFromCalc(); - } - } - - - public void specular(float gray) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.specular(gray); - } - } else { - colorCalc(gray); - specularFromCalc(); - } - } - - - public void specular(float x, float y, float z) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.specular(x, y, z); - } - } else { - colorCalc(x, y, z); - specularFromCalc(); - } - } - - - protected void specularFromCalc() { - specularColor = calcColor; - updateSpecularColor(); - } - - - protected void updateSpecularColor() { - if (shapeEnded && tessellated && haveFill) { - Arrays.fill(inGeo.specular, 0, inGeo.vertexCount, PGL.javaToNativeARGB(specularColor)); - Arrays.fill(tessGeo.fillSpecular, firstFillVertex, lastFillVertex + 1, PGL.javaToNativeARGB(specularColor)); - root.setModifiedFillSpecular(firstFillVertex, lastFillVertex); - } - } - - - ////////////////////////////////////////////////////////////// - - // Emissive set/update - - - public void emissive(int rgb) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.emissive(rgb); - } - } else { - colorCalc(rgb); - emissiveFromCalc(); - } - } - - - public void emissive(float gray) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.emissive(gray); - } - } else { - colorCalc(gray); - emissiveFromCalc(); - } - } - - - public void emissive(float x, float y, float z) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.emissive(x, y, z); - } - } else { - colorCalc(x, y, z); - emissiveFromCalc(); - } - } - - - protected void emissiveFromCalc() { - emissiveColor = calcColor; - updateEmissiveColor(); - } - - - protected void updateEmissiveColor() { - if (shapeEnded && tessellated && 0 < tessGeo.fillVertexCount) { - Arrays.fill(inGeo.emissive, 0, inGeo.vertexCount, PGL.javaToNativeARGB(emissiveColor)); - Arrays.fill(tessGeo.fillEmissive, firstFillVertex, lastFillVertex + 1, PGL.javaToNativeARGB(emissiveColor)); - root.setModifiedFillEmissive(firstFillVertex, lastFillVertex); - } - } - - - ////////////////////////////////////////////////////////////// - - // Shininess set/update - - - public void shininess(float shine) { - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.shininess(shine); - } - } else { - shininess = shine; - updateShininessFactor(); - } - } - - - protected void updateShininessFactor() { - if (shapeEnded && tessellated && haveFill) { - Arrays.fill(inGeo.shininess, 0, inGeo.vertexCount, shininess); - Arrays.fill(tessGeo.fillShininess, firstFillVertex, lastFillVertex + 1, shininess); - root.setModifiedFillShininess(firstFillVertex, lastFillVertex); - } - } - - - /////////////////////////////////////////////////////////// - - // - - // Geometric transformations - - - public void translate(float tx, float ty) { - transform(TRANSLATE, tx, ty); - } - - - public void translate(float tx, float ty, float tz) { - transform(TRANSLATE, tx, ty, tz); - } - - - public void rotate(float angle) { - transform(ROTATE, angle); - } - - - public void rotate(float angle, float v0, float v1, float v2) { - transform(ROTATE, angle, v0, v1, v2); + super(parent, family); } - - public void scale(float s) { - transform(SCALE, s, s); - } - - - public void scale(float x, float y) { - transform(SCALE, x, y); - } - - - public void scale(float x, float y, float z) { - transform(SCALE, x, y, z); - } - - - public void applyMatrix(float n00, float n01, float n02, - float n10, float n11, float n12) { - transform(MATRIX, n00, n01, n02, - n10, n11, n12); + public boolean is2D() { + return false; } - - public void applyMatrix(float n00, float n01, float n02, float n03, - float n10, float n11, float n12, float n13, - float n20, float n21, float n22, float n23, - float n30, float n31, float n32, float n33) { - transform(MATRIX, n00, n01, n02, n03, - n10, n11, n12, n13, - n20, n21, n22, n23, - n30, n31, n32, n33); - } - - - public void resetMatrix() { - if (shapeEnded && matrix != null) { - if (family == GROUP) { - updateTessellation(); - } - boolean res = matrix.invert(); - if (res) { - if (tessellated) { - applyMatrixImpl(matrix); - } - matrix = null; - } else { - PGraphics.showWarning("The transformation matrix cannot be inverted"); - } - } - } - - - protected void transform(int type, float... args) { - int dimensions; - if (type == ROTATE) { - dimensions = args.length == 1 ? 2 : 3; - } else if (type == MATRIX) { - dimensions = args.length == 6 ? 2 : 3; - } else { - dimensions = args.length; - } - transformImpl(type, dimensions, args); - } - - - protected void transformImpl(int type, int ncoords, float... args) { - if (shapeEnded) { - if (family == GROUP) { - updateTessellation(); - // The tessellation is not updated for geometry/primitive shapes - // because a common situation is shapes not still tessellated - // but being transformed before adding them to the parent group - // shape. If each shape is tessellated individually, then the process - // is significantly slower than tessellating all the geometry in a single - // batch when calling tessellate() on the root shape. - } - checkMatrix(ncoords); - calcTransform(type, ncoords, args); - if (tessellated) { - applyMatrixImpl(transform); - } - } - } - - - protected void calcTransform(int type, int dimensions, float... args) { - if (transform == null) { - if (dimensions == 2) { - transform = new PMatrix2D(); - } else { - transform = new PMatrix3D(); - } - } else { - transform.reset(); - } - - switch (type) { - case TRANSLATE: - if (dimensions == 3) { - transform.translate(args[0], args[1], args[2]); - } else { - transform.translate(args[0], args[1]); - } - break; - case ROTATE: - if (dimensions == 3) { - transform.rotate(args[0], args[1], args[2], args[3]); - } else { - transform.rotate(args[0]); - } - break; - case SCALE: - if (dimensions == 3) { - transform.scale(args[0], args[1], args[2]); - } else { - transform.scale(args[0], args[1]); - } - break; - case MATRIX: - if (dimensions == 3) { - transform.set(args[ 0], args[ 1], args[ 2], args[ 3], - args[ 4], args[ 5], args[ 6], args[ 7], - args[ 8], args[ 9], args[10], args[11], - args[12], args[13], args[14], args[15]); - } else { - transform.set(args[0], args[1], args[2], - args[3], args[4], args[5]); - } - break; - } - matrix.apply(transform); - } - - - protected void applyMatrixImpl(PMatrix matrix) { - if (haveFill) { - tessGeo.applyMatrixOnFillGeometry(matrix, firstFillVertex, lastFillVertex); - root.setModifiedFillVertices(firstFillVertex, lastFillVertex); - root.setModifiedFillNormals(firstFillVertex, lastFillVertex); - } - - if (haveLines) { - tessGeo.applyMatrixOnLineGeometry(matrix, firstLineVertex, lastLineVertex); - root.setModifiedLineVertices(firstLineVertex, lastLineVertex); - root.setModifiedLineAttributes(firstLineVertex, lastLineVertex); - } - - if (havePoints) { - tessGeo.applyMatrixOnPointGeometry(matrix, firstPointVertex, lastPointVertex); - root.setModifiedPointVertices(firstPointVertex, lastPointVertex); - } - } - - - /////////////////////////////////////////////////////////// - - // - - // Bezier curves - - - public void bezierDetail(int detail) { - bezierDetail = detail; - pg.bezierDetail(detail); - } - - - public void bezierVertex(float x2, float y2, - float x3, float y3, - float x4, float y4) { - bezierVertex(x2, y2, 0, - x3, y3, 0, - x4, y4, 0); - } - - - public void bezierVertex(float x2, float y2, float z2, - float x3, float y3, float z3, - float x4, float y4, float z4) { - inGeo.setMaterial(fillColor, strokeColor, strokeWeight, - ambientColor, specularColor, emissiveColor, shininess); - inGeo.setNormal(normalX, normalY, normalZ); - inGeo.addBezierVertex(x2, y2, z2, - x3, y3, z3, - x4, y4, z4, - fill, stroke, bezierDetail, vertexCode(), kind); - } - - - public void quadraticVertex(float cx, float cy, - float x3, float y3) { - quadraticVertex(cx, cy, 0, - x3, y3, 0); - } - - - public void quadraticVertex(float cx, float cy, float cz, - float x3, float y3, float z3) { - inGeo.setMaterial(fillColor, strokeColor, strokeWeight, - ambientColor, specularColor, emissiveColor, shininess); - inGeo.setNormal(normalX, normalY, normalZ); - inGeo.addQuadraticVertex(cx, cy, cz, - x3, y3, z3, - fill, stroke, bezierDetail, vertexCode(), kind); - } - - - /////////////////////////////////////////////////////////// - - // - - // Catmull-Rom curves - - - public void curveDetail(int detail) { - curveDetail = detail; - pg.curveDetail(detail); - } - - - public void curveTightness(float tightness) { - curveTightness = tightness; - pg.curveTightness(tightness); - } - - - public void curveVertex(float x, float y) { - curveVertex(x, y, 0); - } - - - public void curveVertex(float x, float y, float z) { - inGeo.setMaterial(fillColor, strokeColor, strokeWeight, - ambientColor, specularColor, emissiveColor, shininess); - inGeo.setNormal(normalX, normalY, normalZ); - inGeo.addCurveVertex(x, y, z, - fill, stroke, curveDetail, vertexCode(), kind); - } - - - /////////////////////////////////////////////////////////// - - // - - // Setters/getters of individual vertices - - - public int getVertexCount() { - return inGeo.vertexCount; - } - - - public PVector getVertex(int index, PVector vec) { - updateTessellation(); - - if (vec == null) { - vec = new PVector(); - } - vec.x = inGeo.vertices[4 * index + 0]; - vec.y = inGeo.vertices[4 * index + 1]; - vec.z = inGeo.vertices[4 * index + 2]; - return vec; - } - - - public float getVertexX(int index) { - updateTessellation(); - - return inGeo.vertices[4 * index + 0]; - } - - - public float getVertexY(int index) { - updateTessellation(); - - return inGeo.vertices[4 * index + 1]; - } - - - public float getVertexZ(int index) { - updateTessellation(); - - return inGeo.vertices[4 * index + 2]; - } - - - public void setVertex(int index, float x, float y) { - setVertex(index, x, y, 0); - } - - - public void setVertex(int index, float x, float y, float z) { - updateTessellation(); - - int[] indices; - int[] indices1; - float[] vertices; - float[] attribs; - - if (havePoints) { - indices = inGeo.tessMap.pointIndices[index]; - vertices = tessGeo.pointVertices; - for (int i = 0; i < indices.length; i++) { - int tessIdx = indices[i]; - vertices[4 * tessIdx + 0] = x; - vertices[4 * tessIdx + 1] = y; - vertices[4 * tessIdx + 2] = z; - root.setModifiedPointVertices(tessIdx, tessIdx); - } - } - - if (haveLines) { - indices = inGeo.tessMap.lineIndices0[index]; - indices1 = inGeo.tessMap.lineIndices1[index]; - vertices = tessGeo.lineVertices; - attribs = tessGeo.lineDirWidths; - for (int i = 0; i < indices.length; i++) { - int tessIdx = indices[i]; - vertices[4 * tessIdx + 0] = x; - vertices[4 * tessIdx + 1] = y; - vertices[4 * tessIdx + 2] = z; - root.setModifiedLineVertices(tessIdx, tessIdx); - - int tessIdx1 = indices1[i]; - attribs[4 * tessIdx1 + 0] = x; - attribs[4 * tessIdx1 + 1] = y; - attribs[4 * tessIdx1 + 2] = z; - root.setModifiedLineAttributes(tessIdx1, tessIdx1); - } - } - - if (haveFill) { - vertices = tessGeo.fillVertices; - if (-1 < inGeo.tessMap.firstFillIndex) { - // 1-1 mapping, only need to offset the input index - int tessIdx = inGeo.tessMap.firstFillIndex + index; - vertices[4 * tessIdx + 0] = x; - vertices[4 * tessIdx + 1] = y; - vertices[4 * tessIdx + 2] = z; - root.setModifiedFillVertices(tessIdx, tessIdx); - } else { - // Multi-valued mapping. Going through all the tess - // vertices affected by inIdx. - float x0 = inGeo.vertices[4 * index + 0]; - float y0 = inGeo.vertices[4 * index + 1]; - float z0 = inGeo.vertices[4 * index + 2]; - indices = inGeo.tessMap.fillIndices[index]; - float[] weights = inGeo.tessMap.fillWeights[index]; - for (int i = 0; i < indices.length; i++) { - // tessIdx is a linear combination of input vertices, - // including inIdx: - // tessVert[tessIdx] = SUM(i from I, inVert[i]), inIdx in I - // For example: - // xt = w0 * x0 + w1 * x1 + w2 * x2 - // If x2 changes from x2 to x2', then the new value of xt is: - // xt' = w0 * x0 + w1 * x1 + w2 * x2' = - // = w0 * x0 + w1 * x1 + w2 * x2' + w2 * x2 - w2 * x2 - // = xt + w2 * (x2' - x2) - // This explains the calculations below: - int tessIdx = indices[i]; - float weight = weights[i]; - float tx0 = vertices[4 * tessIdx + 0]; - float ty0 = vertices[4 * tessIdx + 1]; - float tz0 = vertices[4 * tessIdx + 2]; - vertices[4 * tessIdx + 0] = tx0 + weight * (x - x0); - vertices[4 * tessIdx + 1] = ty0 + weight * (y - y0); - vertices[4 * tessIdx + 2] = tz0 + weight * (z - z0); - root.setModifiedFillVertices(tessIdx, tessIdx); - } - } - } - - inGeo.vertices[4 * index + 0] = x; - inGeo.vertices[4 * index + 1] = y; - inGeo.vertices[4 * index + 2] = z; - } - - - public PVector getNormal(int index, PVector vec) { - updateTessellation(); - - if (vec == null) { - vec = new PVector(); - } - vec.x = inGeo.normals[3 * index + 0]; - vec.y = inGeo.normals[3 * index + 1]; - vec.z = inGeo.normals[3 * index + 2]; - return vec; - } - - - public float getNormalX(int index) { - updateTessellation(); - - return inGeo.normals[3 * index + 0]; - } - - - public float getNormalY(int index) { - updateTessellation(); - - return inGeo.normals[3 * index + 1]; - } - - - public float getNormalZ(int index) { - updateTessellation(); - - return inGeo.normals[3 * index + 2]; - } - - - public void setNormal(int index, float nx, float ny, float nz) { - updateTessellation(); - - if (haveFill) { - float[] normals = tessGeo.fillNormals; - - if (-1 < inGeo.tessMap.firstFillIndex) { - int tessIdx = inGeo.tessMap.firstFillIndex + index; - normals[3 * tessIdx + 0] = nx; - normals[3 * tessIdx + 1] = ny; - normals[3 * tessIdx + 2] = nz; - root.setModifiedFillNormals(tessIdx, tessIdx); - } else { - float nx0 = inGeo.normals[3 * index + 0]; - float ny0 = inGeo.normals[3 * index + 1]; - float nz0 = inGeo.normals[3 * index + 2]; - int[] indices = inGeo.tessMap.fillIndices[index]; - float[] weights = inGeo.tessMap.fillWeights[index]; - for (int i = 0; i < indices.length; i++) { - int tessIdx = indices[i]; - float weight = weights[i]; - float tnx0 = normals[3 * tessIdx + 0]; - float tny0 = normals[3 * tessIdx + 1]; - float tnz0 = normals[3 * tessIdx + 2]; - float tnx = tnx0 + weight * (nx - nx0); - float tny = tny0 + weight * (ny - ny0); - float tnz = tnz0 + weight * (nz - nz0); - - // Making sure that the new normal vector is indeed - // normalized. - float sum = tnx * tnx + tny * tny + tnz * tnz; - float len = PApplet.sqrt(sum); - tnx /= len; - tny /= len; - tnz /= len; - - normals[3 * tessIdx + 0] = tnx; - normals[3 * tessIdx + 1] = tny; - normals[3 * tessIdx + 2] = tnz; - root.setModifiedFillNormals(tessIdx, tessIdx); - } - } - } - - inGeo.normals[3 * index + 0] = nx; - inGeo.normals[3 * index + 1] = ny; - inGeo.normals[3 * index + 2] = nz; - } - - - public float getTextureU(int index) { - updateTessellation(); - - return inGeo.texcoords[2 * index + 0]; - } - - - public float getTextureV(int index) { - updateTessellation(); - - return inGeo.texcoords[2 * index + 1]; - } - - - public void setTextureUV(int index, float u, float v) { - updateTessellation(); - - if (haveFill) { - float[] texcoords = tessGeo.fillTexcoords; - - if (-1 < inGeo.tessMap.firstFillIndex) { - int tessIdx = inGeo.tessMap.firstFillIndex + index; - tessGeo.fillTexcoords[2 * tessIdx + 0] = u; - tessGeo.fillTexcoords[2 * tessIdx + 1] = v; - root.setModifiedFillTexcoords(tessIdx, tessIdx); - } else { - float u0 = inGeo.texcoords[2 * index + 0]; - float v0 = inGeo.texcoords[2 * index + 1]; - int[] indices = inGeo.tessMap.fillIndices[index]; - float[] weights = inGeo.tessMap.fillWeights[index]; - for (int i = 0; i < indices.length; i++) { - int tessIdx = indices[i]; - float weight = weights[i]; - float tu0 = texcoords[2 * tessIdx + 0]; - float tv0 = texcoords[2 * tessIdx + 1]; - float tu = tu0 + weight * (u - u0); - float tv = tv0 + weight * (v - v0); - texcoords[2 * tessIdx + 0] = tu; - texcoords[2 * tessIdx + 1] = tv; - root.setModifiedFillTexcoords(tessIdx, tessIdx); - } - } - } - - inGeo.texcoords[2 * index + 0] = u; - inGeo.texcoords[2 * index + 1] = v; - } - - - public int getFill(int index) { - updateTessellation(); - - return PGL.nativeToJavaARGB(inGeo.colors[index]); - } - - - public void setFill(int index, int fill) { - updateTessellation(); - - fill = PGL.javaToNativeARGB(fill); - - if (haveFill) { - int[] colors = tessGeo.fillColors; - - if (-1 < inGeo.tessMap.firstFillIndex) { - int tessIdx = inGeo.tessMap.firstFillIndex + index; - colors[tessIdx] = fill; - root.setModifiedFillColors(tessIdx, tessIdx); - } else { - int[] indices = inGeo.tessMap.fillIndices[index]; - float[] weights = inGeo.tessMap.fillWeights[index]; - int fill0 = inGeo.colors[index]; - setColorARGB(colors, fill, fill0, indices, weights); - for (int i = 0; i < indices.length; i++) { - root.setModifiedFillColors(indices[i], indices[i]); - } - } - } - - inGeo.colors[index] = fill; - } - - - public int getStroke(int index) { - updateTessellation(); - - return PGL.nativeToJavaARGB(inGeo.scolors[index]); - } - - - public void setStroke(int index, int stroke) { - updateTessellation(); - - stroke = PGL.javaToNativeARGB(stroke); - - if (havePoints) { - int[] indices = inGeo.tessMap.pointIndices[index]; - int[] colors = tessGeo.pointColors; - for (int i = 0; i < indices.length; i++) { - int tessIdx = indices[i]; - colors[tessIdx] = stroke; - root.setModifiedPointColors(tessIdx, tessIdx); - } - } - - if (haveLines) { - int[] colors = tessGeo.lineColors; - int[] indices = inGeo.tessMap.lineIndices0[index]; - for (int i = 0; i < indices.length; i++) { - int tessIdx = indices[i]; - colors[tessIdx] = stroke; - root.setModifiedLineColors(tessIdx, tessIdx); - } - indices = inGeo.tessMap.lineIndices1[index]; - for (int i = 0; i < indices.length; i++) { - int tessIdx = indices[i]; - colors[tessIdx] = stroke; - root.setModifiedLineColors(tessIdx, tessIdx); - } - } - - inGeo.scolors[index] = stroke; - } - - - public float getStrokeWeight(int index) { - updateTessellation(); - - return inGeo.sweights[index]; - } - - - public void setStrokeWeight(int index, float weight) { - updateTessellation(); - - if (havePoints || haveLines) { - int[] indices; - float[] attribs; - - float weight0 = inGeo.sweights[index]; - float resizeFactor = weight / weight0; - - if (havePoints) { - indices = inGeo.tessMap.pointIndices[index]; - attribs = tessGeo.pointSizes; - for (int i = 0; i < indices.length; i++) { - int tessIdx = indices[i]; - attribs[2 * tessIdx + 0] *= resizeFactor; - attribs[2 * tessIdx + 1] *= resizeFactor; - root.setModifiedPointAttributes(tessIdx, tessIdx); - } - } - - if (haveLines) { - attribs = tessGeo.lineDirWidths; - indices = inGeo.tessMap.lineIndices0[index]; - for (int i = 0; i < indices.length; i++) { - int tessIdx = indices[i]; - attribs[4 * tessIdx + 3] *= resizeFactor; - root.setModifiedLineAttributes(tessIdx, tessIdx); - } - indices = inGeo.tessMap.lineIndices1[index]; - for (int i = 0; i < indices.length; i++) { - int tessIdx = indices[i]; - attribs[4 * tessIdx + 3] *= resizeFactor; - root.setModifiedLineAttributes(tessIdx, tessIdx); - } - } - } - - inGeo.sweights[index] = weight; - } - - - public int getAmbient(int index) { - updateTessellation(); - - return PGL.nativeToJavaARGB(inGeo.ambient[index]); - } - - - public void setAmbient(int index, int ambient) { - updateTessellation(); - - ambient = PGL.javaToNativeARGB(ambient); - - if (haveFill) { - int[] colors = tessGeo.fillAmbient; - - if (-1 < inGeo.tessMap.firstFillIndex) { - int tessIdx = inGeo.tessMap.firstFillIndex + index; - colors[tessIdx] = ambient; - root.setModifiedFillAmbient(tessIdx, tessIdx); - } else { - int[] indices = inGeo.tessMap.fillIndices[index]; - float[] weights = inGeo.tessMap.fillWeights[index]; - int ambient0 = inGeo.ambient[index]; - setColorRGB(colors, ambient, ambient0, indices, weights); - for (int i = 0; i < indices.length; i++) { - root.setModifiedFillAmbient(indices[i], indices[i]); - } - } - } - - inGeo.ambient[index] = ambient; - } - - public int getSpecular(int index) { - updateTessellation(); - - return PGL.nativeToJavaARGB(inGeo.specular[index]); - } - - - public void setSpecular(int index, int specular) { - updateTessellation(); - - specular = PGL.javaToNativeARGB(specular); - - if (haveFill) { - int[] colors = tessGeo.fillSpecular; - - if (-1 < inGeo.tessMap.firstFillIndex) { - int tessIdx = inGeo.tessMap.firstFillIndex + index; - colors[tessIdx] = specular; - root.setModifiedFillSpecular(tessIdx, tessIdx); - } else { - int[] indices = inGeo.tessMap.fillIndices[index]; - float[] weights = inGeo.tessMap.fillWeights[index]; - int specular0 = inGeo.specular[index]; - setColorRGB(colors, specular, specular0, indices, weights); - for (int i = 0; i < indices.length; i++) { - root.setModifiedFillSpecular(indices[i], indices[i]); - } - } - } - - inGeo.specular[index] = specular; - } - - - public int getEmissive(int index) { - updateTessellation(); - - return PGL.nativeToJavaARGB(inGeo.emissive[index]); - } - - - public void setEmissive(int index, int emissive) { - updateTessellation(); - - emissive = PGL.javaToNativeARGB(emissive); - - if (haveFill) { - int[] colors = tessGeo.fillEmissive; - - if (-1 < inGeo.tessMap.firstFillIndex) { - int tessIdx = inGeo.tessMap.firstFillIndex + index; - colors[tessIdx] = emissive; - root.setModifiedFillEmissive(tessIdx, tessIdx); - } else { - int[] indices = inGeo.tessMap.fillIndices[index]; - float[] weights = inGeo.tessMap.fillWeights[index]; - int emissive0 = inGeo.emissive[index]; - setColorRGB(colors, emissive, emissive0, indices, weights); - for (int i = 0; i < indices.length; i++) { - root.setModifiedFillEmissive(indices[i], indices[i]); - } - } - } - - inGeo.emissive[index] = emissive; - } - - - public float getShininess(int index) { - updateTessellation(); - - return inGeo.shininess[index]; - } - - - public void setShininess(int index, float shine) { - updateTessellation(); - - if (haveFill) { - float[] shininess = tessGeo.fillShininess; - - if (-1 < inGeo.tessMap.firstFillIndex) { - int tessIdx = inGeo.tessMap.firstFillIndex + index; - shininess[tessIdx] = shine; - root.setModifiedFillShininess(tessIdx, tessIdx); - } else { - float shine0 = inGeo.shininess[index]; - int[] indices = inGeo.tessMap.fillIndices[index]; - float[] weights = inGeo.tessMap.fillWeights[index]; - for (int i = 0; i < indices.length; i++) { - int tessIdx = indices[i]; - float weight = weights[i]; - float tshine0 = shininess[tessIdx]; - float tshine = tshine0 + weight * (shine - shine0); - shininess[tessIdx] = tshine; - root.setModifiedFillShininess(tessIdx, tessIdx); - } - } - } - - inGeo.shininess[index] = shine; - } - - - protected void setColorARGB(int[] colors, int fill, int fill0, int[] indices, float[] weights) { - float a = (fill >> 24) & 0xFF; - float r = (fill >> 16) & 0xFF; - float g = (fill >> 8) & 0xFF; - float b = (fill >> 0) & 0xFF; - - float a0 = (fill0 >> 24) & 0xFF; - float r0 = (fill0 >> 16) & 0xFF; - float g0 = (fill0 >> 8) & 0xFF; - float b0 = (fill0 >> 0) & 0xFF; - - for (int i = 0; i < indices.length; i++) { - int tessIdx = indices[i]; - float weight = weights[i]; - int tfill0 = colors[tessIdx]; - float ta0 = (tfill0 >> 24) & 0xFF; - float tr0 = (tfill0 >> 16) & 0xFF; - float tg0 = (tfill0 >> 8) & 0xFF; - float tb0 = (tfill0 >> 0) & 0xFF; - - int ta = (int) (ta0 + weight * (a - a0)); - int tr = (int) (tr0 + weight * (r - r0)); - int tg = (int) (tg0 + weight * (g - g0)); - int tb = (int) (tb0 + weight * (b - b0)); - - colors[tessIdx] = (ta << 24) | (tr << 16) | (tg << 8) | tb; - } - } - - - protected void setColorRGB(int[] colors, int fill, int fill0, int[] indices, float[] weights) { - float r = (fill >> 16) & 0xFF; - float g = (fill >> 8) & 0xFF; - float b = (fill >> 0) & 0xFF; - - float r0 = (fill0 >> 16) & 0xFF; - float g0 = (fill0 >> 8) & 0xFF; - float b0 = (fill0 >> 0) & 0xFF; - - for (int i = 0; i < indices.length; i++) { - int tessIdx = indices[i]; - float weight = weights[i]; - int tfill0 = colors[tessIdx]; - float tr0 = (tfill0 >> 16) & 0xFF; - float tg0 = (tfill0 >> 8) & 0xFF; - float tb0 = (tfill0 >> 0) & 0xFF; - - int tr = (int) (tr0 + weight * (r - r0)); - int tg = (int) (tg0 + weight * (g - g0)); - int tb = (int) (tb0 + weight * (b - b0)); - - colors[tessIdx] = 0xff000000 | (tr << 16) | (tg << 8) | tb; - } - } - - - /////////////////////////////////////////////////////////// - - // - - // Tessellated geometry getter. - - - public PShape getTessellation() { - updateTessellation(); - - float[] vertices = tessGeo.fillVertices; - float[] normals = tessGeo.fillNormals; - int[] color = tessGeo.fillColors; - float[] uv = tessGeo.fillTexcoords; - short[] indices = tessGeo.fillIndices; - - PShape tess = pg.createShape(TRIANGLES); - tess.noStroke(); - - IndexCache cache = tessGeo.fillIndexCache; - for (int n = firstFillIndexCache; n <= lastFillIndexCache; n++) { - int ioffset = cache.indexOffset[n]; - int icount = cache.indexCount[n]; - int voffset = cache.vertexOffset[n]; - - for (int tr = ioffset / 3; tr < (ioffset + icount) / 3; tr++) { - int i0 = voffset + indices[3 * tr + 0]; - int i1 = voffset + indices[3 * tr + 1]; - int i2 = voffset + indices[3 * tr + 2]; - - float x0 = vertices[4 * i0 + 0], y0 = vertices[4 * i0 + 1], z0 = vertices[4 * i0 + 2]; - float x1 = vertices[4 * i1 + 0], y1 = vertices[4 * i1 + 1], z1 = vertices[4 * i1 + 2]; - float x2 = vertices[4 * i2 + 0], y2 = vertices[4 * i2 + 1], z2 = vertices[4 * i2 + 2]; - - float nx0 = normals[3 * i0 + 0], ny0 = normals[3 * i0 + 1], nz0 = normals[3 * i0 + 2]; - float nx1 = normals[3 * i1 + 0], ny1 = normals[3 * i1 + 1], nz1 = normals[3 * i1 + 2]; - float nx2 = normals[3 * i2 + 0], ny2 = normals[3 * i2 + 1], nz2 = normals[3 * i2 + 2]; - - int argb0 = PGL.nativeToJavaARGB(color[i0]); - int argb1 = PGL.nativeToJavaARGB(color[i1]); - int argb2 = PGL.nativeToJavaARGB(color[i2]); - - tess.fill(argb0); - tess.normal(nx0, ny0, nz0); - tess.vertex(x0, y0, z0, uv[2 * i0 + 0], uv[2 * i0 + 1]); - - tess.fill(argb1); - tess.normal(nx1, ny1, nz1); - tess.vertex(x1, y1, z1, uv[2 * i1 + 0], uv[2 * i1 + 1]); - - tess.fill(argb2); - tess.normal(nx2, ny2, nz2); - tess.vertex(x2, y2, z2, uv[2 * i2 + 0], uv[2 * i2 + 1]); - } - } - tess.end(); - - return tess; - } - - - /////////////////////////////////////////////////////////// - - // - - // Tessellation - - - protected void updateTessellation() { - if (!root.tessellated || root.contextIsOutdated()) { - root.tessellate(); - root.aggregate(); - } + public boolean is3D() { + return true; } - protected void tessellate() { - if (root == this && parent == null) { - if (tessGeo == null) { - tessGeo = pg.newTessGeometry(PGraphicsOpenGL.RETAINED); - } - tessGeo.clear(); - - tessellateImpl(); - - // Tessellated arrays are trimmed since they are expanded - // by doubling their old size, which might lead to arrays - // larger than the vertex counts. - tessGeo.trim(); - - needBufferInit = true; - - modified = false; - - modifiedFillVertices = false; - modifiedFillColors = false; - modifiedFillNormals = false; - modifiedFillTexCoords = false; - modifiedFillAmbient = false; - modifiedFillSpecular = false; - modifiedFillEmissive = false; - modifiedFillShininess = false; - - modifiedLineVertices = false; - modifiedLineColors = false; - modifiedLineAttributes = false; - - modifiedPointVertices = false; - modifiedPointColors = false; - modifiedPointAttributes = false; - - firstModifiedFillVertex = PConstants.MAX_INT; - lastModifiedFillVertex = PConstants.MIN_INT; - firstModifiedFillColor = PConstants.MAX_INT; - lastModifiedFillColor = PConstants.MIN_INT; - firstModifiedFillNormal = PConstants.MAX_INT; - lastModifiedFillNormal = PConstants.MIN_INT; - firstModifiedFillTexCoord = PConstants.MAX_INT; - lastModifiedFillTexCoord = PConstants.MIN_INT; - firstModifiedFillAmbient = PConstants.MAX_INT; - lastModifiedFillAmbient = PConstants.MIN_INT; - firstModifiedFillSpecular = PConstants.MAX_INT; - lastModifiedFillSpecular = PConstants.MIN_INT; - firstModifiedFillEmissive = PConstants.MAX_INT; - lastModifiedFillEmissive = PConstants.MIN_INT; - firstModifiedFillShininess = PConstants.MAX_INT; - lastModifiedFillShininess = PConstants.MIN_INT; - - firstModifiedLineVertex = PConstants.MAX_INT; - lastModifiedLineVertex = PConstants.MIN_INT; - firstModifiedLineColor = PConstants.MAX_INT; - lastModifiedLineColor = PConstants.MIN_INT; - firstModifiedLineAttribute = PConstants.MAX_INT; - lastModifiedLineAttribute = PConstants.MIN_INT; - - firstModifiedPointVertex = PConstants.MAX_INT; - lastModifiedPointVertex = PConstants.MIN_INT; - firstModifiedPointColor = PConstants.MAX_INT; - lastModifiedPointColor = PConstants.MIN_INT; - firstModifiedPointAttribute = PConstants.MAX_INT; - lastModifiedPointAttribute = PConstants.MIN_INT; - } - } - - - protected void tessellateImpl() { - tessGeo = root.tessGeo; - - if (family == GROUP) { - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.tessellateImpl(); - } - - firstPointIndexCache = -1; - lastPointIndexCache = -1; - firstLineIndexCache = -1; - lastLineIndexCache = -1; - firstFillIndexCache = -1; - lastFillIndexCache = -1; - } else { - if (shapeEnded) { - // If the geometry was tessellated previously, then - // the edges information will still be stored in the - // input object, so it needs to be removed to avoid - // duplication. - inGeo.clearEdges(); - - tessellator.setInGeometry(inGeo); - tessellator.setTessGeometry(tessGeo); - tessellator.setFill(fill || texture != null); - tessellator.setStroke(stroke); - tessellator.setStrokeColor(strokeColor); - tessellator.setStrokeWeight(strokeWeight); - tessellator.setStrokeCap(strokeCap); - tessellator.setStrokeJoin(strokeJoin); - - if (family == GEOMETRY) { - // The tessellation maps are used to associate input - // vertices with the corresponding tessellated vertices. - // This correspondence might not be one-to-one, in the - // case of lines and polygon shapes for example. - inGeo.initTessMap(tessGeo); - - if (kind == POINTS) { - tessellator.tessellatePoints(); - } else if (kind == LINES) { - tessellator.tessellateLines(); - } else if (kind == LINE_STRIP) { - tessellator.tessellateLineStrip(); - } else if (kind == LINE_LOOP) { - tessellator.tessellateLineLoop(); - } else if (kind == TRIANGLE || kind == TRIANGLES) { - if (stroke) inGeo.addTrianglesEdges(); - if (normalMode == NORMAL_MODE_AUTO) inGeo.calcTrianglesNormals(); - tessellator.tessellateTriangles(); - } else if (kind == TRIANGLE_FAN) { - if (stroke) inGeo.addTriangleFanEdges(); - if (normalMode == NORMAL_MODE_AUTO) inGeo.calcTriangleFanNormals(); - tessellator.tessellateTriangleFan(); - } else if (kind == TRIANGLE_STRIP) { - if (stroke) inGeo.addTriangleStripEdges(); - if (normalMode == NORMAL_MODE_AUTO) inGeo.calcTriangleStripNormals(); - tessellator.tessellateTriangleStrip(); - } else if (kind == QUAD || kind == QUADS) { - if (stroke) inGeo.addQuadsEdges(); - if (normalMode == NORMAL_MODE_AUTO) inGeo.calcQuadsNormals(); - tessellator.tessellateQuads(); - } else if (kind == QUAD_STRIP) { - if (stroke) inGeo.addQuadStripEdges(); - if (normalMode == NORMAL_MODE_AUTO) inGeo.calcQuadStripNormals(); - tessellator.tessellateQuadStrip(); - } else if (kind == POLYGON) { - if (stroke) inGeo.addPolygonEdges(isClosed); - tessellator.tessellatePolygon(isSolid, isClosed, normalMode == NORMAL_MODE_AUTO); - } - } else if (family == PRIMITIVE) { - // The input geometry needs to be cleared because the geometry - // generation methods in InGeometry add the vertices of the - // new primitive to what is already stored. - inGeo.clear(); - - if (kind == POINT) { - tessellatePoint(); - } else if (kind == LINE) { - tessellateLine(); - } else if (kind == TRIANGLE) { - tessellateTriangle(); - } else if (kind == QUAD) { - tessellateQuad(); - } else if (kind == RECT) { - tessellateRect(); - } else if (kind == ELLIPSE) { - tessellateEllipse(); - } else if (kind == ARC) { - tessellateArc(); - } else if (kind == BOX) { - tessellateBox(); - } else if (kind == SPHERE) { - tessellateSphere(); - } - } else if (family == PATH) { - tessellatePath(); - } - - if (texture != null && parent != null) { - ((PShape3D)parent).addTexture(texture); - } - - inGeo.compactTessMap(); - - firstPointIndexCache = tessellator.firstPointIndexCache; - lastPointIndexCache = tessellator.lastPointIndexCache; - firstLineIndexCache = tessellator.firstLineIndexCache; - lastLineIndexCache = tessellator.lastLineIndexCache; - firstFillIndexCache = tessellator.firstFillIndexCache; - lastFillIndexCache = tessellator.lastFillIndexCache; - } - } - - tessellated = true; - } - - - protected void tessellatePoint() { - float x = 0, y = 0, z = 0; - if (params.length == 2) { - x = params[0]; - y = params[1]; - z = 0; - } else if (params.length == 3) { - x = params[0]; - y = params[1]; - z = params[2]; - } - - inGeo.setMaterial(fillColor, strokeColor, strokeWeight, - ambientColor, specularColor, emissiveColor, shininess); - inGeo.setNormal(normalX, normalY, normalZ); - inGeo.addPoint(x, y, z, fill, stroke); - inGeo.initTessMap(tessGeo); - tessellator.tessellatePoints(); - } - - - protected void tessellateLine() { - float x1 = 0, y1 = 0, z1 = 0; - float x2 = 0, y2 = 0, z2 = 0; - if (params.length == 4) { - x1 = params[0]; - y1 = params[1]; - x2 = params[2]; - y2 = params[3]; - } else if (params.length == 6) { - x1 = params[0]; - y1 = params[1]; - z1 = params[2]; - x2 = params[3]; - y2 = params[4]; - z2 = params[5]; - } - - inGeo.setMaterial(fillColor, strokeColor, strokeWeight, - ambientColor, specularColor, emissiveColor, shininess); - inGeo.setNormal(normalX, normalY, normalZ); - inGeo.addLine(x1, y1, z1, - x2, y2, z2, - fill, stroke); - inGeo.initTessMap(tessGeo); - tessellator.tessellateLines(); - } - - - protected void tessellateTriangle() { - float x1 = 0, y1 = 0; - float x2 = 0, y2 = 0; - float x3 = 0, y3 = 0; - if (params.length == 6) { - x1 = params[0]; - y1 = params[1]; - x2 = params[2]; - y2 = params[3]; - x3 = params[4]; - y3 = params[5]; - } - - inGeo.setMaterial(fillColor, strokeColor, strokeWeight, - ambientColor, specularColor, emissiveColor, shininess); - inGeo.setNormal(normalX, normalY, normalZ); - inGeo.addTriangle(x1, y1, 0, - x2, y2, 0, - x3, y3, 0, - fill, stroke); - inGeo.initTessMap(tessGeo); - tessellator.tessellateTriangles(); - } - - - protected void tessellateQuad() { - float x1 = 0, y1 = 0; - float x2 = 0, y2 = 0; - float x3 = 0, y3 = 0; - float x4 = 0, y4 = 0; - if (params.length == 8) { - x1 = params[0]; - y1 = params[1]; - x2 = params[2]; - y2 = params[3]; - x3 = params[4]; - y3 = params[5]; - x4 = params[6]; - y4 = params[7]; - } - - inGeo.setMaterial(fillColor, strokeColor, strokeWeight, - ambientColor, specularColor, emissiveColor, shininess); - inGeo.setNormal(normalX, normalY, normalZ); - inGeo.addQuad(x1, y1, 0, - x2, y2, 0, - x3, y3, 0, - x4, y4, 0, - fill, stroke); - inGeo.initTessMap(tessGeo); - tessellator.tessellateQuads(); - } - - - protected void tessellateRect() { - float a = 0, b = 0, c = 0, d = 0; - float tl = 0, tr = 0, br = 0, bl = 0; - boolean rounded = false; - if (params.length == 4) { - rounded = false; - a = params[0]; - b = params[1]; - c = params[2]; - d = params[3]; - } else if (params.length == 5) { - a = params[0]; - b = params[1]; - c = params[2]; - d = params[3]; - tl = tr = br = bl = params[4]; - rounded = true; - } else if (params.length == 8) { - a = params[0]; - b = params[1]; - c = params[2]; - d = params[3]; - tl = params[4]; - tr = params[5]; - br = params[6]; - bl = params[7]; - rounded = true; - } - - rectMode = CORNER; - inGeo.setMaterial(fillColor, strokeColor, strokeWeight, - ambientColor, specularColor, emissiveColor, shininess); - inGeo.setNormal(normalX, normalY, normalZ); - if (rounded) { - inGeo.addRect(a, b, c, d, - tl, tr, br, bl, - fill, stroke, bezierDetail, rectMode); - inGeo.initTessMap(tessGeo); - tessellator.tessellatePolygon(false, true, true); - } else { - inGeo.addRect(a, b, c, d, - fill, stroke, rectMode); - inGeo.initTessMap(tessGeo); - tessellator.tessellateQuads(); - } - } - - - protected void tessellateEllipse() { - float a = 0, b = 0, c = 0, d = 0; - if (params.length == 4) { - a = params[0]; - b = params[1]; - c = params[2]; - d = params[3]; - } - - ellipseMode = CORNER; - inGeo.setMaterial(fillColor, strokeColor, strokeWeight, - ambientColor, specularColor, emissiveColor, shininess); - inGeo.setNormal(normalX, normalY, normalZ); - inGeo.addEllipse(a, b, c, d, fill, stroke, ellipseMode); - inGeo.initTessMap(tessGeo); - tessellator.tessellateTriangleFan(); - } - - - protected void tessellateArc() { - float a = 0, b = 0, c = 0, d = 0; - float start = 0, stop = 0; - if (params.length == 6) { - a = params[0]; - b = params[1]; - c = params[2]; - d = params[3]; - start = params[4]; - stop = params[5]; - } - - ellipseMode = CORNER; - inGeo.setMaterial(fillColor, strokeColor, strokeWeight, - ambientColor, specularColor, emissiveColor, shininess); - inGeo.setNormal(normalX, normalY, normalZ); - inGeo.addArc(a, b, c, d, start, stop, fill, stroke, ellipseMode); - inGeo.initTessMap(tessGeo); - tessellator.tessellateTriangleFan(); - } - - - protected void tessellateBox() { - float w = 0, h = 0, d = 0; - if (params.length == 1) { - w = h = d = params[0]; - } else if (params.length == 3) { - w = params[0]; - h = params[1]; - d = params[2]; - } - - inGeo.setMaterial(fillColor, strokeColor, strokeWeight, - ambientColor, specularColor, emissiveColor, shininess); - inGeo.addBox(w, h, d, fill, stroke); - inGeo.initTessMap(tessGeo); - tessellator.tessellateQuads(); - } - - - protected void tessellateSphere() { - // Getting sphere detail from renderer. Is this correct? - int nu = pg.sphereDetailU; - int nv = pg.sphereDetailV; - float r = 0; - if (params.length == 1) { - r = params[0]; - } - - inGeo.setMaterial(fillColor, strokeColor, strokeWeight, - ambientColor, specularColor, emissiveColor, shininess); - int[] indices = inGeo.addSphere(r, nu, nv, fill, stroke); - inGeo.initTessMap(tessGeo); - tessellator.tessellateTriangles(indices); - } - - - protected void tessellatePath() { - if (vertices == null) return; - - inGeo.setMaterial(fillColor, strokeColor, strokeWeight, - ambientColor, specularColor, emissiveColor, shininess); - - boolean insideContour = false; - - if (vertexCodeCount == 0) { // each point is a simple vertex - if (vertices[0].length == 2) { // tesellating 2D vertices - for (int i = 0; i < vertexCount; i++) { - inGeo.addVertex(vertices[i][X], vertices[i][Y], VERTEX); - } - } else { // drawing 3D vertices - for (int i = 0; i < vertexCount; i++) { - inGeo.addVertex(vertices[i][X], vertices[i][Y], vertices[i][Z], VERTEX); - } - } - } else { // coded set of vertices - int index = 0; - int code = VERTEX; - - if (vertices[0].length == 2) { // tessellating a 2D path - - for (int j = 0; j < vertexCodeCount; j++) { - switch (vertexCodes[j]) { - - case VERTEX: - inGeo.addVertex(vertices[index][X], vertices[index][Y], code); - code = VERTEX; - index++; - break; - - case QUAD_BEZIER_VERTEX: - inGeo.addQuadraticVertex(vertices[index+0][X], vertices[index+0][Y], 0, - vertices[index+1][X], vertices[index+1][Y], 0, - fill, stroke, bezierDetail, code); - code = VERTEX; - index += 2; - break; - - case BEZIER_VERTEX: - inGeo.addBezierVertex(vertices[index+0][X], vertices[index+0][Y], 0, - vertices[index+1][X], vertices[index+1][Y], 0, - vertices[index+2][X], vertices[index+2][Y], 0, - fill, stroke, bezierDetail, code); - code = VERTEX; - index += 3; - break; - - case CURVE_VERTEX: - inGeo.addCurveVertex(vertices[index][X], vertices[index][Y], 0, - fill, stroke, curveDetail, code); - code = VERTEX; - index++; - - case BREAK: - if (insideContour) { - code = BREAK; - } - insideContour = true; - } - } - } else { // tessellating a 3D path - for (int j = 0; j < vertexCodeCount; j++) { - switch (vertexCodes[j]) { - - case VERTEX: - inGeo.addVertex(vertices[index][X], vertices[index][Y], vertices[index][Z], code); - code = VERTEX; - index++; - break; - - case QUAD_BEZIER_VERTEX: - inGeo.addQuadraticVertex(vertices[index+0][X], vertices[index+0][Y], vertices[index+0][Z], - vertices[index+1][X], vertices[index+1][Y], vertices[index+0][Z], - fill, stroke, bezierDetail, code); - code = VERTEX; - index += 2; - break; - - - case BEZIER_VERTEX: - inGeo.addBezierVertex(vertices[index+0][X], vertices[index+0][Y], vertices[index+0][Z], - vertices[index+1][X], vertices[index+1][Y], vertices[index+1][Z], - vertices[index+2][X], vertices[index+2][Y], vertices[index+2][Z], - fill, stroke, bezierDetail, code); - code = VERTEX; - index += 3; - break; - - case CURVE_VERTEX: - inGeo.addCurveVertex(vertices[index][X], vertices[index][Y], vertices[index][Z], - fill, stroke, curveDetail, code); - code = VERTEX; - index++; - - case BREAK: - if (insideContour) { - code = BREAK; - } - insideContour = true; - } - } - } - } - - if (stroke) inGeo.addPolygonEdges(isClosed); - inGeo.initTessMap(tessGeo); - tessellator.tessellatePolygon(false, isClosed, true); - } - - - /////////////////////////////////////////////////////////// - + //////////////////////////////////////////////////////////////////////// // - - // Aggregation + // Shape copy - protected void aggregate() { - if (root == this && parent == null) { - // Initializing auxiliary variables in root node - // needed for aggregation. - fillIndexOffset = 0; - fillVertexOffset = 0; - fillVertexAbs = 0; - fillVertexRel = 0; - - lineIndexOffset = 0; - lineVertexOffset = 0; - lineVertexAbs = 0; - lineVertexRel = 0; - - pointIndexOffset = 0; - pointVertexOffset = 0; - pointVertexAbs = 0; - pointVertexRel = 0; - - // Recursive aggregation. - aggregateImpl(); + static public PShape3D createShape(PApplet parent, PShape src) { + PShape3D dest = null; + if (src.getFamily() == GROUP) { + dest = PGraphics3D.createShapeImpl(parent, GROUP); + PShape3D.copyGroup(parent, src, dest); + } else if (src.getFamily() == PRIMITIVE) { + dest = PGraphics3D.createShapeImpl(parent, src.getKind(), src.getParams()); + PShape.copyPrimitive(src, dest); + } else if (src.getFamily() == GEOMETRY) { + dest = PGraphics3D.createShapeImpl(parent, src.getKind()); + PShape.copyGeometry(src, dest); + } else if (src.getFamily() == PATH) { + dest = PGraphics3D.createShapeImpl(parent, PATH); + PShape.copyPath(src, dest); } - } + dest.setName(src.getName()); + return dest; + } - // This method is very important, as it is responsible of generating the correct - // vertex and index offsets for each level of the shape hierarchy. - // This is the core of the recursive algorithm that calculates the indices - // for the vertices accumulated in a single VBO. - // Basically, the algorithm traverses all the shapes in the hierarchy and - // updates the index cache for each child shape holding geometry (those being - // the leaf nodes in the hierarchy tree), and creates index caches for the - // group shapes so that the draw() method can be called from any shape in the - // hierarchy and the correct piece of geometry will be rendered. - // - // For example, in the following hierarchy: - // - // ROOT GROUP - // | - // /-----------------0-----------------\ - // | | - // CHILD GROUP 0 CHILD GROUP 1 - // | | - // | /---------------0-----------------\ - // | | | | - // GEO SHAPE 0 GEO SHAPE 0 GEO SHAPE 1 GEO SHAPE 2 - // 4 vertices 5 vertices 6 vertices 3 vertices - // - // calling draw() from the root group should result in all the - // vertices (4 + 5 + 6 + 3 = 18) being rendered, while calling - // draw() from either child groups 0 or 1 should result in the first - // 4 vertices or the last 14 vertices being rendered, respectively. - protected void aggregateImpl() { - if (family == GROUP) { - // Recursively aggregating the child shapes. - haveFill = false; - haveLines = false; - havePoints = false; - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - child.aggregateImpl(); - haveFill |= child.haveFill; - haveLines |= child.haveLines; - havePoints |= child.havePoints; - } - } else { // LEAF SHAPE (family either GEOMETRY, PATH or PRIMITIVE) - haveFill = -1 < firstFillIndexCache && -1 < lastFillIndexCache; - haveLines = -1 < firstLineIndexCache && -1 < lastLineIndexCache; - havePoints = -1 < firstPointIndexCache && -1 < lastPointIndexCache; - } - - if (haveFill) updateFillIndexCache(); - if (haveLines) updateLineIndexCache(); - if (havePoints) updatePointIndexCache(); - - if (matrix != null) { - // Some geometric transformations were applied on - // this shape before tessellation, so they are applied now. - //applyMatrixImpl(matrix); - if (haveFill) tessGeo.applyMatrixOnFillGeometry(matrix, firstFillVertex, lastFillVertex); - if (haveLines) tessGeo.applyMatrixOnLineGeometry(matrix, firstLineVertex, lastLineVertex); - if (havePoints) tessGeo.applyMatrixOnPointGeometry(matrix, firstPointVertex, lastPointVertex); - } - } - - - // Updates the index cache for the range that corresponds to this shape. - protected void updateFillIndexCache() { - IndexCache cache = tessGeo.fillIndexCache; - if (family == GROUP) { - // Updates the index cache to include the elements corresponding to - // a group shape, using the cache entries of the child shapes. The - // index cache has a pyramidal structure where the base is formed - // by the entries corresponding to the leaf (geometry) shapes, and - // each subsequent level is determined by the higher-level group shapes - // The index pyramid is flattened into arrays in order to use simple - // data structures, so each shape needs to store the positions in the - // cache that corresponds to itself. - - // The index ranges of the child shapes that share the vertex offset - // are unified into a single range in the parent level. - - firstFillIndexCache = lastFillIndexCache = -1; - int gindex = -1; - - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; + static public void copyGroup(PApplet parent, PShape src, PShape dest) { + copyMatrix(src, dest); + copyStyles(src, dest); + copyImage(src, dest); - int first = child.firstFillIndexCache; - int count = -1 < first ? child.lastFillIndexCache - first + 1 : -1; - for (int n = first; n < first + count; n++) { - if (gindex == -1) { - gindex = cache.addNew(n); - firstFillIndexCache = gindex; - } else { - if (cache.vertexOffset[gindex] == cache.vertexOffset[n]) { - // When the vertex offsets are the same, this means that the - // current index range in the group shape can be extended to - // include either the index range in the current child shape. - // This is a result of how the indices are updated for the - // leaf shapes in aggregateImpl(). - cache.incCounts(gindex, cache.indexCount[n], cache.vertexCount[n]); - } else { - gindex = cache.addNew(n); - } - } - } - } - lastFillIndexCache = gindex; - - if (-1 < firstFillIndexCache && -1 < lastFillIndexCache) { - firstFillVertex = cache.vertexOffset[firstFillIndexCache]; - lastFillVertex = cache.vertexOffset[lastFillIndexCache] + cache.vertexCount[lastFillIndexCache] - 1; - } - } else { - // The index cache is updated in order to reflect the fact that all - // the vertices will be stored in a single VBO in the root shape. - // This update works as follows (the methodology is the same for - // fill, line and point): the VertexAbs variable in the root shape - // stores the index of the last vertex up to this shape (plus one) - // without taking into consideration the MAX_VERTEX_INDEX limit, so - // it effectively runs over the entire range. - // VertexRel, on the other hand, is reset every time the limit is - // exceeded, therefore creating the start of a new index group in the - // root shape. When this happens, the indices in the child shape need - // to be restarted as well to reflect the new index offset. - - firstFillVertex = lastFillVertex = cache.vertexOffset[firstFillIndexCache]; - for (int n = firstFillIndexCache; n <= lastFillIndexCache; n++) { - int ioffset = cache.indexOffset[n]; - int icount = cache.indexCount[n]; - int vcount = cache.vertexCount[n]; - - if (PGL.MAX_VERTEX_INDEX1 <= root.fillVertexRel + vcount) { - root.fillVertexRel = 0; - root.fillVertexOffset = root.fillVertexAbs; - cache.indexOffset[n] = root.fillIndexOffset; - } else tessGeo.incFillIndices(ioffset, ioffset + icount - 1, root.fillVertexRel); - cache.vertexOffset[n] = root.fillVertexOffset; - - root.fillIndexOffset += icount; - root.fillVertexAbs += vcount; - root.fillVertexRel += vcount; - lastFillVertex += vcount; - } - lastFillVertex--; + for (int i = 0; i < src.getChildCount(); i++) { + PShape c = PShape3D.createShape(parent, src.getChild(i)); + dest.addChild(c); } - } - - - protected void updateLineIndexCache() { - IndexCache cache = tessGeo.lineIndexCache; - if (family == GROUP) { - firstLineIndexCache = lastLineIndexCache = -1; - int gindex = -1; - - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - - int first = child.firstLineIndexCache; - int count = -1 < first ? child.lastLineIndexCache - first + 1 : -1; - for (int n = first; n < first + count; n++) { - if (gindex == -1) { - gindex = cache.addNew(n); - firstLineIndexCache = gindex; - } else { - if (cache.vertexOffset[gindex] == cache.vertexOffset[n]) { - cache.incCounts(gindex, cache.indexCount[n], cache.vertexCount[n]); - } else { - gindex = cache.addNew(n); - } - } - } - } - lastLineIndexCache = gindex; - - if (-1 < firstLineIndexCache && -1 < lastLineIndexCache) { - firstLineVertex = cache.vertexOffset[firstLineIndexCache]; - lastLineVertex = cache.vertexOffset[lastLineIndexCache] + cache.vertexCount[lastLineIndexCache] - 1; - } - } else { - firstLineVertex = lastLineVertex = cache.vertexOffset[firstLineIndexCache]; - for (int n = firstLineIndexCache; n <= lastLineIndexCache; n++) { - int ioffset = cache.indexOffset[n]; - int icount = cache.indexCount[n]; - int vcount = cache.vertexCount[n]; - - if (PGL.MAX_VERTEX_INDEX1 <= root.lineVertexRel + vcount) { - root.lineVertexRel = 0; - root.lineVertexOffset = root.lineVertexAbs; - cache.indexOffset[n] = root.lineIndexOffset; - } else tessGeo.incLineIndices(ioffset, ioffset + icount - 1, root.lineVertexRel); - cache.vertexOffset[n] = root.lineVertexOffset; - - root.lineIndexOffset += icount; - root.lineVertexAbs += vcount; - root.lineVertexRel += vcount; - lastLineVertex += vcount; - } - lastLineVertex--; - } - } - - - protected void updatePointIndexCache() { - IndexCache cache = tessGeo.pointIndexCache; - if (family == GROUP) { - firstPointIndexCache = lastPointIndexCache = -1; - int gindex = -1; - - for (int i = 0; i < childCount; i++) { - PShape3D child = (PShape3D) children[i]; - - int first = child.firstPointIndexCache; - int count = -1 < first ? child.lastPointIndexCache - first + 1 : -1; - for (int n = first; n < first + count; n++) { - if (gindex == -1) { - gindex = cache.addNew(n); - firstPointIndexCache = gindex; - } else { - if (cache.vertexOffset[gindex] == cache.vertexOffset[n]) { - // When the vertex offsets are the same, this means that the - // current index range in the group shape can be extended to - // include either the index range in the current child shape. - // This is a result of how the indices are updated for the - // leaf shapes in aggregateImpl(). - cache.incCounts(gindex, cache.indexCount[n], cache.vertexCount[n]); - } else { - gindex = cache.addNew(n); - } - } - } - } - lastPointIndexCache = gindex; - - if (-1 < firstPointIndexCache && -1 < lastPointIndexCache) { - firstPointVertex = cache.vertexOffset[firstPointIndexCache]; - lastPointVertex = cache.vertexOffset[lastPointIndexCache] + cache.vertexCount[lastPointIndexCache] - 1; - } - } else { - firstPointVertex = lastPointVertex = cache.vertexOffset[firstPointIndexCache]; - for (int n = firstPointIndexCache; n <= lastPointIndexCache; n++) { - int ioffset = cache.indexOffset[n]; - int icount = cache.indexCount[n]; - int vcount = cache.vertexCount[n]; - - if (PGL.MAX_VERTEX_INDEX1 <= root.pointVertexRel + vcount) { - root.pointVertexRel = 0; - root.pointVertexOffset = root.pointVertexAbs; - cache.indexOffset[n] = root.pointIndexOffset; - } else tessGeo.incPointIndices(ioffset, ioffset + icount - 1, root.pointVertexRel); - cache.vertexOffset[n] = root.pointVertexOffset; - - root.pointIndexOffset += icount; - root.pointVertexAbs += vcount; - root.pointVertexRel += vcount; - lastPointVertex += vcount; - } - lastPointVertex--; - } - } - - - /////////////////////////////////////////////////////////// - - // - - // Buffer initialization - - - protected void initBuffers() { - if (needBufferInit) { - context = pgl.getCurrentContext(); - - if (0 < tessGeo.fillVertexCount && 0 < tessGeo.fillIndexCount) { - initFillBuffers(); - } - - if (0 < tessGeo.lineVertexCount && 0 < tessGeo.lineIndexCount) { - initLineBuffers(); - } - - if (0 < tessGeo.pointVertexCount && 0 < tessGeo.pointIndexCount) { - initPointBuffers(); - } - - needBufferInit = false; - } - } - - - protected void initFillBuffers() { - int size = tessGeo.fillVertexCount; - int sizef = size * PGL.SIZEOF_FLOAT; - int sizei = size * PGL.SIZEOF_INT; - - glFillVertexBufferID = pg.createVertexBufferObject(context.code()); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillVertexBufferID); - pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 4 * sizef, FloatBuffer.wrap(tessGeo.fillVertices, 0, 4 * size), PGL.GL_STATIC_DRAW); - - glFillColorBufferID = pg.createVertexBufferObject(context.code()); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillColorBufferID); - pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, IntBuffer.wrap(tessGeo.fillColors, 0, size), PGL.GL_STATIC_DRAW); - - glFillNormalBufferID = pg.createVertexBufferObject(context.code()); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillNormalBufferID); - pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 3 * sizef, FloatBuffer.wrap(tessGeo.fillNormals, 0, 3 * size), PGL.GL_STATIC_DRAW); - - glFillTexCoordBufferID = pg.createVertexBufferObject(context.code()); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillTexCoordBufferID); - pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 2 * sizef, FloatBuffer.wrap(tessGeo.fillTexcoords, 0, 2 * size), PGL.GL_STATIC_DRAW); - - glFillAmbientBufferID = pg.createVertexBufferObject(context.code()); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillAmbientBufferID); - pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, IntBuffer.wrap(tessGeo.fillAmbient, 0, size), PGL.GL_STATIC_DRAW); - - glFillSpecularBufferID = pg.createVertexBufferObject(context.code()); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillSpecularBufferID); - pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, IntBuffer.wrap(tessGeo.fillSpecular, 0, size), PGL.GL_STATIC_DRAW); - - glFillEmissiveBufferID = pg.createVertexBufferObject(context.code()); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillEmissiveBufferID); - pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, IntBuffer.wrap(tessGeo.fillEmissive, 0, size), PGL.GL_STATIC_DRAW); - - glFillShininessBufferID = pg.createVertexBufferObject(context.code()); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillShininessBufferID); - pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizef, FloatBuffer.wrap(tessGeo.fillShininess, 0, size), PGL.GL_STATIC_DRAW); - - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); - - glFillIndexBufferID = pg.createVertexBufferObject(context.code()); - pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, glFillIndexBufferID); - pgl.glBufferData(PGL.GL_ELEMENT_ARRAY_BUFFER, tessGeo.fillIndexCount * PGL.SIZEOF_INDEX, - ShortBuffer.wrap(tessGeo.fillIndices, 0, tessGeo.fillIndexCount), PGL.GL_STATIC_DRAW); - - pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, 0); - } - - - protected void initLineBuffers() { - int size = tessGeo.lineVertexCount; - int sizef = size * PGL.SIZEOF_FLOAT; - int sizei = size * PGL.SIZEOF_INT; - - glLineVertexBufferID = pg.createVertexBufferObject(context.code()); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glLineVertexBufferID); - pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 4 * sizef, FloatBuffer.wrap(tessGeo.lineVertices, 0, 4 * size), PGL.GL_STATIC_DRAW); - - glLineColorBufferID = pg.createVertexBufferObject(context.code()); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glLineColorBufferID); - pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, IntBuffer.wrap(tessGeo.lineColors, 0, size), PGL.GL_STATIC_DRAW); - - glLineDirWidthBufferID = pg.createVertexBufferObject(context.code()); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glLineDirWidthBufferID); - pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 4 * sizef, FloatBuffer.wrap(tessGeo.lineDirWidths, 0, 4 * size), PGL.GL_STATIC_DRAW); - - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); - - glLineIndexBufferID = pg.createVertexBufferObject(context.code()); - pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, glLineIndexBufferID); - pgl.glBufferData(PGL.GL_ELEMENT_ARRAY_BUFFER, tessGeo.lineIndexCount * PGL.SIZEOF_INDEX, - ShortBuffer.wrap(tessGeo.lineIndices, 0, tessGeo.lineIndexCount), PGL.GL_STATIC_DRAW); - - pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, 0); - } - - - protected void initPointBuffers() { - int size = tessGeo.pointVertexCount; - int sizef = size * PGL.SIZEOF_FLOAT; - int sizei = size * PGL.SIZEOF_INT; - - glPointVertexBufferID = pg.createVertexBufferObject(context.code()); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPointVertexBufferID); - pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 4 * sizef, FloatBuffer.wrap(tessGeo.pointVertices, 0, 4 * size), PGL.GL_STATIC_DRAW); - - glPointColorBufferID = pg.createVertexBufferObject(context.code()); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPointColorBufferID); - pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, IntBuffer.wrap(tessGeo.pointColors, 0, size), PGL.GL_STATIC_DRAW); - - glPointSizeBufferID = pg.createVertexBufferObject(context.code()); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPointSizeBufferID); - pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 2 * sizef, FloatBuffer.wrap(tessGeo.pointSizes, 0, 2 * size), PGL.GL_STATIC_DRAW); - - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); - - glPointIndexBufferID = pg.createVertexBufferObject(context.code()); - pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, glPointIndexBufferID); - pgl.glBufferData(PGL.GL_ELEMENT_ARRAY_BUFFER, tessGeo.pointIndexCount * PGL.SIZEOF_INDEX, - ShortBuffer.wrap(tessGeo.pointIndices, 0, tessGeo.pointIndexCount), PGL.GL_STATIC_DRAW); - - pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, 0); - } - - - protected boolean contextIsOutdated() { - boolean outdated = !pgl.contextIsCurrent(context); - if (outdated) { - // Removing the VBOs from the renderer's list so they - // doesn't get deleted by OpenGL. The VBOs were already - // automatically disposed when the old context was - // destroyed. - pg.removeVertexBufferObject(glFillVertexBufferID, context.code()); - pg.removeVertexBufferObject(glFillColorBufferID, context.code()); - pg.removeVertexBufferObject(glFillNormalBufferID, context.code()); - pg.removeVertexBufferObject(glFillTexCoordBufferID, context.code()); - pg.removeVertexBufferObject(glFillAmbientBufferID, context.code()); - pg.removeVertexBufferObject(glFillSpecularBufferID, context.code()); - pg.removeVertexBufferObject(glFillEmissiveBufferID, context.code()); - pg.removeVertexBufferObject(glFillShininessBufferID, context.code()); - pg.removeVertexBufferObject(glFillIndexBufferID, context.code()); - - pg.removeVertexBufferObject(glLineVertexBufferID, context.code()); - pg.removeVertexBufferObject(glLineColorBufferID, context.code()); - pg.removeVertexBufferObject(glLineDirWidthBufferID, context.code()); - pg.removeVertexBufferObject(glLineIndexBufferID, context.code()); - - pg.removeVertexBufferObject(glPointVertexBufferID, context.code()); - pg.removeVertexBufferObject(glPointColorBufferID, context.code()); - pg.removeVertexBufferObject(glPointSizeBufferID, context.code()); - pg.removeVertexBufferObject(glPointIndexBufferID, context.code()); - - // The OpenGL resources have been already deleted - // when the context changed. We only need to zero - // them to avoid deleting them again when the GC - // runs the finalizers of the disposed object. - glFillVertexBufferID = 0; - glFillColorBufferID = 0; - glFillNormalBufferID = 0; - glFillTexCoordBufferID = 0; - glFillAmbientBufferID = 0; - glFillSpecularBufferID = 0; - glFillEmissiveBufferID = 0; - glFillShininessBufferID = 0; - glFillIndexBufferID = 0; - - glLineVertexBufferID = 0; - glLineColorBufferID = 0; - glLineDirWidthBufferID = 0; - glLineIndexBufferID = 0; - - glPointVertexBufferID = 0; - glPointColorBufferID = 0; - glPointSizeBufferID = 0; - glPointIndexBufferID = 0; - } - return outdated; - } - - - /////////////////////////////////////////////////////////// - - // - - // Deletion methods - - - protected void release() { - deleteFillBuffers(); - deleteLineBuffers(); - deletePointBuffers(); - } - - - protected void deleteFillBuffers() { - if (glFillVertexBufferID != 0) { - pg.deleteVertexBufferObject(glFillVertexBufferID, context.code()); - glFillVertexBufferID = 0; - } - - if (glFillColorBufferID != 0) { - pg.deleteVertexBufferObject(glFillColorBufferID, context.code()); - glFillColorBufferID = 0; - } - - if (glFillNormalBufferID != 0) { - pg.deleteVertexBufferObject(glFillNormalBufferID, context.code()); - glFillNormalBufferID = 0; - } - - if (glFillTexCoordBufferID != 0) { - pg.deleteVertexBufferObject(glFillTexCoordBufferID, context.code()); - glFillTexCoordBufferID = 0; - } - - if (glFillAmbientBufferID != 0) { - pg.deleteVertexBufferObject(glFillAmbientBufferID, context.code()); - glFillAmbientBufferID = 0; - } - - if (glFillSpecularBufferID != 0) { - pg.deleteVertexBufferObject(glFillSpecularBufferID, context.code()); - glFillSpecularBufferID = 0; - } - - if (glFillEmissiveBufferID != 0) { - pg.deleteVertexBufferObject(glFillEmissiveBufferID, context.code()); - glFillEmissiveBufferID = 0; - } - - if (glFillShininessBufferID != 0) { - pg.deleteVertexBufferObject(glFillShininessBufferID, context.code()); - glFillShininessBufferID = 0; - } - - if (glFillIndexBufferID != 0) { - pg.deleteVertexBufferObject(glFillIndexBufferID, context.code()); - glFillIndexBufferID = 0; - } - } - - - protected void deleteLineBuffers() { - if (glLineVertexBufferID != 0) { - pg.deleteVertexBufferObject(glLineVertexBufferID, context.code()); - glLineVertexBufferID = 0; - } - - if (glLineColorBufferID != 0) { - pg.deleteVertexBufferObject(glLineColorBufferID, context.code()); - glLineColorBufferID = 0; - } - - if (glLineDirWidthBufferID != 0) { - pg.deleteVertexBufferObject(glLineDirWidthBufferID, context.code()); - glLineDirWidthBufferID = 0; - } - - if (glLineIndexBufferID != 0) { - pg.deleteVertexBufferObject(glLineIndexBufferID, context.code()); - glLineIndexBufferID = 0; - } - } - - - protected void deletePointBuffers() { - if (glPointVertexBufferID != 0) { - pg.deleteVertexBufferObject(glPointVertexBufferID, context.code()); - glPointVertexBufferID = 0; - } - - if (glPointColorBufferID != 0) { - pg.deleteVertexBufferObject(glPointColorBufferID, context.code()); - glPointColorBufferID = 0; - } - - if (glPointSizeBufferID != 0) { - pg.deleteVertexBufferObject(glPointSizeBufferID, context.code()); - glPointSizeBufferID = 0; - } - - if (glPointIndexBufferID != 0) { - pg.deleteVertexBufferObject(glPointIndexBufferID, context.code()); - glPointIndexBufferID = 0; - } - } - - - /////////////////////////////////////////////////////////// - - // - - // Geometry update - - - protected void updateGeometry() { - root.initBuffers(); - if (root.modified) { - root.updateGeometryImpl(); - } - } - - - protected void updateGeometryImpl() { - if (modifiedFillVertices) { - int offset = firstModifiedFillVertex; - int size = lastModifiedFillVertex - offset + 1; - copyFillVertices(offset, size); - modifiedFillVertices = false; - firstModifiedFillVertex = PConstants.MAX_INT; - lastModifiedFillVertex = PConstants.MIN_INT; - } - if (modifiedFillColors) { - int offset = firstModifiedFillColor; - int size = lastModifiedFillColor - offset + 1; - copyFillColors(offset, size); - modifiedFillColors = false; - firstModifiedFillColor = PConstants.MAX_INT; - lastModifiedFillColor = PConstants.MIN_INT; - } - if (modifiedFillNormals) { - int offset = firstModifiedFillNormal; - int size = lastModifiedFillNormal - offset + 1; - copyFillNormals(offset, size); - modifiedFillNormals = false; - firstModifiedFillNormal = PConstants.MAX_INT; - lastModifiedFillNormal = PConstants.MIN_INT; - } - if (modifiedFillTexCoords) { - int offset = firstModifiedFillTexCoord; - int size = lastModifiedFillTexCoord - offset + 1; - copyFillTexCoords(offset, size); - modifiedFillTexCoords = false; - firstModifiedFillTexCoord = PConstants.MAX_INT; - lastModifiedFillTexCoord = PConstants.MIN_INT; - } - if (modifiedFillAmbient) { - int offset = firstModifiedFillAmbient; - int size = lastModifiedFillAmbient - offset + 1; - copyFillAmbient(offset, size); - modifiedFillAmbient = false; - firstModifiedFillAmbient = PConstants.MAX_INT; - lastModifiedFillAmbient = PConstants.MIN_INT; - } - if (modifiedFillSpecular) { - int offset = firstModifiedFillSpecular; - int size = lastModifiedFillSpecular - offset + 1; - copyFillSpecular(offset, size); - modifiedFillSpecular = false; - firstModifiedFillSpecular = PConstants.MAX_INT; - lastModifiedFillSpecular = PConstants.MIN_INT; - } - if (modifiedFillEmissive) { - int offset = firstModifiedFillEmissive; - int size = lastModifiedFillEmissive - offset + 1; - copyFillEmissive(offset, size); - modifiedFillEmissive = false; - firstModifiedFillEmissive = PConstants.MAX_INT; - lastModifiedFillEmissive = PConstants.MIN_INT; - } - if (modifiedFillShininess) { - int offset = firstModifiedFillShininess; - int size = lastModifiedFillShininess - offset + 1; - copyFillShininess(offset, size); - modifiedFillShininess = false; - firstModifiedFillShininess = PConstants.MAX_INT; - lastModifiedFillShininess = PConstants.MIN_INT; - } - - if (modifiedLineVertices) { - int offset = firstModifiedLineVertex; - int size = lastModifiedLineVertex - offset + 1; - copyLineVertices(offset, size); - modifiedLineVertices = false; - firstModifiedLineVertex = PConstants.MAX_INT; - lastModifiedLineVertex = PConstants.MIN_INT; - } - if (modifiedLineColors) { - int offset = firstModifiedLineColor; - int size = lastModifiedLineColor - offset + 1; - copyLineColors(offset, size); - modifiedLineColors = false; - firstModifiedLineColor = PConstants.MAX_INT; - lastModifiedLineColor = PConstants.MIN_INT; - } - if (modifiedLineAttributes) { - int offset = firstModifiedLineAttribute; - int size = lastModifiedLineAttribute - offset + 1; - copyLineAttributes(offset, size); - modifiedLineAttributes = false; - firstModifiedLineAttribute = PConstants.MAX_INT; - lastModifiedLineAttribute = PConstants.MIN_INT; - } - - if (modifiedPointVertices) { - int offset = firstModifiedPointVertex; - int size = lastModifiedPointVertex - offset + 1; - copyPointVertices(offset, size); - modifiedPointVertices = false; - firstModifiedPointVertex = PConstants.MAX_INT; - lastModifiedPointVertex = PConstants.MIN_INT; - } - if (modifiedPointColors) { - int offset = firstModifiedPointColor; - int size = lastModifiedPointColor - offset + 1; - copyPointColors(offset, size); - modifiedPointColors = false; - firstModifiedPointColor = PConstants.MAX_INT; - lastModifiedPointColor = PConstants.MIN_INT; - } - if (modifiedPointAttributes) { - int offset = firstModifiedPointAttribute; - int size = lastModifiedPointAttribute - offset + 1; - copyPointAttributes(offset, size); - modifiedPointAttributes = false; - firstModifiedPointAttribute = PConstants.MAX_INT; - lastModifiedPointAttribute = PConstants.MIN_INT; - } - - modified = false; - } - - - protected void copyFillVertices(int offset, int size) { - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillVertexBufferID); - pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, 4 * offset * PGL.SIZEOF_FLOAT, 4 * size * PGL.SIZEOF_FLOAT, - FloatBuffer.wrap(tessGeo.fillVertices, 0, 4 * size)); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); - } - - - protected void copyFillColors(int offset, int size) { - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillColorBufferID); - pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, offset * PGL.SIZEOF_INT, size * PGL.SIZEOF_INT, - IntBuffer.wrap(tessGeo.fillColors, 0, size)); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); - } - - - protected void copyFillNormals(int offset, int size) { - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillNormalBufferID); - pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, 3 * offset * PGL.SIZEOF_FLOAT, 3 * size * PGL.SIZEOF_FLOAT, - FloatBuffer.wrap(tessGeo.fillNormals, 0, 3 * size)); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); - } - - - protected void copyFillTexCoords(int offset, int size) { - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillTexCoordBufferID); - pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, 2 * offset * PGL.SIZEOF_FLOAT, 2 * size * PGL.SIZEOF_FLOAT, - FloatBuffer.wrap(tessGeo.fillTexcoords, 0, 2 * size)); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); - } - - - protected void copyFillAmbient(int offset, int size) { - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillAmbientBufferID); - pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, offset * PGL.SIZEOF_INT, size * PGL.SIZEOF_INT, - IntBuffer.wrap(tessGeo.fillAmbient, 0, size)); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); - } - - - protected void copyFillSpecular(int offset, int size) { - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillSpecularBufferID); - pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, offset * PGL.SIZEOF_INT, size * PGL.SIZEOF_INT, - IntBuffer.wrap(tessGeo.fillSpecular, 0, size)); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); - } - - - protected void copyFillEmissive(int offset, int size) { - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillEmissiveBufferID); - pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, offset * PGL.SIZEOF_INT, size * PGL.SIZEOF_INT, - IntBuffer.wrap(tessGeo.fillEmissive, 0, size)); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); - } - - - protected void copyFillShininess(int offset, int size) { - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glFillShininessBufferID); - pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, offset * PGL.SIZEOF_FLOAT, size * PGL.SIZEOF_FLOAT, - FloatBuffer.wrap(tessGeo.fillShininess, 0, size)); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); - } - - - protected void copyLineVertices(int offset, int size) { - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glLineVertexBufferID); - pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, 4 * offset * PGL.SIZEOF_FLOAT, 4 * size * PGL.SIZEOF_FLOAT, - FloatBuffer.wrap(tessGeo.lineVertices, 0, 4 * size)); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); - } - - - protected void copyLineColors(int offset, int size) { - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glLineColorBufferID); - pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, offset * PGL.SIZEOF_INT, size * PGL.SIZEOF_INT, - IntBuffer.wrap(tessGeo.lineColors, 0, size)); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); - } - - - protected void copyLineAttributes(int offset, int size) { - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glLineDirWidthBufferID); - pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, 4 * offset * PGL.SIZEOF_FLOAT, 4 * size * PGL.SIZEOF_FLOAT, - FloatBuffer.wrap(tessGeo.lineDirWidths, 0, 4 * size)); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); - } - - - protected void copyPointVertices(int offset, int size) { - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPointVertexBufferID); - pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, 4 * offset * PGL.SIZEOF_FLOAT, 4 * size * PGL.SIZEOF_FLOAT, - FloatBuffer.wrap(tessGeo.pointVertices, 0, 4 * size)); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); - } - - - protected void copyPointColors(int offset, int size) { - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPointColorBufferID); - pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, offset * PGL.SIZEOF_INT, size * PGL.SIZEOF_INT, - IntBuffer.wrap(tessGeo.pointColors, 0, size)); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); - } - - - protected void copyPointAttributes(int offset, int size) { - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPointSizeBufferID); - pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, 2 * offset * PGL.SIZEOF_FLOAT, 2 * size * PGL.SIZEOF_FLOAT, - FloatBuffer.wrap(tessGeo.pointSizes, 0, 2 * size)); - pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); - } - - - protected void setModifiedFillVertices(int first, int last) { - if (first < firstModifiedFillVertex) firstModifiedFillVertex = first; - if (last > lastModifiedFillVertex) lastModifiedFillVertex = last; - modifiedFillVertices = true; - modified = true; - } - - - protected void setModifiedFillColors(int first, int last) { - if (first < firstModifiedFillColor) firstModifiedFillColor = first; - if (last > lastModifiedFillColor) lastModifiedFillColor = last; - modifiedFillColors = true; - modified = true; - } - - - protected void setModifiedFillNormals(int first, int last) { - if (first < firstModifiedFillNormal) firstModifiedFillNormal = first; - if (last > lastModifiedFillNormal) lastModifiedFillNormal = last; - modifiedFillNormals = true; - modified = true; - } - - - protected void setModifiedFillTexcoords(int first, int last) { - if (first < firstModifiedFillTexCoord) firstModifiedFillTexCoord = first; - if (last > lastModifiedFillTexCoord) lastModifiedFillTexCoord = last; - modifiedFillTexCoords = true; - modified = true; - } - - - protected void setModifiedFillAmbient(int first, int last) { - if (first < firstModifiedFillAmbient) firstModifiedFillAmbient = first; - if (last > lastModifiedFillAmbient) lastModifiedFillAmbient = last; - modifiedFillAmbient = true; - modified = true; - } - - - protected void setModifiedFillSpecular(int first, int last) { - if (first < firstModifiedFillSpecular) firstModifiedFillSpecular = first; - if (last > lastModifiedFillSpecular) lastModifiedFillSpecular = last; - modifiedFillSpecular = true; - modified = true; - } - - - protected void setModifiedFillEmissive(int first, int last) { - if (first < firstModifiedFillEmissive) firstModifiedFillEmissive = first; - if (last > lastModifiedFillEmissive) lastModifiedFillEmissive = last; - modifiedFillEmissive = true; - modified = true; } - - protected void setModifiedFillShininess(int first, int last) { - if (first < firstModifiedFillShininess) firstModifiedFillShininess = first; - if (last > lastModifiedFillShininess) lastModifiedFillShininess = last; - modifiedFillShininess = true; - modified = true; - } - - - protected void setModifiedLineVertices(int first, int last) { - if (first < firstModifiedLineVertex) firstModifiedLineVertex = first; - if (last > lastModifiedLineVertex) lastModifiedLineVertex = last; - modifiedLineVertices = true; - modified = true; - } - - - protected void setModifiedLineColors(int first, int last) { - if (first < firstModifiedLineColor) firstModifiedLineColor = first; - if (last > lastModifiedLineColor) lastModifiedLineColor = last; - modifiedLineColors = true; - modified = true; - } - - - protected void setModifiedLineAttributes(int first, int last) { - if (first < firstModifiedLineAttribute) firstModifiedLineAttribute = first; - if (last > lastModifiedLineAttribute) lastModifiedLineAttribute = last; - modifiedLineAttributes = true; - modified = true; - } - - - protected void setModifiedPointVertices(int first, int last) { - if (first < firstModifiedPointVertex) firstModifiedPointVertex = first; - if (last > lastModifiedPointVertex) lastModifiedPointVertex = last; - modifiedPointVertices = true; - modified = true; - } - - - protected void setModifiedPointColors(int first, int last) { - if (first < firstModifiedPointColor) firstModifiedPointColor = first; - if (last > lastModifiedPointColor) lastModifiedPointColor = last; - modifiedPointColors = true; - modified = true; - } - - - protected void setModifiedPointAttributes(int first, int last) { - if (first < firstModifiedPointAttribute) firstModifiedPointAttribute = first; - if (last > lastModifiedPointAttribute) lastModifiedPointAttribute = last; - modifiedPointAttributes = true; - modified = true; - } - - - /////////////////////////////////////////////////////////// - - // - - // Style handling - - - // Applies the styles of g. - protected void styles(PGraphics g) { - if (stroke) { - stroke(g.strokeColor); - strokeWeight(g.strokeWeight); - - // These two don't to nothing probably: - strokeCap(g.strokeCap); - strokeJoin(g.strokeJoin); - } else { - noStroke(); - } - - if (fill) { - fill(g.fillColor); - } else { - noFill(); - } - - ambient(g.ambientColor); - specular(g.specularColor); - emissive(g.emissiveColor); - shininess(g.shininess); - - // What about other style parameters, such as rectMode, etc? - // These should force a tessellation update, same as stroke - // cap and weight... right? - } - - - /////////////////////////////////////////////////////////// - - // - - // Rendering methods - - - public void draw() { - draw(pg); - } - - - public void draw(PGraphics g) { - if (visible) { - pre(g); - - updateTessellation(); - updateGeometry(); - - if (family == GROUP) { - if ((textures != null && 1 < textures.size()) || - pg.hintEnabled(ENABLE_ACCURATE_2D)) { - // Some child shapes below this group use different - // texture maps, so they cannot rendered in a single call - // either. - // Or accurate 2D mode is enabled, which forces each - // shape to be rendered separately. - for (int i = 0; i < childCount; i++) { - ((PShape3D) children[i]).draw(g); - } - } else { - PImage tex = null; - if (textures != null && textures.size() == 1) { - tex = (PImage)textures.toArray()[0]; - } - render((PGraphicsOpenGL)g, tex); - } - - } else { - render((PGraphicsOpenGL)g, texture); - } - - post(g); - } - } - - - protected void pre(PGraphics g) { - if (!style) { - styles(g); - } - } - - - public void post(PGraphics g) { - } - - - // Render the geometry stored in the root shape as VBOs, for the vertices - // corresponding to this shape. Sometimes we can have root == this. - protected void render(PGraphicsOpenGL g, PImage texture) { - if (root == null) { - // Some error. Root should never be null. At least it should be this. - return; - } - - if (havePoints) { - renderPoints(g); - if (g.haveRaw()) { - rawPoints(g); - } - } - - if (haveLines) { - renderLines(g); - if (g.haveRaw()) { - rawLines(g); - } - } - - if (haveFill) { - renderFill(g, texture); - if (g.haveRaw()) { - rawFill(g, texture); - } - } - } - - - protected void renderPoints(PGraphicsOpenGL g) { - PointShader shader = g.getPointShader(); - shader.start(); - - IndexCache cache = tessGeo.pointIndexCache; - for (int n = firstPointIndexCache; n <= lastPointIndexCache; n++) { - int ioffset = cache.indexOffset[n]; - int icount = cache.indexCount[n]; - int voffset = cache.vertexOffset[n]; - - shader.setVertexAttribute(root.glPointVertexBufferID, 4, PGL.GL_FLOAT, 0, 4 * voffset * PGL.SIZEOF_FLOAT); - shader.setColorAttribute(root.glPointColorBufferID, 4, PGL.GL_UNSIGNED_BYTE, 0, 4 * voffset * PGL.SIZEOF_BYTE); - shader.setSizeAttribute(root.glPointSizeBufferID, 2, PGL.GL_FLOAT, 0, 2 * voffset * PGL.SIZEOF_FLOAT); - - pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, root.glPointIndexBufferID); - pgl.glDrawElements(PGL.GL_TRIANGLES, icount, PGL.INDEX_TYPE, ioffset * PGL.SIZEOF_INDEX); - pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, 0); - } - - shader.stop(); - } - - - // TODO: finish when points are properly re-implemented. - protected void rawPoints(PGraphicsOpenGL g) { - } - - - protected void renderLines(PGraphicsOpenGL g) { - LineShader shader = g.getLineShader(); - shader.start(); - - IndexCache cache = tessGeo.lineIndexCache; - for (int n = firstLineIndexCache; n <= lastLineIndexCache; n++) { - int ioffset = cache.indexOffset[n]; - int icount = cache.indexCount[n]; - int voffset = cache.vertexOffset[n]; - - shader.setVertexAttribute(root.glLineVertexBufferID, 4, PGL.GL_FLOAT, 0, 4 * voffset * PGL.SIZEOF_FLOAT); - shader.setColorAttribute(root.glLineColorBufferID, 4, PGL.GL_UNSIGNED_BYTE, 0, 4 * voffset * PGL.SIZEOF_BYTE); - shader.setDirWidthAttribute(root.glLineDirWidthBufferID, 4, PGL.GL_FLOAT, 0, 4 * voffset * PGL.SIZEOF_FLOAT); - - pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, root.glLineIndexBufferID); - pgl.glDrawElements(PGL.GL_TRIANGLES, icount, PGL.INDEX_TYPE, ioffset * PGL.SIZEOF_INDEX); - pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, 0); - } - - shader.stop(); - } - - - protected void rawLines(PGraphicsOpenGL g) { - PGraphics raw = g.getRaw(); - - raw.colorMode(RGB); - raw.noFill(); - raw.beginShape(LINES); - - float[] vertices = tessGeo.lineVertices; - int[] color = tessGeo.lineColors; - float[] attribs = tessGeo.lineDirWidths; - short[] indices = tessGeo.lineIndices; - - IndexCache cache = tessGeo.lineIndexCache; - for (int n = firstLineIndexCache; n <= lastLineIndexCache; n++) { - int ioffset = cache.indexOffset[n]; - int icount = cache.indexCount[n]; - int voffset = cache.vertexOffset[n]; - - for (int ln = ioffset / 6; ln < (ioffset + icount) / 6; ln++) { - // Each line segment is defined by six indices since its - // formed by two triangles. We only need the first and last - // vertices. - int i0 = voffset + indices[6 * ln + 0]; - int i1 = voffset + indices[6 * ln + 5]; - - float[] src0 = {0, 0, 0, 0}; - float[] src1 = {0, 0, 0, 0}; - float[] pt0 = {0, 0, 0, 0}; - float[] pt1 = {0, 0, 0, 0}; - int argb0 = PGL.nativeToJavaARGB(color[i0]); - int argb1 = PGL.nativeToJavaARGB(color[i1]); - float sw0 = 2 * attribs[4 * i0 + 3]; - float sw1 = 2 * attribs[4 * i1 + 3]; - - PApplet.arrayCopy(vertices, 4 * i0, src0, 0, 4); - PApplet.arrayCopy(vertices, 4 * i1, src1, 0, 4); - // Applying any transformation is currently stored in the - // modelview matrix of the renderer. - g.modelview.mult(src0, pt0); - g.modelview.mult(src1, pt1); - - if (raw.is3D()) { - raw.strokeWeight(sw0); - raw.stroke(argb0); - raw.vertex(pt0[X], pt0[Y], pt0[Z]); - raw.strokeWeight(sw1); - raw.stroke(argb1); - raw.vertex(pt1[X], pt1[Y], pt1[Z]); - } else if (raw.is2D()) { - float sx0 = g.screenX(pt0[0], pt0[1], pt0[2], pt0[3]), sy0 = g.screenY(pt0[0], pt0[1], pt0[2], pt0[3]); - float sx1 = g.screenX(pt1[0], pt1[1], pt1[2], pt1[3]), sy1 = g.screenY(pt1[0], pt1[1], pt1[2], pt1[3]); - raw.strokeWeight(sw0); - raw.stroke(argb0); - raw.vertex(sx0, sy0); - raw.strokeWeight(sw1); - raw.stroke(argb1); - raw.vertex(sx1, sy1); - } - } - } - - raw.endShape(); - } - - - protected void renderFill(PGraphicsOpenGL g, PImage textureImage) { - PTexture tex = null; - if (textureImage != null) { - tex = g.getTexture(textureImage); - if (tex != null) { - pgl.enableTexturing(tex.glTarget); - pgl.glBindTexture(tex.glTarget, tex.glID); - } - } - - FillShader shader = g.getFillShader(g.lights, tex != null); - shader.start(); - - IndexCache cache = tessGeo.fillIndexCache; - for (int n = firstFillIndexCache; n <= lastFillIndexCache; n++) { - int ioffset = cache.indexOffset[n]; - int icount = cache.indexCount[n]; - int voffset = cache.vertexOffset[n]; - - shader.setVertexAttribute(root.glFillVertexBufferID, 4, PGL.GL_FLOAT, 0, 4 * voffset * PGL.SIZEOF_FLOAT); - shader.setColorAttribute(root.glFillColorBufferID, 4, PGL.GL_UNSIGNED_BYTE, 0, 4 * voffset * PGL.SIZEOF_BYTE); - - if (g.lights) { - shader.setNormalAttribute(root.glFillNormalBufferID, 3, PGL.GL_FLOAT, 0, 3 * voffset * PGL.SIZEOF_FLOAT); - shader.setAmbientAttribute(root.glFillAmbientBufferID, 4, PGL.GL_UNSIGNED_BYTE, 0, 4 * voffset * PGL.SIZEOF_BYTE); - shader.setSpecularAttribute(root.glFillSpecularBufferID, 4, PGL.GL_UNSIGNED_BYTE, 0, 4 * voffset * PGL.SIZEOF_BYTE); - shader.setEmissiveAttribute(root.glFillEmissiveBufferID, 4, PGL.GL_UNSIGNED_BYTE, 0, 4 * voffset * PGL.SIZEOF_BYTE); - shader.setShininessAttribute(root.glFillShininessBufferID, 1, PGL.GL_FLOAT, 0, voffset * PGL.SIZEOF_FLOAT); - } - - if (tex != null) { - shader.setTexCoordAttribute(root.glFillTexCoordBufferID, 2, PGL.GL_FLOAT, 0, 2 * voffset * PGL.SIZEOF_FLOAT); - shader.setTexture(tex); - } - - pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, root.glFillIndexBufferID); - pgl.glDrawElements(PGL.GL_TRIANGLES, icount, PGL.INDEX_TYPE, ioffset * PGL.SIZEOF_INDEX); - pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, 0); - } - - shader.stop(); - - if (tex != null) { - pgl.glBindTexture(tex.glTarget, 0); - pgl.disableTexturing(tex.glTarget); - } - } - - protected void rawFill(PGraphicsOpenGL g, PImage textureImage) { - PGraphics raw = g.getRaw(); - - raw.colorMode(RGB); - raw.noStroke(); - raw.beginShape(TRIANGLES); - - float[] vertices = tessGeo.fillVertices; - int[] color = tessGeo.fillColors; - float[] uv = tessGeo.fillTexcoords; - short[] indices = tessGeo.fillIndices; - - IndexCache cache = tessGeo.fillIndexCache; - for (int n = firstFillIndexCache; n <= lastFillIndexCache; n++) { - int ioffset = cache.indexOffset[n]; - int icount = cache.indexCount[n]; - int voffset = cache.vertexOffset[n]; - - for (int tr = ioffset / 3; tr < (ioffset + icount) / 3; tr++) { - int i0 = voffset + indices[3 * tr + 0]; - int i1 = voffset + indices[3 * tr + 1]; - int i2 = voffset + indices[3 * tr + 2]; - - 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}; - int argb0 = PGL.nativeToJavaARGB(color[i0]); - int argb1 = PGL.nativeToJavaARGB(color[i1]); - int argb2 = PGL.nativeToJavaARGB(color[i2]); - - PApplet.arrayCopy(vertices, 4 * i0, src0, 0, 4); - PApplet.arrayCopy(vertices, 4 * i1, src1, 0, 4); - PApplet.arrayCopy(vertices, 4 * i2, src2, 0, 4); - // Applying any transformation is currently stored in the - // modelview matrix of the renderer. - g.modelview.mult(src0, pt0); - g.modelview.mult(src1, pt1); - g.modelview.mult(src2, pt2); - - if (textureImage != null) { - raw.texture(textureImage); - 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 = g.screenX(pt0[0], pt0[1], pt0[2], pt0[3]), sy0 = g.screenY(pt0[0], pt0[1], pt0[2], pt0[3]); - float sx1 = g.screenX(pt1[0], pt1[1], pt1[2], pt1[3]), sy1 = g.screenY(pt1[0], pt1[1], pt1[2], pt1[3]); - float sx2 = g.screenX(pt2[0], pt2[1], pt2[2], pt2[3]), sy2 = g.screenY(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 = g.screenX(pt0[0], pt0[1], pt0[2], pt0[3]), sy0 = g.screenY(pt0[0], pt0[1], pt0[2], pt0[3]); - float sx1 = g.screenX(pt1[0], pt1[1], pt1[2], pt1[3]), sy1 = g.screenY(pt1[0], pt1[1], pt1[2], pt1[3]); - float sx2 = g.screenX(pt2[0], pt2[1], pt2[2], pt2[3]), sy2 = g.screenY(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); - } - } - } - } - - raw.endShape(); - } } diff --git a/android/core/src/processing/opengl/PShapeOpenGL.java b/android/core/src/processing/opengl/PShapeOpenGL.java new file mode 100644 index 000000000..4fb73bc9b --- /dev/null +++ b/android/core/src/processing/opengl/PShapeOpenGL.java @@ -0,0 +1,4219 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2011-12 Ben Fry and Casey Reas + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation, version 2.1. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.opengl; + +import processing.core.PApplet; +import processing.core.PConstants; +import processing.core.PGraphics; +import processing.core.PImage; +import processing.core.PMatrix; +import processing.core.PMatrix2D; +import processing.core.PMatrix3D; +import processing.core.PShape; +import processing.core.PVector; +import processing.opengl.PGraphicsOpenGL.LineShader; +import processing.opengl.PGraphicsOpenGL.PointShader; +import processing.opengl.PGraphicsOpenGL.PolyShader; +import processing.opengl.PGraphicsOpenGL.IndexCache; +import processing.opengl.PGraphicsOpenGL.InGeometry; +import processing.opengl.PGraphicsOpenGL.TessGeometry; +import processing.opengl.PGraphicsOpenGL.Tessellator; + +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; +import java.util.Arrays; +import java.util.HashSet; + +/** + * This class holds a 3D model composed of vertices, normals, colors (per vertex) and + * texture coordinates (also per vertex). All this data is stored in Vertex Buffer Objects + * (VBO) in GPU memory for very fast access. + * OBJ loading implemented using code from Saito's OBJLoader library (http://code.google.com/p/saitoobjloader/) + * and OBJReader from Ahmet Kizilay (http://www.openprocessing.org/visuals/?visualID=191). + * By Andres Colubri + * + * + * Other formats to consider: + * AMF: http://en.wikipedia.org/wiki/Additive_Manufacturing_File_Format + * STL: http://en.wikipedia.org/wiki/STL_(file_format) + * OFF: http://people.sc.fsu.edu/~jburkardt/data/off/off.html(file_format) + * DXF: http://en.wikipedia.org/wiki/AutoCAD_DXF + */ +public class PShapeOpenGL extends PShape { + static protected final int TRANSLATE = 0; + static protected final int ROTATE = 1; + static protected final int SCALE = 2; + static protected final int MATRIX = 3; + + protected PGraphicsOpenGL pg; + protected PGL pgl; + protected PGL.Context context; // The context that created this shape. + + protected PShapeOpenGL root; + + // ........................................................ + + // Input, tessellated geometry + + protected InGeometry inGeo; + protected TessGeometry tessGeo; + protected Tessellator tessellator; + + // ........................................................ + + // Texturing + + protected HashSet textures; + protected PImage texture; + protected boolean strokedTexture; + + // ........................................................ + + // OpenGL buffers + + public int glPolyVertexBufferID; + public int glPolyColorBufferID; + public int glPolyNormalBufferID; + public int glPolyTexcoordBufferID; + public int glPolyAmbientBufferID; + public int glPolySpecularBufferID; + public int glPolyEmissiveBufferID; + public int glPolyShininessBufferID; + public int glPolyIndexBufferID; + + public int glLineVertexBufferID; + public int glLineColorBufferID; + public int glLineAttribBufferID; + public int glLineIndexBufferID; + + public int glPointVertexBufferID; + public int glPointColorBufferID; + public int glPointAttribBufferID; + public int glPointIndexBufferID; + + // ........................................................ + + // Offsets for geometry aggregation and update. + + protected int polyVertCopyOffset; + protected int polyIndCopyOffset; + protected int lineVertCopyOffset; + protected int lineIndCopyOffset; + protected int pointVertCopyOffset; + protected int pointIndCopyOffset; + + protected int polyIndexOffset; + protected int polyVertexOffset; + protected int polyVertexAbs; + protected int polyVertexRel; + + protected int lineIndexOffset; + protected int lineVertexOffset; + protected int lineVertexAbs; + protected int lineVertexRel; + + protected int pointIndexOffset; + protected int pointVertexOffset; + protected int pointVertexAbs; + protected int pointVertexRel; + + protected int firstPolyIndexCache; + protected int lastPolyIndexCache; + protected int firstLineIndexCache; + protected int lastLineIndexCache; + protected int firstPointIndexCache; + protected int lastPointIndexCache; + + protected int firstPolyVertex; + protected int lastPolyVertex; + protected int firstLineVertex; + protected int lastLineVertex; + protected int firstPointVertex; + protected int lastPointVertex; + + // ........................................................ + + // Geometric transformations. + + protected PMatrix transform; + + // ........................................................ + + // State/rendering flags + + protected boolean tessellated; + protected boolean needBufferInit; + + protected boolean isSolid; + protected boolean isClosed; + + protected boolean openContour = false; + protected boolean breakShape = false; + protected boolean shapeEnded = false; + + // These variables indicate if the shape contains + // polygon, line and/or point geometry. In the case of + // 3D shapes, poly geometry is coincident with the fill + // triangles, as the lines and points are stored separately. + // However, for 2D shapes the poly geometry contains all of + // the three since the same rendering shader applies to + // fill, line and point geometry. + protected boolean hasPolys; + protected boolean hasLines; + protected boolean hasPoints; + + // ........................................................ + + // Modes inherited from renderer + + protected int textureMode; + protected int rectMode; + protected int ellipseMode; + protected int shapeMode; + protected int imageMode; + + // ........................................................ + + // Bezier and Catmull-Rom curves + + protected int bezierDetail = 20; + protected int curveDetail = 20; + protected float curveTightness = 0; + + // ........................................................ + + // Normals + + protected float normalX, normalY, normalZ; + + // normal calculated per triangle + static protected final int NORMAL_MODE_AUTO = 0; + // one normal manually specified per shape + static protected final int NORMAL_MODE_SHAPE = 1; + // normals specified for each shape vertex + static protected final int NORMAL_MODE_VERTEX = 2; + + // Current mode for normals, one of AUTO, SHAPE, or VERTEX + protected int normalMode; + + // ........................................................ + + // Modification variables (used only by the root shape) + + protected boolean modified; + + protected boolean modifiedPolyVertices; + protected boolean modifiedPolyColors; + protected boolean modifiedPolyNormals; + protected boolean modifiedPolyTexcoords; + protected boolean modifiedPolyAmbient; + protected boolean modifiedPolySpecular; + protected boolean modifiedPolyEmissive; + protected boolean modifiedPolyShininess; + + protected boolean modifiedLineVertices; + protected boolean modifiedLineColors; + protected boolean modifiedLineAttributes; + + protected boolean modifiedPointVertices; + protected boolean modifiedPointColors; + protected boolean modifiedPointAttributes; + + protected int firstModifiedPolyVertex; + protected int lastModifiedPolyVertex; + protected int firstModifiedPolyColor; + protected int lastModifiedPolyColor; + protected int firstModifiedPolyNormal; + protected int lastModifiedPolyNormal; + protected int firstModifiedPolyTexcoord; + protected int lastModifiedPolyTexcoord; + protected int firstModifiedPolyAmbient; + protected int lastModifiedPolyAmbient; + protected int firstModifiedPolySpecular; + protected int lastModifiedPolySpecular; + protected int firstModifiedPolyEmissive; + protected int lastModifiedPolyEmissive; + protected int firstModifiedPolyShininess; + protected int lastModifiedPolyShininess; + + protected int firstModifiedLineVertex; + protected int lastModifiedLineVertex; + protected int firstModifiedLineColor; + protected int lastModifiedLineColor; + protected int firstModifiedLineAttribute; + protected int lastModifiedLineAttribute; + + protected int firstModifiedPointVertex; + protected int lastModifiedPointVertex; + protected int firstModifiedPointColor; + protected int lastModifiedPointColor; + protected int firstModifiedPointAttribute; + protected int lastModifiedPointAttribute; + + + PShapeOpenGL() { + } + + + public PShapeOpenGL(PApplet parent, int family) { + pg = (PGraphicsOpenGL)parent.g; + pgl = pg.pgl; + context = pgl.createEmptyContext(); + + glPolyVertexBufferID = 0; + glPolyColorBufferID = 0; + glPolyNormalBufferID = 0; + glPolyTexcoordBufferID = 0; + glPolyAmbientBufferID = 0; + glPolySpecularBufferID = 0; + glPolyEmissiveBufferID = 0; + glPolyShininessBufferID = 0; + glPolyIndexBufferID = 0; + + glLineVertexBufferID = 0; + glLineColorBufferID = 0; + glLineAttribBufferID = 0; + glLineIndexBufferID = 0; + + glPointVertexBufferID = 0; + glPointColorBufferID = 0; + glPointAttribBufferID = 0; + glPointIndexBufferID = 0; + + this.tessellator = PGraphicsOpenGL.tessellator; + this.family = family; + this.root = this; + this.parent = null; + this.tessellated = false; + + if (family == GEOMETRY || family == PRIMITIVE || family == PATH) { + inGeo = pg.newInGeometry(PGraphicsOpenGL.RETAINED); + } + + // Modes are retrieved from the current values in the renderer. + textureMode = pg.textureMode; + rectMode = pg.rectMode; + ellipseMode = pg.ellipseMode; + shapeMode = pg.shapeMode; + imageMode = pg.imageMode; + + colorMode(pg.colorMode, pg.colorModeX, pg.colorModeY, pg.colorModeZ, pg.colorModeA); + + // Initial values for fill, stroke and tint colors are also imported from the renderer. + // This is particular relevant for primitive shapes, since is not possible to set + // their color separately when creating them, and their input vertices are actually + // generated at rendering time, by which the color configuration of the renderer might + // have changed. + fill = pg.fill; + fillColor = pg.fillColor; + + stroke = pg.stroke; + strokeColor = pg.strokeColor; + strokeWeight = pg.strokeWeight; + strokeCap = pg.strokeCap; + strokeJoin = pg.strokeJoin; + + tint = pg.tint; + tintColor = pg.tintColor; + + ambientColor = pg.ambientColor; + specularColor = pg.specularColor; + emissiveColor = pg.emissiveColor; + shininess = pg.shininess; + + normalX = normalY = 0; + normalZ = 1; + + normalMode = NORMAL_MODE_AUTO; + + if (family == GROUP) { + // GROUP shapes are always marked as ended. + shapeEnded = true; + } + } + + + public void addChild(PShape child) { + if (child instanceof PShapeOpenGL) { + if (family == GROUP) { + PShapeOpenGL c3d = (PShapeOpenGL)child; + + super.addChild(c3d); + c3d.updateRoot(root); + markForTessellation(); + + if (c3d.family == GROUP) { + if (c3d.textures != null) { + for (PImage tex: c3d.textures) { + addTexture(tex); + } + } + if (c3d.strokedTexture) { + strokedTexture(true); + } + } else { + if (c3d.texture != null) { + addTexture(c3d.texture); + if (c3d.stroke) { + strokedTexture(true); + } + } + } + + } else { + PGraphics.showWarning("Cannot add child shape to non-group shape."); + } + } else { + PGraphics.showWarning("Shape must be 3D to be added to the group."); + } + } + + + public void updateRoot(PShape root) { + this.root = (PShapeOpenGL) root; + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL)children[i]; + child.updateRoot(root); + } + } + } + + + protected void finalize() throws Throwable { + try { + finalizePolyBuffers(); + finalizeLineBuffers(); + finalizePointBuffers(); + } finally { + super.finalize(); + } + } + + + protected void finalizePolyBuffers() { + if (glPolyVertexBufferID != 0) { + pg.finalizeVertexBufferObject(glPolyVertexBufferID, context.code()); + } + + if (glPolyColorBufferID != 0) { + pg.finalizeVertexBufferObject(glPolyColorBufferID, context.code()); + } + + if (glPolyNormalBufferID != 0) { + pg.finalizeVertexBufferObject(glPolyNormalBufferID, context.code()); + } + + if (glPolyTexcoordBufferID != 0) { + pg.finalizeVertexBufferObject(glPolyTexcoordBufferID, context.code()); + } + + if (glPolyAmbientBufferID != 0) { + pg.finalizeVertexBufferObject(glPolyAmbientBufferID, context.code()); + } + + if (glPolySpecularBufferID != 0) { + pg.finalizeVertexBufferObject(glPolySpecularBufferID, context.code()); + } + + if (glPolyEmissiveBufferID != 0) { + pg.finalizeVertexBufferObject(glPolyEmissiveBufferID, context.code()); + } + + if (glPolyShininessBufferID != 0) { + pg.finalizeVertexBufferObject(glPolyShininessBufferID, context.code()); + } + + if (glPolyIndexBufferID != 0) { + pg.finalizeVertexBufferObject(glPolyIndexBufferID, context.code()); + } + } + + + protected void finalizeLineBuffers() { + if (glLineVertexBufferID != 0) { + pg.finalizeVertexBufferObject(glLineVertexBufferID, context.code()); + } + + if (glLineColorBufferID != 0) { + pg.finalizeVertexBufferObject(glLineColorBufferID, context.code()); + } + + if (glLineAttribBufferID != 0) { + pg.finalizeVertexBufferObject(glLineAttribBufferID, context.code()); + } + + if (glLineIndexBufferID != 0) { + pg.finalizeVertexBufferObject(glLineIndexBufferID, context.code()); + } + } + + + protected void finalizePointBuffers() { + if (glPointVertexBufferID != 0) { + pg.finalizeVertexBufferObject(glPointVertexBufferID, context.code()); + } + + if (glPointColorBufferID != 0) { + pg.finalizeVertexBufferObject(glPointColorBufferID, context.code()); + } + + if (glPointAttribBufferID != 0) { + pg.finalizeVertexBufferObject(glPointAttribBufferID, context.code()); + } + + if (glPointIndexBufferID != 0) { + pg.finalizeVertexBufferObject(glPointIndexBufferID, context.code()); + } + } + + + /////////////////////////////////////////////////////////// + + // + + // Query methods + + + public float getWidth() { + PVector min = new PVector(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY); + PVector max = new PVector(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY); + if (shapeEnded) { + getVertexMin(min); + getVertexMax(max); + } + width = max.x - min.x; + return width; + } + + + public float getHeight() { + PVector min = new PVector(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY); + PVector max = new PVector(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY); + if (shapeEnded) { + getVertexMin(min); + getVertexMax(max); + } + height = max.y - min.y; + return height; + } + + + public float getDepth() { + PVector min = new PVector(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY); + PVector max = new PVector(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY); + if (shapeEnded) { + getVertexMin(min); + getVertexMax(max); + } + depth = max.z - min.z; + return depth; + } + + + public PVector getTop(PVector top) { + if (top == null) { + top = new PVector(); + } + top.set(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY); + getVertexMin(top); + return top; + } + + + public PVector getBottom(PVector bottom) { + if (bottom == null) { + bottom = new PVector(); + } + bottom.set(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY); + getVertexMax(bottom); + return bottom; + } + + + protected void getVertexMin(PVector min) { + updateTessellation(); + + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.getVertexMin(min); + } + } else { + if (hasPolys) tessGeo.getPolyVertexMin(min, firstPolyVertex, lastPolyVertex); + if (is3D()) { + if (hasLines) tessGeo.getLineVertexMin(min, firstLineVertex, lastLineVertex); + if (hasPoints) tessGeo.getPointVertexMin(min, firstPointVertex, lastPointVertex); + } + } + } + + + protected void getVertexMax(PVector max) { + updateTessellation(); + + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.getVertexMax(max); + } + } else { + if (hasPolys) tessGeo.getPolyVertexMax(max, firstPolyVertex, lastPolyVertex); + if (is3D()) { + if (hasLines) tessGeo.getLineVertexMax(max, firstLineVertex, lastLineVertex); + if (hasPoints) tessGeo.getPointVertexMax(max, firstPointVertex, lastPointVertex); + } + } + } + + + protected int getVertexSum(PVector sum, int count) { + updateTessellation(); + + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + count += child.getVertexSum(sum, count); + } + } else { + if (hasPolys) count += tessGeo.getPolyVertexSum(sum, firstPolyVertex, lastPolyVertex); + if (is3D()) { + if (hasLines) count += tessGeo.getLineVertexSum(sum, firstLineVertex, lastLineVertex); + if (hasPoints) count += tessGeo.getPointVertexSum(sum, firstPointVertex, lastPointVertex); + } + } + return count; + } + + + /////////////////////////////////////////////////////////// + + // + + // Drawing methods + + + public void textureMode(int mode) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.textureMode(mode); + } + } else { + textureMode = mode; + } + } + + + public void texture(PImage tex) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.texture(tex); + } + } else { + PImage tex0 = texture; + texture = tex; + if (tex0 != tex && parent != null) { + ((PShapeOpenGL)parent).removeTexture(tex); + } + if (parent != null) { + ((PShapeOpenGL)parent).addTexture(texture); + if (is2D() && stroke) { + ((PShapeOpenGL)parent).strokedTexture(true); + } + } + } + } + + + public void noTexture() { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.noTexture(); + } + } else { + PImage tex0 = texture; + texture = null; + if (tex0 != null && parent != null) { + ((PShapeOpenGL)parent).removeTexture(tex0); + if (is2D()) { + ((PShapeOpenGL)parent).strokedTexture(false); + } + } + } + } + + + protected void addTexture(PImage tex) { + if (textures == null) { + textures = new HashSet(); + } + textures.add(tex); + if (parent != null) { + ((PShapeOpenGL)parent).addTexture(tex); + } + } + + + protected void removeTexture(PImage tex) { + if (textures == null || !textures.contains(tex)) return; // Nothing to remove. + + // First check that none of the child shapes + // have texture tex... + boolean childHasTex = false; + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + if (child.hasTexture(tex)) { + childHasTex = true; + break; + } + } + + if (!childHasTex) { + // ...if not, it is safe to remove from this shape. + textures.remove(tex); + if (textures.size() == 0) { + textures = null; + } + } + + // Since this shape and all its child shapes don't contain + // tex anymore, we now can remove it from the parent. + if (parent != null) { + ((PShapeOpenGL)parent).removeTexture(tex); + } + } + + + protected void strokedTexture(boolean newValue) { + if (strokedTexture == newValue) return; // Nothing to change. + + if (newValue) { + strokedTexture = true; + } else { + // First check that none of the child shapes + // have have a stroked texture... + boolean childHasStrokedTex = false; + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + if (child.hasStrokedTexture()) { + childHasStrokedTex = true; + break; + } + } + + if (!childHasStrokedTex) { + // ...if not, it is safe to mark this shape as without + // stroked texture. + strokedTexture = false; + } + } + + // Now we can update the parent shape. + if (parent != null) { + ((PShapeOpenGL)parent).strokedTexture(newValue); + } + } + + + protected boolean hasTexture(PImage tex) { + if (family == GROUP) { + return textures != null && textures.contains(tex); + } else { + return texture == tex; + } + } + + + protected boolean hasStrokedTexture() { + if (family == GROUP) { + return strokedTexture; + } else { + return texture != null && stroke; + } + } + + + public void solid(boolean solid) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.solid(solid); + } + } else { + isSolid = solid; + } + } + + + public void beginContour() { + if (family == GROUP) { + PGraphics.showWarning("Cannot begin contour in GROUP shapes"); + return; + } + + if (openContour) { + PGraphics.showWarning("P3D: Already called beginContour()."); + return; + } + openContour = true; + } + + + public void endContour() { + if (family == GROUP) { + PGraphics.showWarning("Cannot end contour in GROUP shapes"); + return; + } + + if (!openContour) { + PGraphics.showWarning("P3D: Need to call beginContour() first."); + return; + } + openContour = false; + breakShape = true; + } + + + public void vertex(float x, float y) { + vertexImpl(x, y, 0, 0, 0); + } + + + public void vertex(float x, float y, float u, float v) { + vertexImpl(x, y, 0, u, v); + } + + + public void vertex(float x, float y, float z) { + vertexImpl(x, y, z, 0, 0); + } + + + public void vertex(float x, float y, float z, float u, float v) { + vertexImpl(x, y, z, u, v); + } + + + protected void vertexImpl(float x, float y, float z, float u, float v) { + if (family == GROUP) { + PGraphics.showWarning("Cannot add vertices to GROUP shape"); + return; + } + + boolean textured = texture != null; + int fcolor = 0x00; + if (fill || textured) { + if (!textured) { + fcolor = fillColor; + } else { + if (tint) { + fcolor = tintColor; + } else { + fcolor = 0xffFFFFFF; + } + } + } + + if (texture != null && textureMode == IMAGE) { + u = PApplet.min(1, u / texture.width); + v = PApplet.min(1, v / texture.height); + } + + int scolor = 0x00; + float sweight = 0; + if (stroke) { + scolor = strokeColor; + sweight = strokeWeight; + } + + inGeo.addVertex(x, y, z, + fcolor, + normalX, normalY, normalZ, + u, v, + scolor, sweight, + ambientColor, specularColor, emissiveColor, shininess, + vertexCode()); + + markForTessellation(); + } + + + protected int vertexCode() { + int code = VERTEX; + if (breakShape) { + code = BREAK; + breakShape = false; + } + return code; + } + + + public void normal(float nx, float ny, float nz) { + if (family == GROUP) { + PGraphics.showWarning("Cannot set normal in GROUP shape"); + return; + } + + normalX = nx; + normalY = ny; + normalZ = nz; + + // if drawing a shape and the normal hasn't been set yet, + // then we need to set the normals for each vertex so far + if (normalMode == NORMAL_MODE_AUTO) { + // One normal per begin/end shape + normalMode = NORMAL_MODE_SHAPE; + } else if (normalMode == NORMAL_MODE_SHAPE) { + // a separate normal for each vertex + normalMode = NORMAL_MODE_VERTEX; + } + } + + + public void end() { + end(OPEN); + } + + + public void end(int mode) { + if (family == GROUP) { + PGraphics.showWarning("Cannot end GROUP shape"); + return; + } + + // Input arrays are trimmed since they are expanded by doubling their old size, + // which might lead to arrays larger than the vertex counts. + inGeo.trim(); + + isClosed = mode == CLOSE; + markForTessellation(); + shapeEnded = true; + } + + + public void setParams(float[] source) { + if (family != PRIMITIVE) { + PGraphics.showWarning("Parameters can only be set to PRIMITIVE shapes"); + return; + } + + super.setParams(source); + markForTessellation(); + shapeEnded = true; + } + + + public void setPath(int vcount, float[][] verts, int ccount, int[] codes) { + if (family != PATH) { + PGraphics.showWarning("Vertex coordinates and codes can only be set to PATH shapes"); + return; + } + + super.setPath(vcount, verts, ccount, codes); + markForTessellation(); + shapeEnded = true; + } + + + ////////////////////////////////////////////////////////////// + + // Stroke cap/join/weight set/update + + + public void strokeWeight(float weight) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.strokeWeight(weight); + } + } else { + updateStrokeWeight(weight); + } + } + + + protected void updateStrokeWeight(float newWeight) { + if (PGraphicsOpenGL.same(strokeWeight, newWeight)) return; + float oldWeight = strokeWeight; + strokeWeight = newWeight; + + if (shapeEnded && tessellated && (hasLines || hasPoints)) { + float resizeFactor = newWeight / oldWeight; + + Arrays.fill(inGeo.strokeWeights, 0, inGeo.vertexCount, strokeWeight); + + if (hasLines) { + if (is3D()) { + for (int i = firstLineVertex; i <= lastLineVertex; i++) { + tessGeo.lineAttribs[4 * i + 3] *= resizeFactor; + } + root.setModifiedLineAttributes(firstLineVertex, lastLineVertex); + } else if (is2D()) { + // Changing the stroke weight on a 2D shape needs a + // re-tesellation in order to replace the old line + // geometry. + markForTessellation(); + } + } + + if (hasPoints) { + if (is3D()) { + for (int i = firstPointVertex; i <= lastPointVertex; i++) { + tessGeo.pointAttribs[2 * i + 0] *= resizeFactor; + tessGeo.pointAttribs[2 * i + 1] *= resizeFactor; + } + root.setModifiedPointAttributes(firstPointVertex, lastPointVertex); + } else if (is2D()) { + // Changing the stroke weight on a 2D shape needs a + // re-tesellation in order to replace the old point + // geometry. + markForTessellation(); + } + } + } + } + + + public void strokeJoin(int join) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.strokeJoin(join); + } + } else { + if (is2D() && strokeJoin != join) { + // Changing the stroke join on a 2D shape needs a + // re-tesellation in order to replace the old join + // geometry. + markForTessellation(); + } + strokeJoin = join; + } + } + + + public void strokeCap(int cap) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.strokeCap(cap); + } + } else { + if (is2D() && strokeCap != cap) { + // Changing the stroke cap on a 2D shape needs a + // re-tesellation in order to replace the old cap + // geometry. + markForTessellation(); + } + strokeCap = cap; + } + } + + + ////////////////////////////////////////////////////////////// + + // Fill set/update + + + public void noFill() { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.noFill(); + } + } else { + fill = false; + updateFillColor(0x0); + } + } + + + public void fill(int rgb) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.fill(rgb); + } + } else { + colorCalc(rgb); + fillFromCalc(); + } + } + + + public void fill(int rgb, float alpha) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.fill(rgb, alpha); + } + } else { + colorCalc(rgb, alpha); + fillFromCalc(); + } + } + + + public void fill(float gray) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.fill(gray); + } + } else { + colorCalc(gray); + fillFromCalc(); + } + } + + + public void fill(float gray, float alpha) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.fill(gray, alpha); + } + } else { + colorCalc(gray, alpha); + fillFromCalc(); + } + } + + + public void fill(float x, float y, float z) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.fill(x, y, z); + } + } else { + colorCalc(x, y, z); + fillFromCalc(); + } + } + + + public void fill(float x, float y, float z, float a) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.fill(x, y, z, a); + } + } else { + colorCalc(x, y, z, a); + fillFromCalc(); + } + } + + + protected void fillFromCalc() { + fill = true; + updateFillColor(calcColor); + } + + + protected void updateFillColor(int newFillColor) { + if (fillColor == newFillColor) return; + fillColor = newFillColor; + + if (shapeEnded && tessellated && hasPolys && texture == null) { + Arrays.fill(inGeo.colors, 0, inGeo.vertexCount, PGL.javaToNativeARGB(fillColor)); + if (is3D()) { + Arrays.fill(tessGeo.polyColors, firstPolyVertex, lastPolyVertex + 1, PGL.javaToNativeARGB(fillColor)); + root.setModifiedPolyColors(firstPolyVertex, lastPolyVertex); + } else if (is2D()) { + int last1 = lastPolyVertex + 1; + if (-1 < firstLineVertex) last1 = firstLineVertex; + if (-1 < firstPointVertex) last1 = firstPointVertex; + Arrays.fill(tessGeo.polyColors, firstPolyVertex, last1, PGL.javaToNativeARGB(fillColor)); + root.setModifiedPolyColors(firstPolyVertex, last1 - 1); + } + } + } + + + ////////////////////////////////////////////////////////////// + + // Stroke (color) set/update + + + public void noStroke() { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.noStroke(); + } + } else { + if (stroke) { + // Disabling stroke on a shape previously with + // stroke needs a re-tesellation in order to remove + // the additional geometry of lines and/or points. + markForTessellation(); + stroke = false; + } + updateStrokeColor(0x0); + if (is2D() && parent != null) { + ((PShapeOpenGL)parent).strokedTexture(false); + } + } + } + + + public void stroke(int rgb) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.stroke(rgb); + } + } else { + colorCalc(rgb); + strokeFromCalc(); + } + } + + + public void stroke(int rgb, float alpha) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.stroke(rgb, alpha); + } + } else { + colorCalc(rgb, alpha); + strokeFromCalc(); + } + } + + + public void stroke(float gray) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.stroke(gray); + } + } else { + colorCalc(gray); + strokeFromCalc(); + } + } + + + public void stroke(float gray, float alpha) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.stroke(gray, alpha); + } + } else { + colorCalc(gray, alpha); + strokeFromCalc(); + } + } + + + public void stroke(float x, float y, float z) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.stroke(x, y, z); + } + } else { + colorCalc(x, y, z); + strokeFromCalc(); + } + } + + + public void stroke(float x, float y, float z, float alpha) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.stroke(x, y, z, alpha); + } + } else { + colorCalc(x, y, z, alpha); + strokeFromCalc(); + } + } + + + protected void strokeFromCalc() { + if (!stroke) { + // Enabling stroke on a shape previously without + // stroke needs a re-tessellation in order to incorporate + // the additional geometry of lines and/or points. + markForTessellation(); + stroke = true; + } + updateStrokeColor(calcColor); + if (is2D() && texture != null && parent != null) { + ((PShapeOpenGL)parent).strokedTexture(true); + } + } + + + protected void updateStrokeColor(int newStrokeColor) { + if (strokeColor == newStrokeColor) return; + strokeColor = newStrokeColor; + + if (shapeEnded && tessellated && (hasLines || hasPoints)) { + Arrays.fill(inGeo.strokeColors, 0, inGeo.vertexCount, PGL.javaToNativeARGB(strokeColor)); + + if (hasLines) { + if (is3D()) { + Arrays.fill(tessGeo.lineColors, firstLineVertex, lastLineVertex + 1, PGL.javaToNativeARGB(strokeColor)); + root.setModifiedLineColors(firstLineVertex, lastLineVertex); + } else if (is2D()) { + Arrays.fill(tessGeo.polyColors, firstLineVertex, lastLineVertex + 1, PGL.javaToNativeARGB(strokeColor)); + root.setModifiedPolyColors(firstLineVertex, lastLineVertex); + } + } + + if (hasPoints) { + if (is3D()) { + Arrays.fill(tessGeo.pointColors, firstPointVertex, lastPointVertex + 1, PGL.javaToNativeARGB(strokeColor)); + root.setModifiedPointColors(firstPointVertex, lastPointVertex); + } else if (is2D()) { + Arrays.fill(tessGeo.polyColors, firstPointVertex, lastPointVertex + 1, PGL.javaToNativeARGB(strokeColor)); + root.setModifiedPolyColors(firstPointVertex, lastPointVertex); + } + } + } + } + + + ////////////////////////////////////////////////////////////// + + // Tint set/update + + + public void noTint() { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.noTint(); + } + } else { + tint = false; + updateTintColor(0x0); + } + } + + + public void tint(int rgb) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.tint(rgb); + } + } else { + colorCalc(rgb); + tintFromCalc(); + } + } + + + public void tint(int rgb, float alpha) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.tint(rgb, alpha); + } + } else { + colorCalc(rgb, alpha); + tintFromCalc(); + } + } + + + public void tint(float gray) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.tint(gray); + } + } else { + colorCalc(gray); + tintFromCalc(); + } + } + + + public void tint(float gray, float alpha) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.tint(gray, alpha); + } + } else { + colorCalc(gray, alpha); + tintFromCalc(); + } + } + + + public void tint(float x, float y, float z) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.tint(x, y, z); + } + } else { + colorCalc(x, y, z); + tintFromCalc(); + } + } + + + public void tint(float x, float y, float z, float alpha) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.tint(x, y, z, alpha); + } + } else { + colorCalc(x, y, z, alpha); + tintFromCalc(); + } + } + + + protected void tintFromCalc() { + tint = true; + updateTintColor(calcColor); + } + + + protected void updateTintColor(int newTintColor) { + if (tintColor == newTintColor) return; + tintColor = newTintColor; + + if (shapeEnded && tessellated && hasPolys && texture != null) { + Arrays.fill(inGeo.colors, 0, inGeo.vertexCount, PGL.javaToNativeARGB(tintColor)); + if (is3D()) { + Arrays.fill(tessGeo.polyColors, firstPolyVertex, lastPolyVertex + 1, PGL.javaToNativeARGB(tintColor)); + root.setModifiedPolyColors(firstPolyVertex, lastPolyVertex); + } else if (is2D()) { + int last1 = lastPolyVertex + 1; + if (-1 < firstLineVertex) last1 = firstLineVertex; + if (-1 < firstPointVertex) last1 = firstPointVertex; + Arrays.fill(tessGeo.polyColors, firstPolyVertex, last1, PGL.javaToNativeARGB(tintColor)); + root.setModifiedPolyColors(firstPolyVertex, last1 - 1); + } + } + } + + + ////////////////////////////////////////////////////////////// + + // Ambient set/update + + + public void ambient(int rgb) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.ambient(rgb); + } + } else { + colorCalc(rgb); + ambientFromCalc(); + } + } + + + public void ambient(float gray) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.ambient(gray); + } + } else { + colorCalc(gray); + ambientFromCalc(); + } + } + + + public void ambient(float x, float y, float z) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.ambient(x, y, z); + } + } else { + colorCalc(x, y, z); + ambientFromCalc(); + } + } + + + protected void ambientFromCalc() { + updateAmbientColor(calcColor); + } + + + protected void updateAmbientColor(int newAmbientColor) { + if (ambientColor == newAmbientColor) return; + ambientColor = newAmbientColor; + + if (shapeEnded && tessellated && hasPolys) { + Arrays.fill(inGeo.ambient, 0, inGeo.vertexCount, PGL.javaToNativeARGB(ambientColor)); + if (is3D()) { + Arrays.fill(tessGeo.polyAmbient, firstPolyVertex, lastPolyVertex = 1, PGL.javaToNativeARGB(ambientColor)); + root.setModifiedPolyAmbient(firstPolyVertex, lastPolyVertex); + } else if (is2D()) { + int last1 = lastPolyVertex + 1; + if (-1 < firstLineVertex) last1 = firstLineVertex; + if (-1 < firstPointVertex) last1 = firstPointVertex; + Arrays.fill(tessGeo.polyAmbient, firstPolyVertex, last1, PGL.javaToNativeARGB(ambientColor)); + root.setModifiedPolyColors(firstPolyVertex, last1 - 1); + } + } + } + + + ////////////////////////////////////////////////////////////// + + // Specular set/update + + + public void specular(int rgb) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.specular(rgb); + } + } else { + colorCalc(rgb); + specularFromCalc(); + } + } + + + public void specular(float gray) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.specular(gray); + } + } else { + colorCalc(gray); + specularFromCalc(); + } + } + + + public void specular(float x, float y, float z) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.specular(x, y, z); + } + } else { + colorCalc(x, y, z); + specularFromCalc(); + } + } + + + protected void specularFromCalc() { + updateSpecularColor(calcColor); + } + + + protected void updateSpecularColor(int newSpecularColor) { + if (specularColor == newSpecularColor) return; + specularColor = newSpecularColor; + + if (shapeEnded && tessellated && hasPolys) { + Arrays.fill(inGeo.specular, 0, inGeo.vertexCount, PGL.javaToNativeARGB(specularColor)); + if (is3D()) { + Arrays.fill(tessGeo.polySpecular, firstPolyVertex, lastPolyVertex + 1, PGL.javaToNativeARGB(specularColor)); + root.setModifiedPolySpecular(firstPolyVertex, lastPolyVertex); + } else if (is2D()) { + int last1 = lastPolyVertex + 1; + if (-1 < firstLineVertex) last1 = firstLineVertex; + if (-1 < firstPointVertex) last1 = firstPointVertex; + Arrays.fill(tessGeo.polySpecular, firstPolyVertex, last1, PGL.javaToNativeARGB(specularColor)); + root.setModifiedPolyColors(firstPolyVertex, last1 - 1); + } + } + } + + + ////////////////////////////////////////////////////////////// + + // Emissive set/update + + + public void emissive(int rgb) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.emissive(rgb); + } + } else { + colorCalc(rgb); + emissiveFromCalc(); + } + } + + + public void emissive(float gray) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.emissive(gray); + } + } else { + colorCalc(gray); + emissiveFromCalc(); + } + } + + + public void emissive(float x, float y, float z) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.emissive(x, y, z); + } + } else { + colorCalc(x, y, z); + emissiveFromCalc(); + } + } + + + protected void emissiveFromCalc() { + updateEmissiveColor(calcColor); + } + + + protected void updateEmissiveColor(int newEmissiveColor) { + if (emissiveColor == newEmissiveColor) return; + emissiveColor = newEmissiveColor; + + if (shapeEnded && tessellated && 0 < tessGeo.polyVertexCount) { + Arrays.fill(inGeo.emissive, 0, inGeo.vertexCount, PGL.javaToNativeARGB(emissiveColor)); + if (is3D()) { + Arrays.fill(tessGeo.polyEmissive, firstPolyVertex, lastPolyVertex + 1, PGL.javaToNativeARGB(emissiveColor)); + root.setModifiedPolyEmissive(firstPolyVertex, lastPolyVertex); + } else if (is2D()) { + int last1 = lastPolyVertex + 1; + if (-1 < firstLineVertex) last1 = firstLineVertex; + if (-1 < firstPointVertex) last1 = firstPointVertex; + Arrays.fill(tessGeo.polyEmissive, firstPolyVertex, last1, PGL.javaToNativeARGB(emissiveColor)); + root.setModifiedPolyColors(firstPolyVertex, last1 - 1); + } + } + } + + + ////////////////////////////////////////////////////////////// + + // Shininess set/update + + + public void shininess(float shine) { + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.shininess(shine); + } + } else { + updateShininessFactor(shine); + } + } + + + protected void updateShininessFactor(float newShininess) { + if (PGraphicsOpenGL.same(shininess, newShininess)) return; + shininess = newShininess; + + if (shapeEnded && tessellated && hasPolys) { + Arrays.fill(inGeo.shininess, 0, inGeo.vertexCount, shininess); + if (is3D()) { + Arrays.fill(tessGeo.polyShininess, firstPolyVertex, lastPolyVertex + 1, shininess); + root.setModifiedPolyShininess(firstPolyVertex, lastPolyVertex); + } else if (is2D()) { + int last1 = lastPolyVertex + 1; + if (-1 < firstLineVertex) last1 = firstLineVertex; + if (-1 < firstPointVertex) last1 = firstPointVertex; + Arrays.fill(tessGeo.polyShininess, firstPolyVertex, last1, shininess); + root.setModifiedPolyColors(firstPolyVertex, last1 - 1); + } + } + } + + + /////////////////////////////////////////////////////////// + + // + + // Geometric transformations + + + public void translate(float tx, float ty) { + transform(TRANSLATE, tx, ty); + } + + + public void translate(float tx, float ty, float tz) { + transform(TRANSLATE, tx, ty, tz); + } + + + public void rotate(float angle) { + transform(ROTATE, angle); + } + + + public void rotate(float angle, float v0, float v1, float v2) { + transform(ROTATE, angle, v0, v1, v2); + } + + + public void scale(float s) { + transform(SCALE, s, s); + } + + + public void scale(float x, float y) { + transform(SCALE, x, y); + } + + + public void scale(float x, float y, float z) { + transform(SCALE, x, y, z); + } + + + public void applyMatrix(PMatrix2D source) { + transform(MATRIX, source.m00, source.m01, source.m02, + source.m10, source.m11, source.m12); + } + + + public void applyMatrix(float n00, float n01, float n02, + float n10, float n11, float n12) { + transform(MATRIX, n00, n01, n02, + n10, n11, n12); + } + + + public void applyMatrix(float n00, float n01, float n02, float n03, + float n10, float n11, float n12, float n13, + float n20, float n21, float n22, float n23, + float n30, float n31, float n32, float n33) { + transform(MATRIX, n00, n01, n02, n03, + n10, n11, n12, n13, + n20, n21, n22, n23, + n30, n31, n32, n33); + } + + + public void resetMatrix() { + if (shapeEnded && matrix != null) { + if (family == GROUP) { + updateTessellation(); + } + boolean res = matrix.invert(); + if (res) { + if (tessellated) { + applyMatrixImpl(matrix); + } + matrix = null; + } else { + PGraphics.showWarning("The transformation matrix cannot be inverted"); + } + } + } + + + protected void transform(int type, float... args) { + int dimensions; + if (type == ROTATE) { + dimensions = args.length == 1 ? 2 : 3; + } else if (type == MATRIX) { + dimensions = args.length == 6 ? 2 : 3; + } else { + dimensions = args.length; + } + transformImpl(type, dimensions, args); + } + + + protected void transformImpl(int type, int ncoords, float... args) { + checkMatrix(ncoords); + calcTransform(type, ncoords, args); + if (tessellated) { + applyMatrixImpl(transform); + } + } + + + protected void calcTransform(int type, int dimensions, float... args) { + if (transform == null) { + if (dimensions == 2) { + transform = new PMatrix2D(); + } else { + transform = new PMatrix3D(); + } + } else { + transform.reset(); + } + + switch (type) { + case TRANSLATE: + if (dimensions == 3) { + transform.translate(args[0], args[1], args[2]); + } else { + transform.translate(args[0], args[1]); + } + break; + case ROTATE: + if (dimensions == 3) { + transform.rotate(args[0], args[1], args[2], args[3]); + } else { + transform.rotate(args[0]); + } + break; + case SCALE: + if (dimensions == 3) { + transform.scale(args[0], args[1], args[2]); + } else { + transform.scale(args[0], args[1]); + } + break; + case MATRIX: + if (dimensions == 3) { + transform.set(args[ 0], args[ 1], args[ 2], args[ 3], + args[ 4], args[ 5], args[ 6], args[ 7], + args[ 8], args[ 9], args[10], args[11], + args[12], args[13], args[14], args[15]); + } else { + transform.set(args[0], args[1], args[2], + args[3], args[4], args[5]); + } + break; + } + matrix.apply(transform); + } + + + protected void applyMatrixImpl(PMatrix matrix) { + if (hasPolys) { + tessGeo.applyMatrixOnPolyGeometry(matrix, firstPolyVertex, lastPolyVertex); + root.setModifiedPolyVertices(firstPolyVertex, lastPolyVertex); + root.setModifiedPolyNormals(firstPolyVertex, lastPolyVertex); + } + + if (is3D()) { + if (hasLines) { + tessGeo.applyMatrixOnLineGeometry(matrix, firstLineVertex, lastLineVertex); + root.setModifiedLineVertices(firstLineVertex, lastLineVertex); + root.setModifiedLineAttributes(firstLineVertex, lastLineVertex); + } + + if (hasPoints) { + tessGeo.applyMatrixOnPointGeometry(matrix, firstPointVertex, lastPointVertex); + root.setModifiedPointVertices(firstPointVertex, lastPointVertex); + } + } + } + + + /////////////////////////////////////////////////////////// + + // + + // Bezier curves + + + public void bezierDetail(int detail) { + bezierDetail = detail; + pg.bezierDetail(detail); + } + + + public void bezierVertex(float x2, float y2, + float x3, float y3, + float x4, float y4) { + bezierVertexImpl(x2, y2, 0, + x3, y3, 0, + x4, y4, 0); + } + + + public void bezierVertex(float x2, float y2, float z2, + float x3, float y3, float z3, + float x4, float y4, float z4) { + bezierVertexImpl(x2, y2, z2, + x3, y3, z3, + x4, y4, z4); + } + + + protected void bezierVertexImpl(float x2, float y2, float z2, + float x3, float y3, float z3, + float x4, float y4, float z4) { + inGeo.setMaterial(fillColor, strokeColor, strokeWeight, + ambientColor, specularColor, emissiveColor, shininess); + inGeo.setNormal(normalX, normalY, normalZ); + inGeo.addBezierVertex(x2, y2, z2, + x3, y3, z3, + x4, y4, z4, + fill, stroke, bezierDetail, vertexCode(), kind); + } + + + public void quadraticVertex(float cx, float cy, + float x3, float y3) { + quadraticVertexImpl(cx, cy, 0, + x3, y3, 0); + } + + + public void quadraticVertex(float cx, float cy, float cz, + float x3, float y3, float z3) { + quadraticVertexImpl(cx, cy, cz, + x3, y3, z3); + } + + + protected void quadraticVertexImpl(float cx, float cy, float cz, + float x3, float y3, float z3) { + inGeo.setMaterial(fillColor, strokeColor, strokeWeight, + ambientColor, specularColor, emissiveColor, shininess); + inGeo.setNormal(normalX, normalY, normalZ); + inGeo.addQuadraticVertex(cx, cy, cz, + x3, y3, z3, + fill, stroke, bezierDetail, vertexCode(), kind); + } + + + /////////////////////////////////////////////////////////// + + // + + // Catmull-Rom curves + + + public void curveDetail(int detail) { + curveDetail = detail; + pg.curveDetail(detail); + } + + + public void curveTightness(float tightness) { + curveTightness = tightness; + pg.curveTightness(tightness); + } + + + public void curveVertex(float x, float y) { + curveVertexImpl(x, y, 0); + } + + + public void curveVertex(float x, float y, float z) { + curveVertexImpl(x, y, z); + } + + + protected void curveVertexImpl(float x, float y, float z) { + inGeo.setMaterial(fillColor, strokeColor, strokeWeight, + ambientColor, specularColor, emissiveColor, shininess); + inGeo.setNormal(normalX, normalY, normalZ); + inGeo.addCurveVertex(x, y, z, + fill, stroke, curveDetail, vertexCode(), kind); + } + + + /////////////////////////////////////////////////////////// + + // + + // Setters/getters of individual vertices + + + public int getVertexCount() { + return inGeo.vertexCount; + } + + + public PVector getVertex(int index, PVector vec) { + if (vec == null) { + vec = new PVector(); + } + vec.x = inGeo.vertices[3 * index + 0]; + vec.y = inGeo.vertices[3 * index + 1]; + vec.z = inGeo.vertices[3 * index + 2]; + return vec; + } + + + public float getVertexX(int index) { + return inGeo.vertices[3 * index + 0]; + } + + + public float getVertexY(int index) { + return inGeo.vertices[3 * index + 1]; + } + + + public float getVertexZ(int index) { + return inGeo.vertices[3 * index + 2]; + } + + + public void setVertex(int index, float x, float y) { + setVertex(index, x, y, 0); + } + + + public void setVertex(int index, float x, float y, float z) { + inGeo.vertices[3 * index + 0] = x; + inGeo.vertices[3 * index + 1] = y; + inGeo.vertices[3 * index + 2] = z; + markForTessellation(); + } + + + public PVector getNormal(int index, PVector vec) { + if (vec == null) { + vec = new PVector(); + } + vec.x = inGeo.normals[3 * index + 0]; + vec.y = inGeo.normals[3 * index + 1]; + vec.z = inGeo.normals[3 * index + 2]; + return vec; + } + + + public float getNormalX(int index) { + return inGeo.normals[3 * index + 0]; + } + + + public float getNormalY(int index) { + return inGeo.normals[3 * index + 1]; + } + + + public float getNormalZ(int index) { + return inGeo.normals[3 * index + 2]; + } + + + public void setNormal(int index, float nx, float ny, float nz) { + inGeo.normals[3 * index + 0] = nx; + inGeo.normals[3 * index + 1] = ny; + inGeo.normals[3 * index + 2] = nz; + markForTessellation(); + } + + + public float getTextureU(int index) { + return inGeo.texcoords[2 * index + 0]; + } + + + public float getTextureV(int index) { + return inGeo.texcoords[2 * index + 1]; + } + + + public void setTextureUV(int index, float u, float v) { + inGeo.texcoords[2 * index + 0] = u; + inGeo.texcoords[2 * index + 1] = v; + markForTessellation(); + } + + + public int getFill(int index) { + return PGL.nativeToJavaARGB(inGeo.colors[index]); + } + + + public void setFill(int index, int fill) { + inGeo.colors[index] = PGL.javaToNativeARGB(fill); + markForTessellation(); + } + + + public int getStroke(int index) { + return PGL.nativeToJavaARGB(inGeo.strokeColors[index]); + } + + + public void setStroke(int index, int stroke) { + inGeo.strokeColors[index] = PGL.javaToNativeARGB(stroke); + markForTessellation(); + } + + + public float getStrokeWeight(int index) { + return inGeo.strokeWeights[index]; + } + + + public void setStrokeWeight(int index, float weight) { + inGeo.strokeWeights[index] = weight; + markForTessellation(); + } + + + public int getAmbient(int index) { + return PGL.nativeToJavaARGB(inGeo.ambient[index]); + } + + + public void setAmbient(int index, int ambient) { + inGeo.ambient[index] = PGL.javaToNativeARGB(ambient); + markForTessellation(); + } + + public int getSpecular(int index) { + return PGL.nativeToJavaARGB(inGeo.specular[index]); + } + + + public void setSpecular(int index, int specular) { + inGeo.specular[index] = PGL.javaToNativeARGB(specular); + markForTessellation(); + } + + + public int getEmissive(int index) { + return PGL.nativeToJavaARGB(inGeo.emissive[index]); + } + + + public void setEmissive(int index, int emissive) { + inGeo.emissive[index] = PGL.javaToNativeARGB(emissive); + markForTessellation(); + } + + + public float getShininess(int index) { + return inGeo.shininess[index]; + } + + + public void setShininess(int index, float shine) { + inGeo.shininess[index] = shine; + markForTessellation(); + } + + + /////////////////////////////////////////////////////////// + + // + + // Tessellated geometry getter. + + public PShape getTessellation() { + updateTessellation(); + + float[] vertices = tessGeo.polyVertices; + float[] normals = tessGeo.polyNormals; + int[] color = tessGeo.polyColors; + float[] uv = tessGeo.polyTexcoords; + short[] indices = tessGeo.polyIndices; + + PShape tess; + if (is3D()) { + tess = PGraphics3D.createShapeImpl(pg.parent, TRIANGLES); + } else if (is2D()) { + tess = PGraphics2D.createShapeImpl(pg.parent, TRIANGLES); + } else { + PGraphics.showWarning("This shape is not either 2D or 3D!"); + return null; + } + tess.noStroke(); + + IndexCache cache = tessGeo.polyIndexCache; + for (int n = firstPolyIndexCache; n <= lastPolyIndexCache; n++) { + int ioffset = cache.indexOffset[n]; + int icount = cache.indexCount[n]; + int voffset = cache.vertexOffset[n]; + + for (int tr = ioffset / 3; tr < (ioffset + icount) / 3; tr++) { + int i0 = voffset + indices[3 * tr + 0]; + int i1 = voffset + indices[3 * tr + 1]; + int i2 = voffset + indices[3 * tr + 2]; + + if (is3D()) { + float x0 = vertices[4 * i0 + 0], y0 = vertices[4 * i0 + 1], z0 = vertices[4 * i0 + 2]; + float x1 = vertices[4 * i1 + 0], y1 = vertices[4 * i1 + 1], z1 = vertices[4 * i1 + 2]; + float x2 = vertices[4 * i2 + 0], y2 = vertices[4 * i2 + 1], z2 = vertices[4 * i2 + 2]; + + float nx0 = normals[3 * i0 + 0], ny0 = normals[3 * i0 + 1], nz0 = normals[3 * i0 + 2]; + float nx1 = normals[3 * i1 + 0], ny1 = normals[3 * i1 + 1], nz1 = normals[3 * i1 + 2]; + float nx2 = normals[3 * i2 + 0], ny2 = normals[3 * i2 + 1], nz2 = normals[3 * i2 + 2]; + + int argb0 = PGL.nativeToJavaARGB(color[i0]); + int argb1 = PGL.nativeToJavaARGB(color[i1]); + int argb2 = PGL.nativeToJavaARGB(color[i2]); + + tess.fill(argb0); + tess.normal(nx0, ny0, nz0); + tess.vertex(x0, y0, z0, uv[2 * i0 + 0], uv[2 * i0 + 1]); + + tess.fill(argb1); + tess.normal(nx1, ny1, nz1); + tess.vertex(x1, y1, z1, uv[2 * i1 + 0], uv[2 * i1 + 1]); + + tess.fill(argb2); + tess.normal(nx2, ny2, nz2); + tess.vertex(x2, y2, z2, uv[2 * i2 + 0], uv[2 * i2 + 1]); + } else if (is2D()) { + float x0 = vertices[4 * i0 + 0], y0 = vertices[4 * i0 + 1]; + float x1 = vertices[4 * i1 + 0], y1 = vertices[4 * i1 + 1]; + float x2 = vertices[4 * i2 + 0], y2 = vertices[4 * i2 + 1]; + + int argb0 = PGL.nativeToJavaARGB(color[i0]); + int argb1 = PGL.nativeToJavaARGB(color[i1]); + int argb2 = PGL.nativeToJavaARGB(color[i2]); + + tess.fill(argb0); + tess.vertex(x0, y0, uv[2 * i0 + 0], uv[2 * i0 + 1]); + + tess.fill(argb1); + tess.vertex(x1, y1, uv[2 * i1 + 0], uv[2 * i1 + 1]); + + tess.fill(argb2); + tess.vertex(x2, y2, uv[2 * i2 + 0], uv[2 * i2 + 1]); + } + } + } + tess.end(); + + return tess; + } + + + /////////////////////////////////////////////////////////// + + // + + // Tessellation + + + protected void updateTessellation() { + if (!root.tessellated || root.contextIsOutdated()) { + root.tessellate(); + root.aggregate(); + } + } + + + protected void markForTessellation() { + root.tessellated = false; + tessellated = false; + } + + protected void tessellate() { + if (root == this && parent == null) { + if (tessGeo == null) { + tessGeo = pg.newTessGeometry(PGraphicsOpenGL.RETAINED); + } + tessGeo.clear(); + + tessellateImpl(); + + // Tessellated arrays are trimmed since they are expanded + // by doubling their old size, which might lead to arrays + // larger than the vertex counts. + tessGeo.trim(); + + modified = false; + needBufferInit = true; + + modifiedPolyVertices = false; + modifiedPolyColors = false; + modifiedPolyNormals = false; + modifiedPolyTexcoords = false; + modifiedPolyAmbient = false; + modifiedPolySpecular = false; + modifiedPolyEmissive = false; + modifiedPolyShininess = false; + + modifiedLineVertices = false; + modifiedLineColors = false; + modifiedLineAttributes = false; + + modifiedPointVertices = false; + modifiedPointColors = false; + modifiedPointAttributes = false; + + firstModifiedPolyVertex = PConstants.MAX_INT; + lastModifiedPolyVertex = PConstants.MIN_INT; + firstModifiedPolyColor = PConstants.MAX_INT; + lastModifiedPolyColor = PConstants.MIN_INT; + firstModifiedPolyNormal = PConstants.MAX_INT; + lastModifiedPolyNormal = PConstants.MIN_INT; + firstModifiedPolyTexcoord = PConstants.MAX_INT; + lastModifiedPolyTexcoord = PConstants.MIN_INT; + firstModifiedPolyAmbient = PConstants.MAX_INT; + lastModifiedPolyAmbient = PConstants.MIN_INT; + firstModifiedPolySpecular = PConstants.MAX_INT; + lastModifiedPolySpecular = PConstants.MIN_INT; + firstModifiedPolyEmissive = PConstants.MAX_INT; + lastModifiedPolyEmissive = PConstants.MIN_INT; + firstModifiedPolyShininess = PConstants.MAX_INT; + lastModifiedPolyShininess = PConstants.MIN_INT; + + firstModifiedLineVertex = PConstants.MAX_INT; + lastModifiedLineVertex = PConstants.MIN_INT; + firstModifiedLineColor = PConstants.MAX_INT; + lastModifiedLineColor = PConstants.MIN_INT; + firstModifiedLineAttribute = PConstants.MAX_INT; + lastModifiedLineAttribute = PConstants.MIN_INT; + + firstModifiedPointVertex = PConstants.MAX_INT; + lastModifiedPointVertex = PConstants.MIN_INT; + firstModifiedPointColor = PConstants.MAX_INT; + lastModifiedPointColor = PConstants.MIN_INT; + firstModifiedPointAttribute = PConstants.MAX_INT; + lastModifiedPointAttribute = PConstants.MIN_INT; + } + } + + + protected void tessellateImpl() { + tessGeo = root.tessGeo; + + firstPolyIndexCache = -1; + lastPolyIndexCache = -1; + firstLineIndexCache = -1; + lastLineIndexCache = -1; + firstPointIndexCache = -1; + lastPointIndexCache = -1; + + if (family == GROUP) { + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.tessellateImpl(); + } + } else { + if (shapeEnded) { + // If the geometry was tessellated previously, then + // the edges information will still be stored in the + // input object, so it needs to be removed to avoid + // duplication. + inGeo.clearEdges(); + + tessellator.setInGeometry(inGeo); + tessellator.setTessGeometry(tessGeo); + tessellator.setFill(fill || texture != null); + tessellator.setStroke(stroke); + tessellator.setStrokeColor(strokeColor); + tessellator.setStrokeWeight(strokeWeight); + tessellator.setStrokeCap(strokeCap); + tessellator.setStrokeJoin(strokeJoin); + tessellator.setTexCache(null, null, null); + tessellator.setTransform(matrix); + tessellator.set3D(is3D()); + + if (family == GEOMETRY) { + if (kind == POINTS) { + tessellator.tessellatePoints(); + } else if (kind == LINES) { + tessellator.tessellateLines(); + } else if (kind == LINE_STRIP) { + tessellator.tessellateLineStrip(); + } else if (kind == LINE_LOOP) { + tessellator.tessellateLineLoop(); + } else if (kind == TRIANGLE || kind == TRIANGLES) { + if (stroke) inGeo.addTrianglesEdges(); + if (normalMode == NORMAL_MODE_AUTO) inGeo.calcTrianglesNormals(); + tessellator.tessellateTriangles(); + } else if (kind == TRIANGLE_FAN) { + if (stroke) inGeo.addTriangleFanEdges(); + if (normalMode == NORMAL_MODE_AUTO) inGeo.calcTriangleFanNormals(); + tessellator.tessellateTriangleFan(); + } else if (kind == TRIANGLE_STRIP) { + if (stroke) inGeo.addTriangleStripEdges(); + if (normalMode == NORMAL_MODE_AUTO) inGeo.calcTriangleStripNormals(); + tessellator.tessellateTriangleStrip(); + } else if (kind == QUAD || kind == QUADS) { + if (stroke) inGeo.addQuadsEdges(); + if (normalMode == NORMAL_MODE_AUTO) inGeo.calcQuadsNormals(); + tessellator.tessellateQuads(); + } else if (kind == QUAD_STRIP) { + if (stroke) inGeo.addQuadStripEdges(); + if (normalMode == NORMAL_MODE_AUTO) inGeo.calcQuadStripNormals(); + tessellator.tessellateQuadStrip(); + } else if (kind == POLYGON) { + if (stroke) inGeo.addPolygonEdges(isClosed); + tessellator.tessellatePolygon(isSolid, isClosed, normalMode == NORMAL_MODE_AUTO); + } + } else if (family == PRIMITIVE) { + // The input geometry needs to be cleared because the geometry + // generation methods in InGeometry add the vertices of the + // new primitive to what is already stored. + inGeo.clear(); + + if (kind == POINT) { + tessellatePoint(); + } else if (kind == LINE) { + tessellateLine(); + } else if (kind == TRIANGLE) { + tessellateTriangle(); + } else if (kind == QUAD) { + tessellateQuad(); + } else if (kind == RECT) { + tessellateRect(); + } else if (kind == ELLIPSE) { + tessellateEllipse(); + } else if (kind == ARC) { + tessellateArc(); + } else if (kind == BOX) { + tessellateBox(); + } else if (kind == SPHERE) { + tessellateSphere(); + } + } else if (family == PATH) { + inGeo.clear(); + tessellatePath(); + } + + if (texture != null && parent != null) { + ((PShapeOpenGL)parent).addTexture(texture); + } + + firstPolyIndexCache = tessellator.firstPolyIndexCache; + lastPolyIndexCache = tessellator.lastPolyIndexCache; + firstLineIndexCache = tessellator.firstLineIndexCache; + lastLineIndexCache = tessellator.lastLineIndexCache; + firstPointIndexCache = tessellator.firstPointIndexCache; + lastPointIndexCache = tessellator.lastPointIndexCache; + } + } + + firstPolyVertex = lastPolyVertex = -1; + firstLineVertex = lastLineVertex = -1; + firstPointVertex = lastPointVertex = -1; + + tessellated = true; + } + + + protected void tessellatePoint() { + float x = 0, y = 0, z = 0; + if (params.length == 2) { + x = params[0]; + y = params[1]; + z = 0; + } else if (params.length == 3) { + x = params[0]; + y = params[1]; + z = params[2]; + } + + inGeo.setMaterial(fillColor, strokeColor, strokeWeight, + ambientColor, specularColor, emissiveColor, shininess); + inGeo.setNormal(normalX, normalY, normalZ); + inGeo.addPoint(x, y, z, fill, stroke); + tessellator.tessellatePoints(); + } + + + protected void tessellateLine() { + float x1 = 0, y1 = 0, z1 = 0; + float x2 = 0, y2 = 0, z2 = 0; + if (params.length == 4) { + x1 = params[0]; + y1 = params[1]; + x2 = params[2]; + y2 = params[3]; + } else if (params.length == 6) { + x1 = params[0]; + y1 = params[1]; + z1 = params[2]; + x2 = params[3]; + y2 = params[4]; + z2 = params[5]; + } + + inGeo.setMaterial(fillColor, strokeColor, strokeWeight, + ambientColor, specularColor, emissiveColor, shininess); + inGeo.setNormal(normalX, normalY, normalZ); + inGeo.addLine(x1, y1, z1, + x2, y2, z2, + fill, stroke); + tessellator.tessellateLines(); + } + + + protected void tessellateTriangle() { + float x1 = 0, y1 = 0; + float x2 = 0, y2 = 0; + float x3 = 0, y3 = 0; + if (params.length == 6) { + x1 = params[0]; + y1 = params[1]; + x2 = params[2]; + y2 = params[3]; + x3 = params[4]; + y3 = params[5]; + } + + inGeo.setMaterial(fillColor, strokeColor, strokeWeight, + ambientColor, specularColor, emissiveColor, shininess); + inGeo.setNormal(normalX, normalY, normalZ); + inGeo.addTriangle(x1, y1, 0, + x2, y2, 0, + x3, y3, 0, + fill, stroke); + tessellator.tessellateTriangles(); + } + + + protected void tessellateQuad() { + float x1 = 0, y1 = 0; + float x2 = 0, y2 = 0; + float x3 = 0, y3 = 0; + float x4 = 0, y4 = 0; + if (params.length == 8) { + x1 = params[0]; + y1 = params[1]; + x2 = params[2]; + y2 = params[3]; + x3 = params[4]; + y3 = params[5]; + x4 = params[6]; + y4 = params[7]; + } + + inGeo.setMaterial(fillColor, strokeColor, strokeWeight, + ambientColor, specularColor, emissiveColor, shininess); + inGeo.setNormal(normalX, normalY, normalZ); + inGeo.addQuad(x1, y1, 0, + x2, y2, 0, + x3, y3, 0, + x4, y4, 0, + fill, stroke); + tessellator.tessellateQuads(); + } + + + protected void tessellateRect() { + float a = 0, b = 0, c = 0, d = 0; + float tl = 0, tr = 0, br = 0, bl = 0; + boolean rounded = false; + if (params.length == 4) { + rounded = false; + a = params[0]; + b = params[1]; + c = params[2]; + d = params[3]; + } else if (params.length == 5) { + a = params[0]; + b = params[1]; + c = params[2]; + d = params[3]; + tl = tr = br = bl = params[4]; + rounded = true; + } else if (params.length == 8) { + a = params[0]; + b = params[1]; + c = params[2]; + d = params[3]; + tl = params[4]; + tr = params[5]; + br = params[6]; + bl = params[7]; + rounded = true; + } + + rectMode = CORNER; + inGeo.setMaterial(fillColor, strokeColor, strokeWeight, + ambientColor, specularColor, emissiveColor, shininess); + inGeo.setNormal(normalX, normalY, normalZ); + if (rounded) { + inGeo.addRect(a, b, c, d, + tl, tr, br, bl, + fill, stroke, bezierDetail, rectMode); + tessellator.tessellatePolygon(false, true, true); + } else { + inGeo.addRect(a, b, c, d, + fill, stroke, rectMode); + tessellator.tessellateQuads(); + } + } + + + protected void tessellateEllipse() { + float a = 0, b = 0, c = 0, d = 0; + if (params.length == 4) { + a = params[0]; + b = params[1]; + c = params[2]; + d = params[3]; + } + + ellipseMode = CORNER; + inGeo.setMaterial(fillColor, strokeColor, strokeWeight, + ambientColor, specularColor, emissiveColor, shininess); + inGeo.setNormal(normalX, normalY, normalZ); + inGeo.addEllipse(a, b, c, d, fill, stroke, ellipseMode); + tessellator.tessellateTriangleFan(); + } + + + protected void tessellateArc() { + float a = 0, b = 0, c = 0, d = 0; + float start = 0, stop = 0; + if (params.length == 6) { + a = params[0]; + b = params[1]; + c = params[2]; + d = params[3]; + start = params[4]; + stop = params[5]; + } + + ellipseMode = CORNER; + inGeo.setMaterial(fillColor, strokeColor, strokeWeight, + ambientColor, specularColor, emissiveColor, shininess); + inGeo.setNormal(normalX, normalY, normalZ); + inGeo.addArc(a, b, c, d, start, stop, fill, stroke, ellipseMode); + tessellator.tessellateTriangleFan(); + } + + + protected void tessellateBox() { + float w = 0, h = 0, d = 0; + if (params.length == 1) { + w = h = d = params[0]; + } else if (params.length == 3) { + w = params[0]; + h = params[1]; + d = params[2]; + } + + inGeo.setMaterial(fillColor, strokeColor, strokeWeight, + ambientColor, specularColor, emissiveColor, shininess); + inGeo.addBox(w, h, d, fill, stroke); + tessellator.tessellateQuads(); + } + + + protected void tessellateSphere() { + // Getting sphere detail from renderer. Is this correct? + int nu = pg.sphereDetailU; + int nv = pg.sphereDetailV; + float r = 0; + if (params.length == 1) { + r = params[0]; + } + + inGeo.setMaterial(fillColor, strokeColor, strokeWeight, + ambientColor, specularColor, emissiveColor, shininess); + int[] indices = inGeo.addSphere(r, nu, nv, fill, stroke); + tessellator.tessellateTriangles(indices); + } + + + protected void tessellatePath() { + if (vertices == null) return; + + inGeo.setMaterial(fillColor, strokeColor, strokeWeight, + ambientColor, specularColor, emissiveColor, shininess); + + if (vertexCodeCount == 0) { // each point is a simple vertex + if (vertices[0].length == 2) { // tesellating 2D vertices + for (int i = 0; i < vertexCount; i++) { + inGeo.addVertex(vertices[i][X], vertices[i][Y], VERTEX); + } + } else { // drawing 3D vertices + for (int i = 0; i < vertexCount; i++) { + inGeo.addVertex(vertices[i][X], vertices[i][Y], vertices[i][Z], VERTEX); + } + } + } else { // coded set of vertices + int index = 0; + int code = BREAK; + + if (vertices[0].length == 2) { // tessellating a 2D path + + for (int j = 0; j < vertexCodeCount; j++) { + switch (vertexCodes[j]) { + + case VERTEX: + inGeo.addVertex(vertices[index][X], vertices[index][Y], code); + code = VERTEX; + index++; + break; + + case QUAD_BEZIER_VERTEX: + inGeo.addQuadraticVertex(vertices[index+0][X], vertices[index+0][Y], 0, + vertices[index+1][X], vertices[index+1][Y], 0, + fill, stroke, bezierDetail, code); + code = VERTEX; + index += 2; + break; + + case BEZIER_VERTEX: + inGeo.addBezierVertex(vertices[index+0][X], vertices[index+0][Y], 0, + vertices[index+1][X], vertices[index+1][Y], 0, + vertices[index+2][X], vertices[index+2][Y], 0, + fill, stroke, bezierDetail, code); + code = VERTEX; + index += 3; + break; + + case CURVE_VERTEX: + inGeo.addCurveVertex(vertices[index][X], vertices[index][Y], 0, + fill, stroke, curveDetail, code); + code = VERTEX; + index++; + + case BREAK: + code = BREAK; + } + } + } else { // tessellating a 3D path + for (int j = 0; j < vertexCodeCount; j++) { + switch (vertexCodes[j]) { + + case VERTEX: + inGeo.addVertex(vertices[index][X], vertices[index][Y], vertices[index][Z], code); + code = VERTEX; + index++; + break; + + case QUAD_BEZIER_VERTEX: + inGeo.addQuadraticVertex(vertices[index+0][X], vertices[index+0][Y], vertices[index+0][Z], + vertices[index+1][X], vertices[index+1][Y], vertices[index+0][Z], + fill, stroke, bezierDetail, code); + code = VERTEX; + index += 2; + break; + + + case BEZIER_VERTEX: + inGeo.addBezierVertex(vertices[index+0][X], vertices[index+0][Y], vertices[index+0][Z], + vertices[index+1][X], vertices[index+1][Y], vertices[index+1][Z], + vertices[index+2][X], vertices[index+2][Y], vertices[index+2][Z], + fill, stroke, bezierDetail, code); + code = VERTEX; + index += 3; + break; + + case CURVE_VERTEX: + inGeo.addCurveVertex(vertices[index][X], vertices[index][Y], vertices[index][Z], + fill, stroke, curveDetail, code); + code = VERTEX; + index++; + + case BREAK: + code = BREAK; + } + } + } + } + + if (stroke) inGeo.addPolygonEdges(isClosed); + tessellator.tessellatePolygon(false, isClosed, true); + } + + + /////////////////////////////////////////////////////////// + + // + + // Aggregation + + + protected void aggregate() { + if (root == this && parent == null) { + // Initializing auxiliary variables in root node + // needed for aggregation. + polyIndexOffset = 0; + polyVertexOffset = 0; + polyVertexAbs = 0; + polyVertexRel = 0; + + lineIndexOffset = 0; + lineVertexOffset = 0; + lineVertexAbs = 0; + lineVertexRel = 0; + + pointIndexOffset = 0; + pointVertexOffset = 0; + pointVertexAbs = 0; + pointVertexRel = 0; + + // Recursive aggregation. + aggregateImpl(); + } + } + + + // This method is very important, as it is responsible of generating the correct + // vertex and index offsets for each level of the shape hierarchy. + // This is the core of the recursive algorithm that calculates the indices + // for the vertices accumulated in a single VBO. + // Basically, the algorithm traverses all the shapes in the hierarchy and + // updates the index cache for each child shape holding geometry (those being + // the leaf nodes in the hierarchy tree), and creates index caches for the + // group shapes so that the draw() method can be called from any shape in the + // hierarchy and the correct piece of geometry will be rendered. + // + // For example, in the following hierarchy: + // + // ROOT GROUP + // | + // /-----------------0-----------------\ + // | | + // CHILD GROUP 0 CHILD GROUP 1 + // | | + // | /---------------0-----------------\ + // | | | | + // GEO SHAPE 0 GEO SHAPE 0 GEO SHAPE 1 GEO SHAPE 2 + // 4 vertices 5 vertices 6 vertices 3 vertices + // + // calling draw() from the root group should result in all the + // vertices (4 + 5 + 6 + 3 = 18) being rendered, while calling + // draw() from either child groups 0 or 1 should result in the first + // 4 vertices or the last 14 vertices being rendered, respectively. + protected void aggregateImpl() { + if (family == GROUP) { + // Recursively aggregating the child shapes. + hasPolys = false; + hasLines = false; + hasPoints = false; + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + child.aggregateImpl(); + hasPolys |= child.hasPolys; + hasLines |= child.hasLines; + hasPoints |= child.hasPoints; + } + } else { // LEAF SHAPE (family either GEOMETRY, PATH or PRIMITIVE) + hasPolys = -1 < firstPolyIndexCache && -1 < lastPolyIndexCache; + hasLines = -1 < firstLineIndexCache && -1 < lastLineIndexCache; + hasPoints = -1 < firstPointIndexCache && -1 < lastPointIndexCache; + } + + if (hasPolys) updatePolyIndexCache(); + if (is3D()) { + if (hasLines) updateLineIndexCache(); + if (hasPoints) updatePointIndexCache(); + } + + if (matrix != null) { + // Some geometric transformations were applied on + // this shape before tessellation, so they are applied now. + //applyMatrixImpl(matrix); + if (hasPolys) tessGeo.applyMatrixOnPolyGeometry(matrix, firstPolyVertex, lastPolyVertex); + if (is3D()) { + if (hasLines) tessGeo.applyMatrixOnLineGeometry(matrix, firstLineVertex, lastLineVertex); + if (hasPoints) tessGeo.applyMatrixOnPointGeometry(matrix, firstPointVertex, lastPointVertex); + } + } + } + + + // Updates the index cache for the range that corresponds to this shape. + protected void updatePolyIndexCache() { + IndexCache cache = tessGeo.polyIndexCache; + if (family == GROUP) { + // Updates the index cache to include the elements corresponding to + // a group shape, using the cache entries of the child shapes. The + // index cache has a pyramidal structure where the base is formed + // by the entries corresponding to the leaf (geometry) shapes, and + // each subsequent level is determined by the higher-level group shapes + // The index pyramid is flattened into arrays in order to use simple + // data structures, so each shape needs to store the positions in the + // cache that corresponds to itself. + + // The index ranges of the child shapes that share the vertex offset + // are unified into a single range in the parent level. + + firstPolyIndexCache = lastPolyIndexCache = -1; + int gindex = -1; + + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + + int first = child.firstPolyIndexCache; + int count = -1 < first ? child.lastPolyIndexCache - first + 1 : -1; + for (int n = first; n < first + count; n++) { + if (gindex == -1) { + gindex = cache.addNew(n); + firstPolyIndexCache = gindex; + } else { + if (cache.vertexOffset[gindex] == cache.vertexOffset[n]) { + // When the vertex offsets are the same, this means that the + // current index range in the group shape can be extended to + // include the index range in the current child shape. + // This is a result of how the indices are updated for the + // leaf shapes. + cache.incCounts(gindex, cache.indexCount[n], cache.vertexCount[n]); + } else { + gindex = cache.addNew(n); + } + } + } + + // Updating the first and last poly vertices for this group shape. + if (-1 < child.firstPolyVertex) { + if (firstPolyVertex == -1) firstPolyVertex = Integer.MAX_VALUE; + firstPolyVertex = PApplet.min(firstPolyVertex, child.firstPolyVertex); + } + if (-1 < child.lastPolyVertex) { + lastPolyVertex = PApplet.max(lastPolyVertex, child.lastPolyVertex); + } + } + lastPolyIndexCache = gindex; + } else { + // The index cache is updated in order to reflect the fact that all + // the vertices will be stored in a single VBO in the root shape. + // This update works as follows (the methodology is the same for + // poly, line and point): the VertexAbs variable in the root shape + // stores the index of the last vertex up to this shape (plus one) + // without taking into consideration the MAX_VERTEX_INDEX limit, so + // it effectively runs over the entire range. + // VertexRel, on the other hand, is reset every time the limit is + // exceeded, therefore creating the start of a new index group in the + // root shape. When this happens, the indices in the child shape need + // to be restarted as well to reflect the new index offset. + + firstPolyVertex = lastPolyVertex = cache.vertexOffset[firstPolyIndexCache]; + for (int n = firstPolyIndexCache; n <= lastPolyIndexCache; n++) { + int ioffset = cache.indexOffset[n]; + int icount = cache.indexCount[n]; + int vcount = cache.vertexCount[n]; + + if (PGL.MAX_VERTEX_INDEX1 <= root.polyVertexRel + vcount || // Too many vertices already signal the start of a new cache... + (is2D() && startStrokedTex(n))) { // ... or, in 2D, the beginning of line or points. + root.polyVertexRel = 0; + root.polyVertexOffset = root.polyVertexAbs; + cache.indexOffset[n] = root.polyIndexOffset; + } else tessGeo.incPolyIndices(ioffset, ioffset + icount - 1, root.polyVertexRel); + cache.vertexOffset[n] = root.polyVertexOffset; + if (is2D()) setFirstStrokeVertex(n, lastPolyVertex); + + root.polyIndexOffset += icount; + root.polyVertexAbs += vcount; + root.polyVertexRel += vcount; + lastPolyVertex += vcount; + } + lastPolyVertex--; + if (is2D()) setLastStrokeVertex(lastPolyVertex); + } + } + + + protected boolean startStrokedTex(int n) { + return texture != null && (n == firstLineIndexCache || n == firstPointIndexCache); + } + + + protected void setFirstStrokeVertex(int n, int vert) { + if (n == firstLineIndexCache && firstLineVertex == -1) { + firstLineVertex = lastLineVertex = vert; + } + if (n == firstPointIndexCache && firstPointVertex == -1) { + firstPointVertex = lastPointVertex = vert; + } + } + + protected void setLastStrokeVertex(int vert) { + if (-1 < lastLineVertex) { + lastLineVertex = vert; + } + if (-1 < lastPointVertex) { + lastPointVertex += vert; + } + } + + protected void updateLineIndexCache() { + IndexCache cache = tessGeo.lineIndexCache; + if (family == GROUP) { + firstLineIndexCache = lastLineIndexCache = -1; + int gindex = -1; + + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + + int first = child.firstLineIndexCache; + int count = -1 < first ? child.lastLineIndexCache - first + 1 : -1; + for (int n = first; n < first + count; n++) { + if (gindex == -1) { + gindex = cache.addNew(n); + firstLineIndexCache = gindex; + } else { + if (cache.vertexOffset[gindex] == cache.vertexOffset[n]) { + cache.incCounts(gindex, cache.indexCount[n], cache.vertexCount[n]); + } else { + gindex = cache.addNew(n); + } + } + } + + // Updating the first and last line vertices for this group shape. + if (-1 < child.firstLineVertex) { + if (firstLineVertex == -1) firstLineVertex = Integer.MAX_VALUE; + firstLineVertex = PApplet.min(firstLineVertex, child.firstLineVertex); + } + if (-1 < child.lastLineVertex) { + lastLineVertex = PApplet.max(lastLineVertex, child.lastLineVertex); + } + } + lastLineIndexCache = gindex; + } else { + firstLineVertex = lastLineVertex = cache.vertexOffset[firstLineIndexCache]; + for (int n = firstLineIndexCache; n <= lastLineIndexCache; n++) { + int ioffset = cache.indexOffset[n]; + int icount = cache.indexCount[n]; + int vcount = cache.vertexCount[n]; + + if (PGL.MAX_VERTEX_INDEX1 <= root.lineVertexRel + vcount) { + root.lineVertexRel = 0; + root.lineVertexOffset = root.lineVertexAbs; + cache.indexOffset[n] = root.lineIndexOffset; + } else tessGeo.incLineIndices(ioffset, ioffset + icount - 1, root.lineVertexRel); + cache.vertexOffset[n] = root.lineVertexOffset; + + root.lineIndexOffset += icount; + root.lineVertexAbs += vcount; + root.lineVertexRel += vcount; + lastLineVertex += vcount; + } + lastLineVertex--; + } + } + + + protected void updatePointIndexCache() { + IndexCache cache = tessGeo.pointIndexCache; + if (family == GROUP) { + firstPointIndexCache = lastPointIndexCache = -1; + int gindex = -1; + + for (int i = 0; i < childCount; i++) { + PShapeOpenGL child = (PShapeOpenGL) children[i]; + + int first = child.firstPointIndexCache; + int count = -1 < first ? child.lastPointIndexCache - first + 1 : -1; + for (int n = first; n < first + count; n++) { + if (gindex == -1) { + gindex = cache.addNew(n); + firstPointIndexCache = gindex; + } else { + if (cache.vertexOffset[gindex] == cache.vertexOffset[n]) { + // When the vertex offsets are the same, this means that the + // current index range in the group shape can be extended to + // include either the index range in the current child shape. + // This is a result of how the indices are updated for the + // leaf shapes in aggregateImpl(). + cache.incCounts(gindex, cache.indexCount[n], cache.vertexCount[n]); + } else { + gindex = cache.addNew(n); + } + } + } + + // Updating the first and last point vertices for this group shape. + if (-1 < child.firstPointVertex) { + if (firstPointVertex == -1) firstPointVertex = Integer.MAX_VALUE; + firstPointVertex = PApplet.min(firstPointVertex, child.firstPointVertex); + } + if (-1 < child.lastPointVertex) { + lastPointVertex = PApplet.max(lastPointVertex, child.lastPointVertex); + } + } + lastPointIndexCache = gindex; + } else { + firstPointVertex = lastPointVertex = cache.vertexOffset[firstPointIndexCache]; + for (int n = firstPointIndexCache; n <= lastPointIndexCache; n++) { + int ioffset = cache.indexOffset[n]; + int icount = cache.indexCount[n]; + int vcount = cache.vertexCount[n]; + + if (PGL.MAX_VERTEX_INDEX1 <= root.pointVertexRel + vcount) { + root.pointVertexRel = 0; + root.pointVertexOffset = root.pointVertexAbs; + cache.indexOffset[n] = root.pointIndexOffset; + } else tessGeo.incPointIndices(ioffset, ioffset + icount - 1, root.pointVertexRel); + cache.vertexOffset[n] = root.pointVertexOffset; + + root.pointIndexOffset += icount; + root.pointVertexAbs += vcount; + root.pointVertexRel += vcount; + lastPointVertex += vcount; + } + lastPointVertex--; + } + } + + + /////////////////////////////////////////////////////////// + + // + + // Buffer initialization + + + protected void initBuffers() { + if (needBufferInit) { + context = pgl.getCurrentContext(); + + if (0 < tessGeo.polyVertexCount && 0 < tessGeo.polyIndexCount) { + initPolyBuffers(); + } + + if (0 < tessGeo.lineVertexCount && 0 < tessGeo.lineIndexCount) { + initLineBuffers(); + } + + if (0 < tessGeo.pointVertexCount && 0 < tessGeo.pointIndexCount) { + initPointBuffers(); + } + + needBufferInit = false; + } + } + + + protected void initPolyBuffers() { + int size = tessGeo.polyVertexCount; + int sizef = size * PGL.SIZEOF_FLOAT; + int sizei = size * PGL.SIZEOF_INT; + + glPolyVertexBufferID = pg.createVertexBufferObject(context.code()); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyVertexBufferID); + pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 4 * sizef, FloatBuffer.wrap(tessGeo.polyVertices, 0, 4 * size), PGL.GL_STATIC_DRAW); + + glPolyColorBufferID = pg.createVertexBufferObject(context.code()); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyColorBufferID); + pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, IntBuffer.wrap(tessGeo.polyColors, 0, size), PGL.GL_STATIC_DRAW); + + glPolyNormalBufferID = pg.createVertexBufferObject(context.code()); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyNormalBufferID); + pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 3 * sizef, FloatBuffer.wrap(tessGeo.polyNormals, 0, 3 * size), PGL.GL_STATIC_DRAW); + + glPolyTexcoordBufferID = pg.createVertexBufferObject(context.code()); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyTexcoordBufferID); + pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 2 * sizef, FloatBuffer.wrap(tessGeo.polyTexcoords, 0, 2 * size), PGL.GL_STATIC_DRAW); + + glPolyAmbientBufferID = pg.createVertexBufferObject(context.code()); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyAmbientBufferID); + pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, IntBuffer.wrap(tessGeo.polyAmbient, 0, size), PGL.GL_STATIC_DRAW); + + glPolySpecularBufferID = pg.createVertexBufferObject(context.code()); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolySpecularBufferID); + pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, IntBuffer.wrap(tessGeo.polySpecular, 0, size), PGL.GL_STATIC_DRAW); + + glPolyEmissiveBufferID = pg.createVertexBufferObject(context.code()); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyEmissiveBufferID); + pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, IntBuffer.wrap(tessGeo.polyEmissive, 0, size), PGL.GL_STATIC_DRAW); + + glPolyShininessBufferID = pg.createVertexBufferObject(context.code()); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyShininessBufferID); + pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizef, FloatBuffer.wrap(tessGeo.polyShininess, 0, size), PGL.GL_STATIC_DRAW); + + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); + + glPolyIndexBufferID = pg.createVertexBufferObject(context.code()); + pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, glPolyIndexBufferID); + pgl.glBufferData(PGL.GL_ELEMENT_ARRAY_BUFFER, tessGeo.polyIndexCount * PGL.SIZEOF_INDEX, + ShortBuffer.wrap(tessGeo.polyIndices, 0, tessGeo.polyIndexCount), PGL.GL_STATIC_DRAW); + + pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, 0); + } + + + protected void initLineBuffers() { + int size = tessGeo.lineVertexCount; + int sizef = size * PGL.SIZEOF_FLOAT; + int sizei = size * PGL.SIZEOF_INT; + + glLineVertexBufferID = pg.createVertexBufferObject(context.code()); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glLineVertexBufferID); + pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 4 * sizef, FloatBuffer.wrap(tessGeo.lineVertices, 0, 4 * size), PGL.GL_STATIC_DRAW); + + glLineColorBufferID = pg.createVertexBufferObject(context.code()); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glLineColorBufferID); + pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, IntBuffer.wrap(tessGeo.lineColors, 0, size), PGL.GL_STATIC_DRAW); + + glLineAttribBufferID = pg.createVertexBufferObject(context.code()); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glLineAttribBufferID); + pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 4 * sizef, FloatBuffer.wrap(tessGeo.lineAttribs, 0, 4 * size), PGL.GL_STATIC_DRAW); + + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); + + glLineIndexBufferID = pg.createVertexBufferObject(context.code()); + pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, glLineIndexBufferID); + pgl.glBufferData(PGL.GL_ELEMENT_ARRAY_BUFFER, tessGeo.lineIndexCount * PGL.SIZEOF_INDEX, + ShortBuffer.wrap(tessGeo.lineIndices, 0, tessGeo.lineIndexCount), PGL.GL_STATIC_DRAW); + + pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, 0); + } + + + protected void initPointBuffers() { + int size = tessGeo.pointVertexCount; + int sizef = size * PGL.SIZEOF_FLOAT; + int sizei = size * PGL.SIZEOF_INT; + + glPointVertexBufferID = pg.createVertexBufferObject(context.code()); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPointVertexBufferID); + pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 4 * sizef, FloatBuffer.wrap(tessGeo.pointVertices, 0, 4 * size), PGL.GL_STATIC_DRAW); + + glPointColorBufferID = pg.createVertexBufferObject(context.code()); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPointColorBufferID); + pgl.glBufferData(PGL.GL_ARRAY_BUFFER, sizei, IntBuffer.wrap(tessGeo.pointColors, 0, size), PGL.GL_STATIC_DRAW); + + glPointAttribBufferID = pg.createVertexBufferObject(context.code()); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPointAttribBufferID); + pgl.glBufferData(PGL.GL_ARRAY_BUFFER, 2 * sizef, FloatBuffer.wrap(tessGeo.pointAttribs, 0, 2 * size), PGL.GL_STATIC_DRAW); + + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); + + glPointIndexBufferID = pg.createVertexBufferObject(context.code()); + pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, glPointIndexBufferID); + pgl.glBufferData(PGL.GL_ELEMENT_ARRAY_BUFFER, tessGeo.pointIndexCount * PGL.SIZEOF_INDEX, + ShortBuffer.wrap(tessGeo.pointIndices, 0, tessGeo.pointIndexCount), PGL.GL_STATIC_DRAW); + + pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, 0); + } + + + protected boolean contextIsOutdated() { + boolean outdated = !pgl.contextIsCurrent(context); + if (outdated) { + // Removing the VBOs from the renderer's list so they + // doesn't get deleted by OpenGL. The VBOs were already + // automatically disposed when the old context was + // destroyed. + pg.removeVertexBufferObject(glPolyVertexBufferID, context.code()); + pg.removeVertexBufferObject(glPolyColorBufferID, context.code()); + pg.removeVertexBufferObject(glPolyNormalBufferID, context.code()); + pg.removeVertexBufferObject(glPolyTexcoordBufferID, context.code()); + pg.removeVertexBufferObject(glPolyAmbientBufferID, context.code()); + pg.removeVertexBufferObject(glPolySpecularBufferID, context.code()); + pg.removeVertexBufferObject(glPolyEmissiveBufferID, context.code()); + pg.removeVertexBufferObject(glPolyShininessBufferID, context.code()); + pg.removeVertexBufferObject(glPolyIndexBufferID, context.code()); + + pg.removeVertexBufferObject(glLineVertexBufferID, context.code()); + pg.removeVertexBufferObject(glLineColorBufferID, context.code()); + pg.removeVertexBufferObject(glLineAttribBufferID, context.code()); + pg.removeVertexBufferObject(glLineIndexBufferID, context.code()); + + pg.removeVertexBufferObject(glPointVertexBufferID, context.code()); + pg.removeVertexBufferObject(glPointColorBufferID, context.code()); + pg.removeVertexBufferObject(glPointAttribBufferID, context.code()); + pg.removeVertexBufferObject(glPointIndexBufferID, context.code()); + + // The OpenGL resources have been already deleted + // when the context changed. We only need to zero + // them to avoid deleting them again when the GC + // runs the finalizers of the disposed object. + glPolyVertexBufferID = 0; + glPolyColorBufferID = 0; + glPolyNormalBufferID = 0; + glPolyTexcoordBufferID = 0; + glPolyAmbientBufferID = 0; + glPolySpecularBufferID = 0; + glPolyEmissiveBufferID = 0; + glPolyShininessBufferID = 0; + glPolyIndexBufferID = 0; + + glLineVertexBufferID = 0; + glLineColorBufferID = 0; + glLineAttribBufferID = 0; + glLineIndexBufferID = 0; + + glPointVertexBufferID = 0; + glPointColorBufferID = 0; + glPointAttribBufferID = 0; + glPointIndexBufferID = 0; + } + return outdated; + } + + + /////////////////////////////////////////////////////////// + + // + + // Deletion methods + + + protected void release() { + deletePolyBuffers(); + deleteLineBuffers(); + deletePointBuffers(); + } + + + protected void deletePolyBuffers() { + if (glPolyVertexBufferID != 0) { + pg.deleteVertexBufferObject(glPolyVertexBufferID, context.code()); + glPolyVertexBufferID = 0; + } + + if (glPolyColorBufferID != 0) { + pg.deleteVertexBufferObject(glPolyColorBufferID, context.code()); + glPolyColorBufferID = 0; + } + + if (glPolyNormalBufferID != 0) { + pg.deleteVertexBufferObject(glPolyNormalBufferID, context.code()); + glPolyNormalBufferID = 0; + } + + if (glPolyTexcoordBufferID != 0) { + pg.deleteVertexBufferObject(glPolyTexcoordBufferID, context.code()); + glPolyTexcoordBufferID = 0; + } + + if (glPolyAmbientBufferID != 0) { + pg.deleteVertexBufferObject(glPolyAmbientBufferID, context.code()); + glPolyAmbientBufferID = 0; + } + + if (glPolySpecularBufferID != 0) { + pg.deleteVertexBufferObject(glPolySpecularBufferID, context.code()); + glPolySpecularBufferID = 0; + } + + if (glPolyEmissiveBufferID != 0) { + pg.deleteVertexBufferObject(glPolyEmissiveBufferID, context.code()); + glPolyEmissiveBufferID = 0; + } + + if (glPolyShininessBufferID != 0) { + pg.deleteVertexBufferObject(glPolyShininessBufferID, context.code()); + glPolyShininessBufferID = 0; + } + + if (glPolyIndexBufferID != 0) { + pg.deleteVertexBufferObject(glPolyIndexBufferID, context.code()); + glPolyIndexBufferID = 0; + } + } + + + protected void deleteLineBuffers() { + if (glLineVertexBufferID != 0) { + pg.deleteVertexBufferObject(glLineVertexBufferID, context.code()); + glLineVertexBufferID = 0; + } + + if (glLineColorBufferID != 0) { + pg.deleteVertexBufferObject(glLineColorBufferID, context.code()); + glLineColorBufferID = 0; + } + + if (glLineAttribBufferID != 0) { + pg.deleteVertexBufferObject(glLineAttribBufferID, context.code()); + glLineAttribBufferID = 0; + } + + if (glLineIndexBufferID != 0) { + pg.deleteVertexBufferObject(glLineIndexBufferID, context.code()); + glLineIndexBufferID = 0; + } + } + + + protected void deletePointBuffers() { + if (glPointVertexBufferID != 0) { + pg.deleteVertexBufferObject(glPointVertexBufferID, context.code()); + glPointVertexBufferID = 0; + } + + if (glPointColorBufferID != 0) { + pg.deleteVertexBufferObject(glPointColorBufferID, context.code()); + glPointColorBufferID = 0; + } + + if (glPointAttribBufferID != 0) { + pg.deleteVertexBufferObject(glPointAttribBufferID, context.code()); + glPointAttribBufferID = 0; + } + + if (glPointIndexBufferID != 0) { + pg.deleteVertexBufferObject(glPointIndexBufferID, context.code()); + glPointIndexBufferID = 0; + } + } + + + /////////////////////////////////////////////////////////// + + // + + // Geometry update + + + protected void updateGeometry() { + root.initBuffers(); + if (root.modified) { + root.updateGeometryImpl(); + } + } + + + protected void updateGeometryImpl() { + if (modifiedPolyVertices) { + int offset = firstModifiedPolyVertex; + int size = lastModifiedPolyVertex - offset + 1; + copyPolyVertices(offset, size); + modifiedPolyVertices = false; + firstModifiedPolyVertex = PConstants.MAX_INT; + lastModifiedPolyVertex = PConstants.MIN_INT; + } + if (modifiedPolyColors) { + int offset = firstModifiedPolyColor; + int size = lastModifiedPolyColor - offset + 1; + copyPolyColors(offset, size); + modifiedPolyColors = false; + firstModifiedPolyColor = PConstants.MAX_INT; + lastModifiedPolyColor = PConstants.MIN_INT; + } + if (modifiedPolyNormals) { + int offset = firstModifiedPolyNormal; + int size = lastModifiedPolyNormal - offset + 1; + copyPolyNormals(offset, size); + modifiedPolyNormals = false; + firstModifiedPolyNormal = PConstants.MAX_INT; + lastModifiedPolyNormal = PConstants.MIN_INT; + } + if (modifiedPolyTexcoords) { + int offset = firstModifiedPolyTexcoord; + int size = lastModifiedPolyTexcoord - offset + 1; + copyPolyTexcoords(offset, size); + modifiedPolyTexcoords = false; + firstModifiedPolyTexcoord = PConstants.MAX_INT; + lastModifiedPolyTexcoord = PConstants.MIN_INT; + } + if (modifiedPolyAmbient) { + int offset = firstModifiedPolyAmbient; + int size = lastModifiedPolyAmbient - offset + 1; + copyPolyAmbient(offset, size); + modifiedPolyAmbient = false; + firstModifiedPolyAmbient = PConstants.MAX_INT; + lastModifiedPolyAmbient = PConstants.MIN_INT; + } + if (modifiedPolySpecular) { + int offset = firstModifiedPolySpecular; + int size = lastModifiedPolySpecular - offset + 1; + copyPolySpecular(offset, size); + modifiedPolySpecular = false; + firstModifiedPolySpecular = PConstants.MAX_INT; + lastModifiedPolySpecular = PConstants.MIN_INT; + } + if (modifiedPolyEmissive) { + int offset = firstModifiedPolyEmissive; + int size = lastModifiedPolyEmissive - offset + 1; + copyPolyEmissive(offset, size); + modifiedPolyEmissive = false; + firstModifiedPolyEmissive = PConstants.MAX_INT; + lastModifiedPolyEmissive = PConstants.MIN_INT; + } + if (modifiedPolyShininess) { + int offset = firstModifiedPolyShininess; + int size = lastModifiedPolyShininess - offset + 1; + copyPolyShininess(offset, size); + modifiedPolyShininess = false; + firstModifiedPolyShininess = PConstants.MAX_INT; + lastModifiedPolyShininess = PConstants.MIN_INT; + } + + if (modifiedLineVertices) { + int offset = firstModifiedLineVertex; + int size = lastModifiedLineVertex - offset + 1; + copyLineVertices(offset, size); + modifiedLineVertices = false; + firstModifiedLineVertex = PConstants.MAX_INT; + lastModifiedLineVertex = PConstants.MIN_INT; + } + if (modifiedLineColors) { + int offset = firstModifiedLineColor; + int size = lastModifiedLineColor - offset + 1; + copyLineColors(offset, size); + modifiedLineColors = false; + firstModifiedLineColor = PConstants.MAX_INT; + lastModifiedLineColor = PConstants.MIN_INT; + } + if (modifiedLineAttributes) { + int offset = firstModifiedLineAttribute; + int size = lastModifiedLineAttribute - offset + 1; + copyLineAttributes(offset, size); + modifiedLineAttributes = false; + firstModifiedLineAttribute = PConstants.MAX_INT; + lastModifiedLineAttribute = PConstants.MIN_INT; + } + + if (modifiedPointVertices) { + int offset = firstModifiedPointVertex; + int size = lastModifiedPointVertex - offset + 1; + copyPointVertices(offset, size); + modifiedPointVertices = false; + firstModifiedPointVertex = PConstants.MAX_INT; + lastModifiedPointVertex = PConstants.MIN_INT; + } + if (modifiedPointColors) { + int offset = firstModifiedPointColor; + int size = lastModifiedPointColor - offset + 1; + copyPointColors(offset, size); + modifiedPointColors = false; + firstModifiedPointColor = PConstants.MAX_INT; + lastModifiedPointColor = PConstants.MIN_INT; + } + if (modifiedPointAttributes) { + int offset = firstModifiedPointAttribute; + int size = lastModifiedPointAttribute - offset + 1; + copyPointAttributes(offset, size); + modifiedPointAttributes = false; + firstModifiedPointAttribute = PConstants.MAX_INT; + lastModifiedPointAttribute = PConstants.MIN_INT; + } + + modified = false; + } + + + protected void copyPolyVertices(int offset, int size) { + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyVertexBufferID); + pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, 4 * offset * PGL.SIZEOF_FLOAT, 4 * size * PGL.SIZEOF_FLOAT, + FloatBuffer.wrap(tessGeo.polyVertices, 4 * offset, 4 * size)); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); + } + + + protected void copyPolyColors(int offset, int size) { + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyColorBufferID); + pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, offset * PGL.SIZEOF_INT, size * PGL.SIZEOF_INT, + IntBuffer.wrap(tessGeo.polyColors, offset, size)); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); + } + + + protected void copyPolyNormals(int offset, int size) { + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyNormalBufferID); + pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, 3 * offset * PGL.SIZEOF_FLOAT, 3 * size * PGL.SIZEOF_FLOAT, + FloatBuffer.wrap(tessGeo.polyNormals, 3 * offset, 3 * size)); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); + } + + + protected void copyPolyTexcoords(int offset, int size) { + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyTexcoordBufferID); + pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, 2 * offset * PGL.SIZEOF_FLOAT, 2 * size * PGL.SIZEOF_FLOAT, + FloatBuffer.wrap(tessGeo.polyTexcoords, 2 * offset, 2 * size)); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); + } + + + protected void copyPolyAmbient(int offset, int size) { + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyAmbientBufferID); + pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, offset * PGL.SIZEOF_INT, size * PGL.SIZEOF_INT, + IntBuffer.wrap(tessGeo.polyAmbient, offset, size)); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); + } + + + protected void copyPolySpecular(int offset, int size) { + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolySpecularBufferID); + pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, offset * PGL.SIZEOF_INT, size * PGL.SIZEOF_INT, + IntBuffer.wrap(tessGeo.polySpecular, offset, size)); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); + } + + + protected void copyPolyEmissive(int offset, int size) { + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyEmissiveBufferID); + pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, offset * PGL.SIZEOF_INT, size * PGL.SIZEOF_INT, + IntBuffer.wrap(tessGeo.polyEmissive, offset, size)); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); + } + + + protected void copyPolyShininess(int offset, int size) { + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPolyShininessBufferID); + pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, offset * PGL.SIZEOF_FLOAT, size * PGL.SIZEOF_FLOAT, + FloatBuffer.wrap(tessGeo.polyShininess, offset, size)); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); + } + + + protected void copyLineVertices(int offset, int size) { + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glLineVertexBufferID); + pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, 4 * offset * PGL.SIZEOF_FLOAT, 4 * size * PGL.SIZEOF_FLOAT, + FloatBuffer.wrap(tessGeo.lineVertices, 4 * offset, 4 * size)); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); + } + + + protected void copyLineColors(int offset, int size) { + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glLineColorBufferID); + pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, offset * PGL.SIZEOF_INT, size * PGL.SIZEOF_INT, + IntBuffer.wrap(tessGeo.lineColors, offset, size)); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); + } + + + protected void copyLineAttributes(int offset, int size) { + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glLineAttribBufferID); + pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, 4 * offset * PGL.SIZEOF_FLOAT, 4 * size * PGL.SIZEOF_FLOAT, + FloatBuffer.wrap(tessGeo.lineAttribs, 4 * offset, 4 * size)); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); + } + + + protected void copyPointVertices(int offset, int size) { + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPointVertexBufferID); + pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, 4 * offset * PGL.SIZEOF_FLOAT, 4 * size * PGL.SIZEOF_FLOAT, + FloatBuffer.wrap(tessGeo.pointVertices, 4 * offset, 4 * size)); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); + } + + + protected void copyPointColors(int offset, int size) { + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPointColorBufferID); + pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, offset * PGL.SIZEOF_INT, size * PGL.SIZEOF_INT, + IntBuffer.wrap(tessGeo.pointColors, offset, size)); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); + } + + + protected void copyPointAttributes(int offset, int size) { + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, glPointAttribBufferID); + pgl.glBufferSubData(PGL.GL_ARRAY_BUFFER, 2 * offset * PGL.SIZEOF_FLOAT, 2 * size * PGL.SIZEOF_FLOAT, + FloatBuffer.wrap(tessGeo.pointAttribs, 2 * offset, 2 * size)); + pgl.glBindBuffer(PGL.GL_ARRAY_BUFFER, 0); + } + + + protected void setModifiedPolyVertices(int first, int last) { + if (first < firstModifiedPolyVertex) firstModifiedPolyVertex = first; + if (last > lastModifiedPolyVertex) lastModifiedPolyVertex = last; + modifiedPolyVertices = true; + modified = true; + } + + + protected void setModifiedPolyColors(int first, int last) { + if (first < firstModifiedPolyColor) firstModifiedPolyColor = first; + if (last > lastModifiedPolyColor) lastModifiedPolyColor = last; + modifiedPolyColors = true; + modified = true; + } + + + protected void setModifiedPolyNormals(int first, int last) { + if (first < firstModifiedPolyNormal) firstModifiedPolyNormal = first; + if (last > lastModifiedPolyNormal) lastModifiedPolyNormal = last; + modifiedPolyNormals = true; + modified = true; + } + + + protected void setModifiedPolyTexcoords(int first, int last) { + if (first < firstModifiedPolyTexcoord) firstModifiedPolyTexcoord = first; + if (last > lastModifiedPolyTexcoord) lastModifiedPolyTexcoord = last; + modifiedPolyTexcoords = true; + modified = true; + } + + + protected void setModifiedPolyAmbient(int first, int last) { + if (first < firstModifiedPolyAmbient) firstModifiedPolyAmbient = first; + if (last > lastModifiedPolyAmbient) lastModifiedPolyAmbient = last; + modifiedPolyAmbient = true; + modified = true; + } + + + protected void setModifiedPolySpecular(int first, int last) { + if (first < firstModifiedPolySpecular) firstModifiedPolySpecular = first; + if (last > lastModifiedPolySpecular) lastModifiedPolySpecular = last; + modifiedPolySpecular = true; + modified = true; + } + + + protected void setModifiedPolyEmissive(int first, int last) { + if (first < firstModifiedPolyEmissive) firstModifiedPolyEmissive = first; + if (last > lastModifiedPolyEmissive) lastModifiedPolyEmissive = last; + modifiedPolyEmissive = true; + modified = true; + } + + + protected void setModifiedPolyShininess(int first, int last) { + if (first < firstModifiedPolyShininess) firstModifiedPolyShininess = first; + if (last > lastModifiedPolyShininess) lastModifiedPolyShininess = last; + modifiedPolyShininess = true; + modified = true; + } + + + protected void setModifiedLineVertices(int first, int last) { + if (first < firstModifiedLineVertex) firstModifiedLineVertex = first; + if (last > lastModifiedLineVertex) lastModifiedLineVertex = last; + modifiedLineVertices = true; + modified = true; + } + + + protected void setModifiedLineColors(int first, int last) { + if (first < firstModifiedLineColor) firstModifiedLineColor = first; + if (last > lastModifiedLineColor) lastModifiedLineColor = last; + modifiedLineColors = true; + modified = true; + } + + + protected void setModifiedLineAttributes(int first, int last) { + if (first < firstModifiedLineAttribute) firstModifiedLineAttribute = first; + if (last > lastModifiedLineAttribute) lastModifiedLineAttribute = last; + modifiedLineAttributes = true; + modified = true; + } + + + protected void setModifiedPointVertices(int first, int last) { + if (first < firstModifiedPointVertex) firstModifiedPointVertex = first; + if (last > lastModifiedPointVertex) lastModifiedPointVertex = last; + modifiedPointVertices = true; + modified = true; + } + + + protected void setModifiedPointColors(int first, int last) { + if (first < firstModifiedPointColor) firstModifiedPointColor = first; + if (last > lastModifiedPointColor) lastModifiedPointColor = last; + modifiedPointColors = true; + modified = true; + } + + + protected void setModifiedPointAttributes(int first, int last) { + if (first < firstModifiedPointAttribute) firstModifiedPointAttribute = first; + if (last > lastModifiedPointAttribute) lastModifiedPointAttribute = last; + modifiedPointAttributes = true; + modified = true; + } + + + /////////////////////////////////////////////////////////// + + // + + // Style handling + + + // Applies the styles of g. + protected void styles(PGraphics g) { + if (stroke) { + stroke(g.strokeColor); + strokeWeight(g.strokeWeight); + + // These two don't to nothing probably: + strokeCap(g.strokeCap); + strokeJoin(g.strokeJoin); + } else { + noStroke(); + } + + if (fill) { + fill(g.fillColor); + } else { + noFill(); + } + + ambient(g.ambientColor); + specular(g.specularColor); + emissive(g.emissiveColor); + shininess(g.shininess); + + // What about other style parameters, such as rectMode, etc? + // These should force a tessellation update, same as stroke + // cap and weight... right? + } + + + /////////////////////////////////////////////////////////// + + // + + // Rendering methods + + + public void draw() { + draw(pg); + } + + + public void draw(PGraphics g) { + if (visible) { + pre(g); + + updateTessellation(); + updateGeometry(); + + if (family == GROUP) { + if (fragmentedGroup(g)) { + for (int i = 0; i < childCount; i++) { + ((PShapeOpenGL) children[i]).draw(g); + } + } else { + PImage tex = null; + if (textures != null && textures.size() == 1) { + tex = (PImage)textures.toArray()[0]; + } + render((PGraphicsOpenGL)g, tex); + } + + } else { + render((PGraphicsOpenGL)g, texture); + } + + post(g); + } + } + + + // Returns true if some child shapes below this one either + // use different texture maps or have stroked textures, + // so they cannot rendered in a single call. + // Or accurate 2D mode is enabled, which forces each + // shape to be rendered separately. + protected boolean fragmentedGroup(PGraphics g) { + return g.hintEnabled(ENABLE_ACCURATE_2D) || + (textures != null && 1 < textures.size()) || + strokedTexture; + } + + + protected void pre(PGraphics g) { + if (!style) { + styles(g); + } + } + + + public void post(PGraphics g) { + } + + + // Render the geometry stored in the root shape as VBOs, for the vertices + // corresponding to this shape. Sometimes we can have root == this. + protected void render(PGraphicsOpenGL g, PImage texture) { + if (root == null) { + // Some error. Root should never be null. At least it should be 'this'. + throw new RuntimeException("Error rendering PShapeOpenGL, root shape is null"); + } + + if (hasPolys) { + renderPolys(g, texture); + if (g.haveRaw()) { + rawPolys(g, texture); + } + } + + if (is3D()) { + // In 3D mode, the lines and points need to be rendered separately + // as they require their own shaders. + if (hasLines) { + renderLines(g); + if (g.haveRaw()) { + rawLines(g); + } + } + + if (hasPoints) { + renderPoints(g); + if (g.haveRaw()) { + rawPoints(g); + } + } + } + } + + + protected void renderPolys(PGraphicsOpenGL g, PImage textureImage) { + PTexture tex = null; + if (textureImage != null) { + tex = g.getTexture(textureImage); + if (tex != null) { + pgl.enableTexturing(tex.glTarget); + pgl.glBindTexture(tex.glTarget, tex.glID); + } + } + + boolean renderingFill = false, renderingStroke = false; + PolyShader shader = null; + IndexCache cache = tessGeo.polyIndexCache; + for (int n = firstPolyIndexCache; n <= lastPolyIndexCache; n++) { + if (is3D() || (tex != null && (firstLineIndexCache == -1 || n < firstLineIndexCache) && + (firstPointIndexCache == -1 || n < firstPointIndexCache))) { + // Rendering fill triangles, which can be lit and textured. + if (!renderingFill) { + shader = g.getPolyShader(g.lights, tex != null); + shader.start(); + renderingFill = true; + } + } else { + // Rendering line or point triangles, which are never lit nor textured. + if (!renderingStroke) { + if (tex != null) { + pgl.glBindTexture(tex.glTarget, 0); + pgl.disableTexturing(tex.glTarget); + tex = null; + } + + if (shader != null && shader.active()) { + shader.stop(); + } + + // If the renderer is 2D, then g.lights should always be false, + // so no need to worry about that. + shader = g.getPolyShader(g.lights, false); + shader.start(); + + renderingFill = false; + renderingStroke = true; + } + } + + int ioffset = cache.indexOffset[n]; + int icount = cache.indexCount[n]; + int voffset = cache.vertexOffset[n]; + + shader.setVertexAttribute(root.glPolyVertexBufferID, 4, PGL.GL_FLOAT, 0, 4 * voffset * PGL.SIZEOF_FLOAT); + shader.setColorAttribute(root.glPolyColorBufferID, 4, PGL.GL_UNSIGNED_BYTE, 0, 4 * voffset * PGL.SIZEOF_BYTE); + + if (g.lights) { + shader.setNormalAttribute(root.glPolyNormalBufferID, 3, PGL.GL_FLOAT, 0, 3 * voffset * PGL.SIZEOF_FLOAT); + shader.setAmbientAttribute(root.glPolyAmbientBufferID, 4, PGL.GL_UNSIGNED_BYTE, 0, 4 * voffset * PGL.SIZEOF_BYTE); + shader.setSpecularAttribute(root.glPolySpecularBufferID, 4, PGL.GL_UNSIGNED_BYTE, 0, 4 * voffset * PGL.SIZEOF_BYTE); + shader.setEmissiveAttribute(root.glPolyEmissiveBufferID, 4, PGL.GL_UNSIGNED_BYTE, 0, 4 * voffset * PGL.SIZEOF_BYTE); + shader.setShininessAttribute(root.glPolyShininessBufferID, 1, PGL.GL_FLOAT, 0, voffset * PGL.SIZEOF_FLOAT); + } + + if (tex != null) { + shader.setTexcoordAttribute(root.glPolyTexcoordBufferID, 2, PGL.GL_FLOAT, 0, 2 * voffset * PGL.SIZEOF_FLOAT); + shader.setTexture(tex); + } + + pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, root.glPolyIndexBufferID); + pgl.glDrawElements(PGL.GL_TRIANGLES, icount, PGL.INDEX_TYPE, ioffset * PGL.SIZEOF_INDEX); + pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, 0); + } + + if (shader != null && shader.active()) { + shader.stop(); + } + + if (tex != null) { + pgl.glBindTexture(tex.glTarget, 0); + pgl.disableTexturing(tex.glTarget); + } + } + + + protected void rawPolys(PGraphicsOpenGL g, PImage textureImage) { + PGraphics raw = g.getRaw(); + + raw.colorMode(RGB); + raw.noStroke(); + raw.beginShape(TRIANGLES); + + float[] vertices = tessGeo.polyVertices; + int[] color = tessGeo.polyColors; + float[] uv = tessGeo.polyTexcoords; + short[] indices = tessGeo.polyIndices; + + IndexCache cache = tessGeo.polyIndexCache; + for (int n = firstPolyIndexCache; n <= lastPolyIndexCache; n++) { + int ioffset = cache.indexOffset[n]; + int icount = cache.indexCount[n]; + int voffset = cache.vertexOffset[n]; + + for (int tr = ioffset / 3; tr < (ioffset + icount) / 3; tr++) { + int i0 = voffset + indices[3 * tr + 0]; + int i1 = voffset + indices[3 * tr + 1]; + int i2 = voffset + indices[3 * tr + 2]; + + 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}; + int argb0 = PGL.nativeToJavaARGB(color[i0]); + int argb1 = PGL.nativeToJavaARGB(color[i1]); + int argb2 = PGL.nativeToJavaARGB(color[i2]); + + PApplet.arrayCopy(vertices, 4 * i0, src0, 0, 4); + PApplet.arrayCopy(vertices, 4 * i1, src1, 0, 4); + PApplet.arrayCopy(vertices, 4 * i2, src2, 0, 4); + // Applying any transformation is currently stored in the + // modelview matrix of the renderer. + g.modelview.mult(src0, pt0); + g.modelview.mult(src1, pt1); + g.modelview.mult(src2, pt2); + + if (textureImage != null) { + raw.texture(textureImage); + 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 = g.screenXImpl(pt0[0], pt0[1], pt0[2], pt0[3]), sy0 = g.screenYImpl(pt0[0], pt0[1], pt0[2], pt0[3]); + float sx1 = g.screenXImpl(pt1[0], pt1[1], pt1[2], pt1[3]), sy1 = g.screenYImpl(pt1[0], pt1[1], pt1[2], pt1[3]); + float sx2 = g.screenXImpl(pt2[0], pt2[1], pt2[2], pt2[3]), sy2 = g.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 = g.screenXImpl(pt0[0], pt0[1], pt0[2], pt0[3]), sy0 = g.screenYImpl(pt0[0], pt0[1], pt0[2], pt0[3]); + float sx1 = g.screenXImpl(pt1[0], pt1[1], pt1[2], pt1[3]), sy1 = g.screenYImpl(pt1[0], pt1[1], pt1[2], pt1[3]); + float sx2 = g.screenXImpl(pt2[0], pt2[1], pt2[2], pt2[3]), sy2 = g.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); + } + } + } + } + + raw.endShape(); + } + + + protected void renderLines(PGraphicsOpenGL g) { + LineShader shader = g.getLineShader(); + shader.start(); + + IndexCache cache = tessGeo.lineIndexCache; + for (int n = firstLineIndexCache; n <= lastLineIndexCache; n++) { + int ioffset = cache.indexOffset[n]; + int icount = cache.indexCount[n]; + int voffset = cache.vertexOffset[n]; + + shader.setVertexAttribute(root.glLineVertexBufferID, 4, PGL.GL_FLOAT, 0, 4 * voffset * PGL.SIZEOF_FLOAT); + shader.setColorAttribute(root.glLineColorBufferID, 4, PGL.GL_UNSIGNED_BYTE, 0, 4 * voffset * PGL.SIZEOF_BYTE); + shader.setLineAttribute(root.glLineAttribBufferID, 4, PGL.GL_FLOAT, 0, 4 * voffset * PGL.SIZEOF_FLOAT); + + pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, root.glLineIndexBufferID); + pgl.glDrawElements(PGL.GL_TRIANGLES, icount, PGL.INDEX_TYPE, ioffset * PGL.SIZEOF_INDEX); + pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, 0); + } + + shader.stop(); + } + + + protected void rawLines(PGraphicsOpenGL g) { + PGraphics raw = g.getRaw(); + + raw.colorMode(RGB); + raw.noFill(); + raw.strokeCap(strokeCap); + raw.strokeJoin(strokeJoin); + raw.beginShape(LINES); + + float[] vertices = tessGeo.lineVertices; + int[] color = tessGeo.lineColors; + float[] attribs = tessGeo.lineAttribs; + short[] indices = tessGeo.lineIndices; + + IndexCache cache = tessGeo.lineIndexCache; + for (int n = firstLineIndexCache; n <= lastLineIndexCache; n++) { + int ioffset = cache.indexOffset[n]; + int icount = cache.indexCount[n]; + int voffset = cache.vertexOffset[n]; + + for (int ln = ioffset / 6; ln < (ioffset + icount) / 6; ln++) { + // Each line segment is defined by six indices since its + // formed by two triangles. We only need the first and last + // vertices. + // This bunch of vertices could also be the bevel triangles, + // with we detect this situation by looking at the line weight. + int i0 = voffset + indices[6 * ln + 0]; + int i1 = voffset + indices[6 * ln + 5]; + float sw0 = 2 * attribs[4 * i0 + 3]; + float sw1 = 2 * attribs[4 * i1 + 3]; + + if (PGraphicsOpenGL.zero(sw0)) continue; // Bevel triangles, skip. + + float[] src0 = {0, 0, 0, 0}; + float[] src1 = {0, 0, 0, 0}; + float[] pt0 = {0, 0, 0, 0}; + float[] pt1 = {0, 0, 0, 0}; + int argb0 = PGL.nativeToJavaARGB(color[i0]); + int argb1 = PGL.nativeToJavaARGB(color[i1]); + + PApplet.arrayCopy(vertices, 4 * i0, src0, 0, 4); + PApplet.arrayCopy(vertices, 4 * i1, src1, 0, 4); + // Applying any transformation is currently stored in the + // modelview matrix of the renderer. + g.modelview.mult(src0, pt0); + g.modelview.mult(src1, pt1); + + if (raw.is3D()) { + raw.strokeWeight(sw0); + raw.stroke(argb0); + raw.vertex(pt0[X], pt0[Y], pt0[Z]); + raw.strokeWeight(sw1); + raw.stroke(argb1); + raw.vertex(pt1[X], pt1[Y], pt1[Z]); + } else if (raw.is2D()) { + float sx0 = g.screenXImpl(pt0[0], pt0[1], pt0[2], pt0[3]), sy0 = g.screenYImpl(pt0[0], pt0[1], pt0[2], pt0[3]); + float sx1 = g.screenXImpl(pt1[0], pt1[1], pt1[2], pt1[3]), sy1 = g.screenYImpl(pt1[0], pt1[1], pt1[2], pt1[3]); + raw.strokeWeight(sw0); + raw.stroke(argb0); + raw.vertex(sx0, sy0); + raw.strokeWeight(sw1); + raw.stroke(argb1); + raw.vertex(sx1, sy1); + } + } + } + + raw.endShape(); + } + + + protected void renderPoints(PGraphicsOpenGL g) { + PointShader shader = g.getPointShader(); + shader.start(); + + IndexCache cache = tessGeo.pointIndexCache; + for (int n = firstPointIndexCache; n <= lastPointIndexCache; n++) { + int ioffset = cache.indexOffset[n]; + int icount = cache.indexCount[n]; + int voffset = cache.vertexOffset[n]; + + shader.setVertexAttribute(root.glPointVertexBufferID, 4, PGL.GL_FLOAT, 0, 4 * voffset * PGL.SIZEOF_FLOAT); + shader.setColorAttribute(root.glPointColorBufferID, 4, PGL.GL_UNSIGNED_BYTE, 0, 4 * voffset * PGL.SIZEOF_BYTE); + shader.setPointAttribute(root.glPointAttribBufferID, 2, PGL.GL_FLOAT, 0, 2 * voffset * PGL.SIZEOF_FLOAT); + + pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, root.glPointIndexBufferID); + pgl.glDrawElements(PGL.GL_TRIANGLES, icount, PGL.INDEX_TYPE, ioffset * PGL.SIZEOF_INDEX); + pgl.glBindBuffer(PGL.GL_ELEMENT_ARRAY_BUFFER, 0); + } + + shader.stop(); + } + + + protected void rawPoints(PGraphicsOpenGL g) { + PGraphics raw = g.getRaw(); + + raw.colorMode(RGB); + raw.noFill(); + raw.strokeCap(strokeCap); + raw.beginShape(POINTS); + + float[] vertices = tessGeo.pointVertices; + int[] color = tessGeo.pointColors; + float[] attribs = tessGeo.pointAttribs; + short[] indices = tessGeo.pointIndices; + + IndexCache cache = tessGeo.pointIndexCache; + for (int n = 0; n < cache.size; n++) { + int ioffset = cache.indexOffset[n]; + int icount = cache.indexCount[n]; + int voffset = cache.vertexOffset[n]; + + int pt = ioffset; + while (pt < (ioffset + icount) / 3) { + float size = attribs[2 * pt + 2]; + float weight; + int perim; + if (0 < size) { // round point + weight = +size / 0.5f; + perim = PApplet.max(PGraphicsOpenGL.MIN_POINT_ACCURACY, + (int) (TWO_PI * weight / PGraphicsOpenGL.POINT_ACCURACY_FACTOR)) + 1; + } else { // Square point + weight = -size / 0.5f; + perim = 5; + } + + int i0 = voffset + indices[3 * pt]; + int argb0 = PGL.nativeToJavaARGB(color[i0]); + float[] pt0 = {0, 0, 0, 0}; + + float[] src0 = {0, 0, 0, 0}; + PApplet.arrayCopy(vertices, 4 * i0, src0, 0, 4); + g.modelview.mult(src0, pt0); + + if (raw.is3D()) { + raw.strokeWeight(weight); + raw.stroke(argb0); + raw.vertex(pt0[X], pt0[Y], pt0[Z]); + } else if (raw.is2D()) { + float sx0 = g.screenXImpl(pt0[0], pt0[1], pt0[2], pt0[3]), sy0 = g.screenYImpl(pt0[0], pt0[1], pt0[2], pt0[3]); + raw.strokeWeight(weight); + raw.stroke(argb0); + raw.vertex(sx0, sy0); + } + + pt += perim; + } + } + + raw.endShape(); + } +} diff --git a/android/core/src/processing/opengl/PTexture.java b/android/core/src/processing/opengl/PTexture.java index 9495339a0..90a856de7 100644 --- a/android/core/src/processing/opengl/PTexture.java +++ b/android/core/src/processing/opengl/PTexture.java @@ -3,11 +3,12 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2010 Ben Fry and Casey Reas + Copyright (c) 2011-12 Ben Fry and Casey Reas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public - License version 2.1 as published by the Free Software Foundation. + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -22,18 +23,16 @@ package processing.opengl; +import processing.core.PApplet; +import processing.core.PConstants; +import processing.core.PGraphics; +import processing.core.PImage; import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.util.LinkedList; import java.util.NoSuchElementException; -import processing.core.PApplet; -import processing.core.PConstants; -import processing.core.PGraphics; -import processing.core.PImage; - - /** * This class wraps an OpenGL texture. * By Andres Colubri @@ -59,8 +58,8 @@ public class PTexture implements PConstants { public int glHeight; protected boolean usingMipmaps; - protected float maxTexCoordU; - protected float maxTexCoordV; + protected float maxTexcoordU; + protected float maxTexcoordV; protected boolean flippedX; protected boolean flippedY; @@ -250,17 +249,72 @@ public class PTexture implements PConstants { pgl.glBindTexture(glTarget, glID); if (usingMipmaps) { - if (PGraphicsOpenGL.mipmapGeneration) { + if (PGraphicsOpenGL.autoMipmapGenSupported) { // Automatic mipmap generation. int[] rgbaPixels = new int[w * h]; convertToRGBA(pixels, rgbaPixels, format, w, h); setTexels(rgbaPixels, x, y, w, h); pgl.glGenerateMipmap(glTarget); rgbaPixels = null; - } else { - // TODO: Manual mipmap generation. - // Open source implementation of gluBuild2DMipmaps here: - // http://code.google.com/p/glues/source/browse/trunk/glues/source/glues_mipmap.c + } else { + // TODO: finish manual mipmap generation, replacing Bitmap with AWT's BufferedImage, + // making it work in npot textures (embed npot tex into larger pot tex?), subregions, + // and moving GLUtils.texImage2D (originally from Android SDK) into PGL. + // Actually, this whole code should go into PGL, so the Android implementation can + // use Bitmap, and desktop use BufferedImage. + + /* + if (w != width || h != height) { + System.err.println("Sorry but I don't know how to generate mipmaps for a subregion."); + return; + } + + // Code by Mike Miller obtained from here: + // http://insanitydesign.com/wp/2009/08/01/android-opengl-es-mipmaps/ + int w0 = glWidth; + int h0 = glHeight; + int[] argbPixels = new int[w0 * h0]; + convertToARGB(pixels, argbPixels, format); + int level = 0; + int denom = 1; + + // We create a Bitmap because then we use its built-in filtered downsampling + // functionality. + Bitmap bitmap = Bitmap.createBitmap(w0, h0, Config.ARGB_8888); + bitmap.setPixels(argbPixels, 0, w0, 0, 0, w0, h0); + + while (w0 >= 1 || h0 >= 1) { + //First of all, generate the texture from our bitmap and set it to the according level + GLUtils.texImage2D(glTarget, level, bitmap, 0); + + // We are done. + if (w0 == 1 && h0 == 1) { + break; + } + + // Increase the mipmap level + level++; + denom *= 2; + + // Downsampling bitmap. We must eventually arrive to the 1x1 level, + // and if the width and height are different, there will be a few 1D + // texture levels just before. + // This update formula also allows for NPOT resolutions. + w0 = PApplet.max(1, PApplet.floor((float)glWidth / denom)); + h0 = PApplet.max(1, PApplet.floor((float)glHeight / denom)); + // (see getScaledInstance in AWT Image) + Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, w0, h0, true); + + // Clean up + bitmap.recycle(); + bitmap = bitmap2; + } + */ + + int[] rgbaPixels = new int[w * h]; + convertToRGBA(pixels, rgbaPixels, format, w, h); + setTexels(rgbaPixels, x, y, w, h); + rgbaPixels = null; } } else { int[] rgbaPixels = new int[w * h]; @@ -347,8 +401,8 @@ public class PTexture implements PConstants { * Returns the maximum possible value for the texture coordinate U (horizontal). * @return float */ - public float getMaxTexCoordU() { - return maxTexCoordU; + public float getMaxU() { + return maxTexcoordU; } @@ -356,8 +410,8 @@ public class PTexture implements PConstants { * Returns the maximum possible value for the texture coordinate V (vertical). * @return float */ - public float getMaxTexCoordV() { - return maxTexCoordV; + public float getMaxV() { + return maxTexcoordV; } @@ -743,8 +797,8 @@ public class PTexture implements PConstants { // is non-power-of-two, then glWidth (glHeight) will be greater than w (h) because it // is chosen to be the next power of two, and this quotient will give the appropriate // maximum texture coordinate value given this situation. - maxTexCoordU = (float)width / glWidth; - maxTexCoordV = (float)height / glHeight; + maxTexcoordU = (float)width / glWidth; + maxTexcoordV = (float)height / glHeight; } @@ -878,8 +932,8 @@ public class PTexture implements PConstants { glHeight = src.glHeight; usingMipmaps = src.usingMipmaps; - maxTexCoordU = src.maxTexCoordU; - maxTexCoordV = src.maxTexCoordV; + maxTexcoordU = src.maxTexcoordU; + maxTexcoordV = src.maxTexcoordV; flippedX = src.flippedX; flippedY = src.flippedY; @@ -905,14 +959,26 @@ public class PTexture implements PConstants { res.format = ALPHA; } - if (glMinFilter == PGL.GL_NEAREST) { + if (glMagFilter == PGL.GL_NEAREST && glMinFilter == PGL.GL_NEAREST) { res.sampling = POINT; - } else if (glMinFilter == PGL.GL_LINEAR) { + res.mipmaps = false; + } else if (glMagFilter == PGL.GL_NEAREST && glMinFilter == PGL.GL_LINEAR) { + res.sampling = LINEAR; + res.mipmaps = false; + } else if (glMagFilter == PGL.GL_NEAREST && glMinFilter == PGL.GL_LINEAR_MIPMAP_NEAREST) { + res.sampling = LINEAR; + res.mipmaps = true; + } else if (glMagFilter == PGL.GL_LINEAR && glMinFilter == PGL.GL_LINEAR) { res.sampling = BILINEAR; - } else if (glMinFilter == PGL.GL_LINEAR_MIPMAP_LINEAR) { + res.mipmaps = false; + } else if (glMagFilter == PGL.GL_LINEAR && glMinFilter == PGL.GL_LINEAR_MIPMAP_NEAREST) { + res.sampling = BILINEAR; + res.mipmaps = true; + } else if (glMagFilter == PGL.GL_LINEAR && glMinFilter == PGL.GL_LINEAR_MIPMAP_LINEAR) { res.sampling = TRILINEAR; + res.mipmaps = true; } - + if (glWrapS == PGL.GL_CLAMP_TO_EDGE) { res.wrapU = CLAMP; } else if (glWrapS == PGL.GL_REPEAT) { @@ -938,7 +1004,7 @@ public class PTexture implements PConstants { if (params.target == TEXTURE2D) { glTarget = PGL.GL_TEXTURE_2D; } else { - throw new RuntimeException("OPENGL2: Unknown texture target"); + throw new RuntimeException("Unknown texture target"); } if (params.format == RGB) { @@ -948,20 +1014,23 @@ public class PTexture implements PConstants { } else if (params.format == ALPHA) { glFormat = PGL.GL_ALPHA; } else { - throw new RuntimeException("OPENGL2: Unknown texture format"); + throw new RuntimeException("Unknown texture format"); } if (params.sampling == POINT) { glMagFilter = PGL.GL_NEAREST; glMinFilter = PGL.GL_NEAREST; + } else if (params.sampling == LINEAR) { + glMagFilter = PGL.GL_NEAREST; + glMinFilter = params.mipmaps ? PGL.GL_LINEAR_MIPMAP_NEAREST : PGL.GL_LINEAR; } else if (params.sampling == BILINEAR) { glMagFilter = PGL.GL_LINEAR; - glMinFilter = PGL.GL_LINEAR; + glMinFilter = params.mipmaps ? PGL.GL_LINEAR_MIPMAP_NEAREST : PGL.GL_LINEAR; } else if (params.sampling == TRILINEAR) { glMagFilter = PGL.GL_LINEAR; - glMinFilter = PGL.GL_LINEAR_MIPMAP_LINEAR; + glMinFilter = PGL.GL_LINEAR_MIPMAP_LINEAR; } else { - throw new RuntimeException("OPENGL2: Unknown texture filtering mode"); + throw new RuntimeException("Unknown texture filtering mode"); } if (params.wrapU == CLAMP) { @@ -969,7 +1038,7 @@ public class PTexture implements PConstants { } else if (params.wrapU == REPEAT) { glWrapS = PGL.GL_REPEAT; } else { - throw new RuntimeException("OPENGL2: Unknown wrapping mode"); + throw new RuntimeException("Unknown wrapping mode"); } if (params.wrapV == CLAMP) { @@ -977,10 +1046,11 @@ public class PTexture implements PConstants { } else if (params.wrapV == REPEAT) { glWrapT = PGL.GL_REPEAT; } else { - throw new RuntimeException("OPENGL2: Unknown wrapping mode"); + throw new RuntimeException("Unknown wrapping mode"); } - usingMipmaps = glMinFilter == PGL.GL_LINEAR_MIPMAP_LINEAR; + usingMipmaps = glMinFilter == PGL.GL_LINEAR_MIPMAP_NEAREST || + glMinFilter == PGL.GL_LINEAR_MIPMAP_LINEAR; flippedX = false; flippedY = false; @@ -990,27 +1060,7 @@ public class PTexture implements PConstants { /////////////////////////////////////////////////////////////////////////// // Parameters object - - - static public Parameters newParameters() { - return new Parameters(); - } - - static public Parameters newParameters(int format) { - return new Parameters(format); - } - - - static public Parameters newParameters(int format, int sampling) { - return new Parameters(format, sampling); - } - - - static public Parameters newParameters(Parameters params) { - return new Parameters(params); - } - /** * This class stores the parameters for a texture: target, internal format, minimization filter @@ -1028,10 +1078,15 @@ public class PTexture implements PConstants { public int format; /** - * Texture filtering (POINT, BILINEAR or TRILINEAR). + * Texture filtering (POINT, LINEAR, BILINEAR or TRILINEAR). */ public int sampling; + /** + * Use mipmaps or not. + */ + public boolean mipmaps; + /** * Wrapping mode along U. */ @@ -1043,12 +1098,13 @@ public class PTexture implements PConstants { public int wrapV; /** - * Creates an instance of GLTextureParameters, setting all the parameters to default values. + * Sets all the parameters to default values. */ public Parameters() { this.target = TEXTURE2D; this.format = ARGB; this.sampling = BILINEAR; + this.mipmaps = true; this.wrapU = CLAMP; this.wrapV = CLAMP; } @@ -1057,6 +1113,7 @@ public class PTexture implements PConstants { this.target = TEXTURE2D; this.format = format; this.sampling = BILINEAR; + this.mipmaps = true; this.wrapU = CLAMP; this.wrapV = CLAMP; } @@ -1065,16 +1122,22 @@ public class PTexture implements PConstants { this.target = TEXTURE2D; this.format = format; this.sampling = sampling; + this.mipmaps = true; this.wrapU = CLAMP; this.wrapV = CLAMP; } + public Parameters(int format, int sampling, boolean mipmaps) { + this.target = TEXTURE2D; + this.format = format; + this.sampling = sampling; + this.mipmaps = mipmaps; + this.wrapU = CLAMP; + this.wrapV = CLAMP; + } + public Parameters(Parameters src) { - this.target = src.target; - this.format = src.format; - this.sampling = src.sampling; - this.wrapU = src.wrapU; - this.wrapV = src.wrapV; + set(src); } public void set(int format) { @@ -1086,10 +1149,17 @@ public class PTexture implements PConstants { this.sampling = sampling; } + public void set(int format, int sampling, boolean mipmaps) { + this.format = format; + this.sampling = sampling; + this.mipmaps = mipmaps; + } + public void set(Parameters src) { this.target = src.target; this.format = src.format; this.sampling = src.sampling; + this.mipmaps = src.mipmaps; this.wrapU = src.wrapU; this.wrapV = src.wrapV; } diff --git a/android/core/src/processing/opengl/geom/LineSink.java b/android/core/src/processing/opengl/geom/LineSink.java deleted file mode 100644 index 2e57143cd..000000000 --- a/android/core/src/processing/opengl/geom/LineSink.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package processing.opengl.geom; - -/** - * The LineSink interface accepts a series of line - * drawing commands: moveTo, lineTo, - * close (equivalent to a lineTo command - * with an argument equal to the argument of the last - * moveTo command), and end. - * - */ -public abstract class LineSink { - /** - * Moves the current drawing position to the point (x0, - * y0). - * - * @param x0 the X coordinate in S15.16 format - * @param y0 the Y coordinate in S15.16 format - */ - public abstract void moveTo(int x0, int y0); - - /** - * Provides a hint that the current segment should be joined to - * the following segment using an explicit miter or round join if - * required. - * - *

An application-generated path will generally have no need - * to contain calls to this method; they are typically introduced - * by a Flattener to mark segment divisions that - * appear in its input, and consumed by a Stroker - * that is responsible for emitting the miter or round join - * segments. - * - *

Other LineSink classes should simply pass this - * hint to their output sink as needed. - */ - public abstract void lineJoin(); - - /** - * Draws a line from the current drawing position to the point - * (x1, y1) and sets the current drawing position to - * (x1, y1). - * - * @param x1 the X coordinate in S15.16 format - * @param y1 the Y coordinate in S15.16 format - */ - public abstract void lineTo(int x1, int y1); - - /** - * Closes the current path by drawing a line from the current - * drawing position to the point specified by the moset recent - * moveTo command. - */ - public abstract void close(); - - /** - * Ends the current path. It may be necessary to end a path in - * order to allow end caps to be drawn. - */ - public abstract void end(); -} diff --git a/core/src/processing/core/PConstants.java b/core/src/processing/core/PConstants.java index 791c99492..448724d08 100644 --- a/core/src/processing/core/PConstants.java +++ b/core/src/processing/core/PConstants.java @@ -577,7 +577,7 @@ public interface PConstants { static final int DISABLE_TEXTURE_MIPMAPS = 11; static final int ENABLE_TEXTURE_MIPMAPS = -11; - static final int HINT_COUNT = 12; + static final int HINT_COUNT = 12; // error messages