From 1cd006af198e0acfde22dbb754fa4ce344958e72 Mon Sep 17 00:00:00 2001 From: codeanticode Date: Wed, 16 May 2012 01:16:59 +0000 Subject: [PATCH] android sync --- android/core/src/processing/core/PShape.java | 163 ++++++-- .../src/processing/opengl/PGraphics2D.java | 18 +- .../processing/opengl/PGraphicsOpenGL.java | 374 ++++++++++++++---- .../core/src/processing/opengl/PShape3D.java | 220 ++++++----- core/src/processing/core/PShape.java | 5 +- 5 files changed, 563 insertions(+), 217 deletions(-) diff --git a/android/core/src/processing/core/PShape.java b/android/core/src/processing/core/PShape.java index 28b43a7a8..1d9e3aaec 100644 --- a/android/core/src/processing/core/PShape.java +++ b/android/core/src/processing/core/PShape.java @@ -664,6 +664,122 @@ public class PShape implements PConstants { } } + + + //////////////////////////////////////////////////////////////////////// + // + // The new copy methods to put an SVG into a PShape3D, for example + + 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); + } + return res; + } + + + 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); + } + } + + + protected void copyPrimitive(PShape s) { + if (matrix != null) { + s.applyMatrix(matrix); + } + copyStyles(s); + copyImage(s); + } + + protected void copyGeometry(PShape s) { + if (matrix != null) { + s.applyMatrix(matrix); + } + copyStyles(s); + copyImage(s); + + if (style) { + for (int i = 0; i < vertexCount; i++) { + float[] vert = vertices[i]; +// 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]); + } + } else { + for (int i = 0; i < vertexCount; i++) { + float[] vert = vertices[i]; + if (vert[PGraphics.Z] == 0) { + s.vertex(vert[X], vert[Y]); + } else { + s.vertex(vert[X], vert[Y], vert[Z]); + } + } + } + + s.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); + + } + + protected void copyStyles(PShape s) { + if (stroke) { + s.stroke = true; + s.strokeColor = strokeColor; + s.strokeWeight = strokeWeight; + s.strokeCap = strokeCap; + s.strokeJoin = strokeJoin; + } else { + s.stroke = false; + } + + if (fill) { + s.fill = true; + s.fillColor = fillColor; + } else { + s.fill = false; + } + } + + protected void copyImage(PShape s) { + if (image != null) { + s.texture(image); + } + } + + //////////////////////////////////////////////////////////////////////// + /** * Called by the following (the shape() command adds the g) @@ -1143,42 +1259,27 @@ public class PShape implements PConstants { } - public void setPath(float[][] coords) { - setPath(coords, null); + public void setPath(int vcount, float[][] verts) { + setPath(vcount, verts, 0, null); } - public void setPath(float[][] coords, int[] codes) { - if (coords == null || coords.length == 0) return; - - if (codes == null || codes.length == 0) { - vertexCodeCount = 0; - } else { - if (codes.length != coords.length) { - PGraphics.showWarning("Wrong number of vertex codes"); - return; - } - vertexCodeCount = codes.length; - } + public void setPath(int vcount, float[][] verts, int ccount, int[] codes) { + if (verts == null || verts.length < vcount) return; + if (0 < ccount && (codes == null || codes.length < ccount)) return; - int ncoords = coords[0].length; - - if (vertices == null || - vertices.length != coords.length || - vertices[0].length != ncoords) { - vertices = new float[coords.length][ncoords]; - } - - for (int i = 0; i < coords.length; i++) { - PApplet.arrayCopy(coords[i], vertices[i]); - } - + int ndim = verts[0].length; + vertexCount = vcount; + vertices = new float[vertexCount][ndim]; + for (int i = 0; i < vertexCount; i++) { + PApplet.arrayCopy(verts[i], vertices[i]); + } + + vertexCodeCount = ccount; if (0 < vertexCodeCount) { - if (vertexCodes == null || vertexCodes.length != vertexCodeCount) { - vertexCodes = new int[vertexCodeCount]; - } - PApplet.arrayCopy(codes, vertexCodes); - } + vertexCodes = new int[vertexCodeCount]; + PApplet.arrayCopy(codes, vertexCodes, vertexCodeCount); + } } diff --git a/android/core/src/processing/opengl/PGraphics2D.java b/android/core/src/processing/opengl/PGraphics2D.java index 586a376a6..3b7c233ce 100644 --- a/android/core/src/processing/opengl/PGraphics2D.java +++ b/android/core/src/processing/opengl/PGraphics2D.java @@ -27,7 +27,23 @@ public class PGraphics2D extends PGraphicsOpenGL { public PGraphics2D() { super(); - hints[ENABLE_ACCURATE_2D] = true; + //hints[ENABLE_ACCURATE_2D] = true; + hints[ENABLE_PERSPECTIVE_CORRECTED_LINES] = false; } + /** + * 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 false; + } + } \ 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 8aa89eed3..55367e19f 100644 --- a/android/core/src/processing/opengl/PGraphicsOpenGL.java +++ b/android/core/src/processing/opengl/PGraphicsOpenGL.java @@ -26,6 +26,7 @@ import java.nio.*; import java.util.*; import processing.core.*; +import processing.opengl.geom.LinePath; // drawPixels is missing...calls to glDrawPixels are commented out @@ -361,10 +362,15 @@ public class PGraphicsOpenGL extends PGraphics { protected boolean defaultEdges = false; protected PImage textureImage0; + static protected final int EDGE_MIDDLE = 0; + static protected final int EDGE_START = 1; + static protected final int EDGE_STOP = 2; + static protected final int EDGE_SINGLE = 3; + protected boolean perspectiveCorrectedLines = false; /** Used in point tessellation. */ - final static protected int MIN_POINT_ACCURACY = 6; + final static protected int MIN_POINT_ACCURACY = 20; final protected float[][] QUAD_POINT_SIGNS = { {-1, +1}, {-1, -1}, {+1, -1}, {+1, +1} }; @@ -1937,6 +1943,8 @@ public class PGraphicsOpenGL extends PGraphics { 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); @@ -4661,6 +4669,12 @@ public class PGraphicsOpenGL extends PGraphics { // Initializes the pixels array, copying the current contents of the // color buffer into it. public void loadPixels() { + boolean needEndDraw = false; + if (!drawing) { + beginDraw(); + needEndDraw = true; + } + if (!setgetPixels) { // Draws any remaining geometry in case the user is still not // setting/getting new pixels. @@ -4677,6 +4691,10 @@ public class PGraphicsOpenGL extends PGraphics { pixelsToTexture(); } } + + if (needEndDraw) { + endDraw(); + } } @@ -7627,6 +7645,7 @@ public class PGraphicsOpenGL extends PGraphics { begin = true; } else if (!breaks[i1]) { addEdge(i0, i1, begin, false); + begin = false; } } } @@ -7972,10 +7991,7 @@ public class PGraphicsOpenGL extends PGraphics { float sx2 = screenX(x + w, y + h); float sy2 = screenY(x + w, y + h); - int accuracy = (int) (TWO_PI * PApplet.dist(sx1, sy1, sx2, sy2) / 20); - if (accuracy < 6) { - accuracy = 6; - } + int accuracy = PApplet.max(MIN_POINT_ACCURACY, (int) (TWO_PI * PApplet.dist(sx1, sy1, sx2, sy2) / 20)); float inc = (float) PGraphicsOpenGL.SINCOS_LENGTH / accuracy; if (fill) { @@ -8550,18 +8566,18 @@ public class PGraphicsOpenGL extends PGraphics { void getLineVertexMin(PVector v, int first, int last) { for (int i = first; i <= last; i++) { int index = 4 * i; - v.x += PApplet.min(v.x, lineVertices[index++]); - v.y += PApplet.min(v.y, lineVertices[index++]); - v.z += PApplet.min(v.z, lineVertices[index ]); + v.x = PApplet.min(v.x, lineVertices[index++]); + v.y = PApplet.min(v.y, lineVertices[index++]); + v.z = PApplet.min(v.z, lineVertices[index ]); } } void getPointVertexMin(PVector v, int first, int last) { for (int i = first; i <= last; i++) { int index = 4 * i; - v.x += PApplet.min(v.x, pointVertices[index++]); - v.y += PApplet.min(v.y, pointVertices[index++]); - v.z += PApplet.min(v.z, pointVertices[index ]); + v.x = PApplet.min(v.x, pointVertices[index++]); + v.y = PApplet.min(v.y, pointVertices[index++]); + v.z = PApplet.min(v.z, pointVertices[index ]); } } @@ -8577,18 +8593,18 @@ public class PGraphicsOpenGL extends PGraphics { void getLineVertexMax(PVector v, int first, int last) { for (int i = first; i <= last; i++) { int index = 4 * i; - v.x += PApplet.max(v.x, lineVertices[index++]); - v.y += PApplet.max(v.y, lineVertices[index++]); - v.z += PApplet.max(v.z, lineVertices[index ]); + v.x = PApplet.max(v.x, lineVertices[index++]); + v.y = PApplet.max(v.y, lineVertices[index++]); + v.z = PApplet.max(v.z, lineVertices[index ]); } } void getPointVertexMax(PVector v, int first, int last) { for (int i = first; i <= last; i++) { int index = 4 * i; - v.x += PApplet.max(v.x, pointVertices[index++]); - v.y += PApplet.max(v.y, pointVertices[index++]); - v.z += PApplet.max(v.z, pointVertices[index ]); + v.x = PApplet.max(v.x, pointVertices[index++]); + v.y = PApplet.max(v.y, pointVertices[index++]); + v.z = PApplet.max(v.z, pointVertices[index ]); } } @@ -9548,6 +9564,7 @@ public class PGraphicsOpenGL extends PGraphics { } lastPointIndexCache = index; +// NEW TESSMAP API // if (tess.renderMode == RETAINED) { // in.addPointMapping(in.firstVertex, in.lastVertex, tess.firstPointVertex, nPtVert); // } @@ -9620,6 +9637,7 @@ public class PGraphicsOpenGL extends PGraphics { } lastPointIndexCache = index; +// NEW TESSMAP API // if (tess.renderMode == RETAINED) { // in.addPointMapping(in.firstVertex, in.lastVertex, tess.firstPointVertex, 5); // } @@ -9632,57 +9650,82 @@ public class PGraphicsOpenGL extends PGraphics { void tessellateLines() { int nInVert = in.lastVertex - in.firstVertex + 1; - + if (stroke && 2 <= nInVert) { int lineCount = nInVert / 2; int first = in.firstVertex; - - // 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; - + 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); + } } } void tessellateLineStrip() { int nInVert = in.lastVertex - in.firstVertex + 1; - + int lineCount = nInVert - 1; + if (stroke && 2 <= nInVert) { - int lineCount = nInVert - 1; - 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; - -// if (tess.renderMode == RETAINED) { -// addLineMapping(in.firstVertex, in.lastVertex); -// } + 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); + } } } @@ -9691,47 +9734,119 @@ public class PGraphicsOpenGL extends PGraphics { if (stroke && 2 <= nInVert) { int lineCount = nInVert; - 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; + 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); } - index = addLine(in.lastVertex, in.firstVertex, index, false); - lastLineIndexCache = index; - -// if (tess.renderMode == RETAINED) { -// addLineMapping(in.firstVertex, in.lastVertex); -// } } } void tessellateEdges() { if (stroke) { - int nInVert = in.getNumLineVertices(); - int nInInd = in.getNumLineIndices(); + 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); + 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); } - lastLineIndexCache = index; - -// if (tess.renderMode == RETAINED) { -// addLineMapping(in.firstVertex, in.lastVertex); -// } } + + + +// 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; } // Adding the data that defines a quad starting at vertex i0 and @@ -9774,6 +9889,7 @@ public class PGraphicsOpenGL extends PGraphics { 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); // } @@ -10031,6 +10147,7 @@ public class PGraphicsOpenGL extends PGraphics { void tessellatePolygon(boolean solid, boolean closed, boolean calcNormals) { int nInVert = in.lastVertex - in.firstVertex + 1; +// NEW TESSMAP API // if (tess.renderMode == RETAINED) { // in.addWeightedMapping(in.firstVertex, in.lastVertex); // } @@ -10097,8 +10214,91 @@ public class PGraphicsOpenGL extends PGraphics { } 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; + + int cap = strokeCap == ROUND ? LinePath.CAP_ROUND : + strokeCap == PROJECT ? LinePath.CAP_SQUARE : + LinePath.CAP_BUTT; + int join = strokeJoin == ROUND ? LinePath.JOIN_ROUND : + strokeJoin == BEVEL ? LinePath.JOIN_BEVEL : + LinePath.JOIN_MITER; + + // Make the outline of the stroke from the path + LinePath strokedPath = LinePath.createStrokedPath(path, strokeWeight, cap, join); + + gluTess.beginPolygon(); + + double[] vertex; + float[] coords = new float[6]; + + LinePath.PathIterator iter = strokedPath.getPathIterator(); + int rule = iter.getWindingRule(); + switch(rule) { + case LinePath.WIND_EVEN_ODD: + gluTess.setWindingRule(PGL.GLU_TESS_WINDING_ODD); + break; + case LinePath.WIND_NON_ZERO: + gluTess.setWindingRule(PGL.GLU_TESS_WINDING_NONZERO); + break; + } + + while (!iter.isDone()) { + float sr = 0; + float sg = 0; + float sb = 0; + float sa = 0; + + switch (iter.currentSegment(coords)) { + + case LinePath.SEG_MOVETO: + gluTess.beginContour(); + + case LinePath.SEG_LINETO: + sa = (strokeColor >> 24) & 0xFF; + sr = (strokeColor >> 16) & 0xFF; + sg = (strokeColor >> 8) & 0xFF; + sb = (strokeColor >> 0) & 0xFF; + + // Vertex data includes coordinates, colors, normals, texture coordinates, and material properties. + vertex = new double[] { coords[0], coords[1], 0, + 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!!!!!!!!!!! + + gluTess.addVertex(vertex); + + break; + case LinePath.SEG_CLOSE: + gluTess.endContour(); + break; + } + iter.next(); + } + gluTess.endPolygon(); } + ///////////////////////////////////////// + + // Interenting notes about using the GLU tessellator to render thick polylines: + // http://stackoverflow.com/questions/687173/how-do-i-render-thick-2d-lines-as-polygons + // + // "...Since I disliked the tesselator API I lifted the tesselation code from the free + // SGI OpenGL reference implementation, rewrote the entire front-end and added memory + // pools to get the number of allocations down. It took two days to do this, but it was + // well worth it (like factor five performance improvement)..." + // + // This C implementation of GLU could be useful: + // http://code.google.com/p/glues/ + // to eventually come up with an optimized GLU tessellator in native code. protected class TessellatorCallback implements PGL.TessellatorCallback { boolean calcNormals; IndexCache cache; diff --git a/android/core/src/processing/opengl/PShape3D.java b/android/core/src/processing/opengl/PShape3D.java index 6b0ffa24a..0ffb18e4c 100644 --- a/android/core/src/processing/opengl/PShape3D.java +++ b/android/core/src/processing/opengl/PShape3D.java @@ -32,16 +32,6 @@ import java.util.Arrays; import java.util.HashSet; -//Notes about geometry update in PShape3D. -//1) When applying a transformation on a group shape -// check if it is more efficient to apply as a gl -// transformation on all the childs, instead of -// propagating the transformation downwards in order -// to calculate the transformation matrices. -//2) Change the transformation logic, so the matrix is applied -// on the values stored in the vertex cache and not on the -// tessellated vertices. - /** * 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 @@ -54,7 +44,7 @@ import java.util.HashSet; * 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://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 { @@ -470,7 +460,7 @@ public class PShape3D extends PShape { PVector max = new PVector(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY); if (shapeEnded) { getVertexMin(min); - getVertexMin(max); + getVertexMax(max); } width = max.x - min.x; return width; @@ -482,9 +472,9 @@ public class PShape3D extends PShape { PVector max = new PVector(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY); if (shapeEnded) { getVertexMin(min); - getVertexMin(max); + getVertexMax(max); } - width = max.y - min.y; + height = max.y - min.y; return height; } @@ -494,9 +484,9 @@ public class PShape3D extends PShape { PVector max = new PVector(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY); if (shapeEnded) { getVertexMin(min); - getVertexMin(max); + getVertexMax(max); } - width = max.z - min.z; + depth = max.z - min.z; return depth; } @@ -517,59 +507,53 @@ public class PShape3D extends PShape { protected void getVertexMin(PVector min) { + updateTessellation(); + if (family == GROUP) { for (int i = 0; i < childCount; i++) { PShape3D child = (PShape3D) children[i]; - child.getVertexMin(min); + child.getVertexMin(min); } } else { - if (tessellated) { - if (haveFill) tessGeo.getFillVertexMin(min, firstFillVertex, lastFillVertex); - if (haveLines) tessGeo.getLineVertexMin(min, firstLineVertex, lastLineVertex); - if (havePoints) tessGeo.getPointVertexMin(min, firstPointVertex, lastPointVertex); - } else { - inGeo.getVertexMin(min); - } + 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 (tessellated) { - if (haveFill) tessGeo.getFillVertexMax(max, firstFillVertex, lastFillVertex); - if (haveLines) tessGeo.getLineVertexMax(max, firstLineVertex, lastLineVertex); - if (havePoints) tessGeo.getPointVertexMax(max, firstPointVertex, lastPointVertex); - } else { - inGeo.getVertexMax(max); - } + 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 (tessellated) { - if (haveFill) count += tessGeo.getFillVertexSum(sum, firstFillVertex, lastFillVertex); - if (haveLines) count += tessGeo.getLineVertexSum(sum, firstLineVertex, lastLineVertex); - if (havePoints) count += tessGeo.getPointVertexSum(sum, firstPointVertex, lastPointVertex); - } else { - count += inGeo.getVertexSum(sum); - } + 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; } - + /////////////////////////////////////////////////////////// @@ -840,29 +824,13 @@ public class PShape3D extends PShape { } - public void setPath(float[][] coords) { - setPath(coords, null, OPEN); - } - - - public void setPath(float[][] coords, int mode) { - setPath(coords, null, mode); - } - - - public void setPath(float[][] coords, int[] codes) { - setPath(coords, codes, OPEN); - } - - - public void setPath(float[][] coords, int[] codes, int mode) { + 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(coords, codes); - isClosed = mode == CLOSE; + super.setPath(vcount, verts, ccount, codes); root.tessellated = false; tessellated = false; shapeEnded = true; @@ -1043,7 +1011,7 @@ public class PShape3D extends PShape { 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, PGL.javaToNativeARGB(fillColor)); + Arrays.fill(tessGeo.fillColors, firstFillVertex, lastFillVertex + 1, PGL.javaToNativeARGB(fillColor)); root.setModifiedFillColors(firstFillVertex, lastFillVertex); } } @@ -1157,11 +1125,11 @@ public class PShape3D extends PShape { if (shapeEnded && tessellated && (haveLines || havePoints)) { Arrays.fill(inGeo.scolors, 0, inGeo.vertexCount, PGL.javaToNativeARGB(strokeColor)); if (haveLines) { - Arrays.fill(tessGeo.lineColors, firstLineVertex, lastLineVertex, PGL.javaToNativeARGB(strokeColor)); + Arrays.fill(tessGeo.lineColors, firstLineVertex, lastLineVertex + 1, PGL.javaToNativeARGB(strokeColor)); root.setModifiedLineColors(firstLineVertex, lastLineVertex); } if (havePoints) { - Arrays.fill(tessGeo.pointColors, firstPointVertex, lastPointVertex, PGL.javaToNativeARGB(strokeColor)); + Arrays.fill(tessGeo.pointColors, firstPointVertex, lastPointVertex + 1, PGL.javaToNativeARGB(strokeColor)); root.setModifiedPointColors(firstPointVertex, lastPointVertex); } } @@ -1275,7 +1243,7 @@ public class PShape3D extends PShape { 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, PGL.javaToNativeARGB(tintColor)); + Arrays.fill(tessGeo.fillColors, firstFillVertex, lastFillVertex + 1, PGL.javaToNativeARGB(tintColor)); root.setModifiedFillColors(firstFillVertex, lastFillVertex); } } @@ -1334,7 +1302,7 @@ public class PShape3D extends PShape { protected void updateAmbientColor() { if (shapeEnded && tessellated && haveFill) { Arrays.fill(inGeo.ambient, 0, inGeo.vertexCount, PGL.javaToNativeARGB(ambientColor)); - Arrays.fill(tessGeo.fillAmbient, firstFillVertex, lastFillVertex, PGL.javaToNativeARGB(ambientColor)); + Arrays.fill(tessGeo.fillAmbient, firstFillVertex, lastFillVertex = 1, PGL.javaToNativeARGB(ambientColor)); root.setModifiedFillAmbient(firstFillVertex, lastFillVertex); } } @@ -1393,7 +1361,7 @@ public class PShape3D extends PShape { protected void updateSpecularColor() { if (shapeEnded && tessellated && haveFill) { Arrays.fill(inGeo.specular, 0, inGeo.vertexCount, PGL.javaToNativeARGB(specularColor)); - Arrays.fill(tessGeo.fillSpecular, firstFillVertex, lastFillVertex, PGL.javaToNativeARGB(specularColor)); + Arrays.fill(tessGeo.fillSpecular, firstFillVertex, lastFillVertex + 1, PGL.javaToNativeARGB(specularColor)); root.setModifiedFillSpecular(firstFillVertex, lastFillVertex); } } @@ -1452,7 +1420,7 @@ public class PShape3D extends PShape { 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, PGL.javaToNativeARGB(emissiveColor)); + Arrays.fill(tessGeo.fillEmissive, firstFillVertex, lastFillVertex + 1, PGL.javaToNativeARGB(emissiveColor)); root.setModifiedFillEmissive(firstFillVertex, lastFillVertex); } } @@ -1479,7 +1447,7 @@ public class PShape3D extends PShape { protected void updateShininessFactor() { if (shapeEnded && tessellated && haveFill) { Arrays.fill(inGeo.shininess, 0, inGeo.vertexCount, shininess); - Arrays.fill(tessGeo.fillShininess, firstFillVertex, lastFillVertex, shininess); + Arrays.fill(tessGeo.fillShininess, firstFillVertex, lastFillVertex + 1, shininess); root.setModifiedFillShininess(firstFillVertex, lastFillVertex); } } @@ -2399,7 +2367,7 @@ public class PShape3D extends PShape { return tess; } - + /////////////////////////////////////////////////////////// @@ -2736,6 +2704,7 @@ public class PShape3D extends PShape { rounded = true; } + rectMode = CORNER; inGeo.setMaterial(fillColor, strokeColor, strokeWeight, ambientColor, specularColor, emissiveColor, shininess); inGeo.setNormal(normalX, normalY, normalZ); @@ -2762,7 +2731,8 @@ public class PShape3D extends PShape { c = params[2]; d = params[3]; } - + + ellipseMode = CORNER; inGeo.setMaterial(fillColor, strokeColor, strokeWeight, ambientColor, specularColor, emissiveColor, shininess); inGeo.setNormal(normalX, normalY, normalZ); @@ -2784,6 +2754,7 @@ public class PShape3D extends PShape { stop = params[5]; } + ellipseMode = CORNER; inGeo.setMaterial(fillColor, strokeColor, strokeWeight, ambientColor, specularColor, emissiveColor, shininess); inGeo.setNormal(normalX, normalY, normalZ); @@ -2851,39 +2822,43 @@ public class PShape3D extends PShape { 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); + 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); + 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); + fill, stroke, curveDetail, code); + code = VERTEX; index++; case BREAK: if (insideContour) { - code = VERTEX; - } - code = BREAK; + code = BREAK; + } insideContour = true; } } @@ -2893,35 +2868,38 @@ public class PShape3D extends PShape { 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); + 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); + 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); + fill, stroke, curveDetail, code); + code = VERTEX; index++; case BREAK: if (insideContour) { - code = VERTEX; - } - code = BREAK; + code = BREAK; + } insideContour = true; } } @@ -3878,6 +3856,43 @@ public class PShape3D extends PShape { } + /////////////////////////////////////////////////////////// + + // + + // 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? + } + + /////////////////////////////////////////////////////////// // @@ -3892,25 +3907,23 @@ public class PShape3D extends PShape { public void draw(PGraphics g) { if (visible) { + pre(g); + updateTessellation(); updateGeometry(); - + if (family == GROUP) { - if (textures != null && 1 < textures.size()) { - // Some child shape below this group has a non-null matrix - // transformation assigned to it, so the group cannot - // be drawn in a single render call. - // Or, some child shapes below this group use different + 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. + // 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 { - // None of the child shapes below this group has a matrix - // transformation applied to them, so we can render everything - // in a single block. - // And all have the same texture applied to them. + } else { PImage tex = null; if (textures != null && textures.size() == 1) { tex = (PImage)textures.toArray()[0]; @@ -3921,10 +3934,23 @@ public class PShape3D extends PShape { } 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) { diff --git a/core/src/processing/core/PShape.java b/core/src/processing/core/PShape.java index 3ba31654f..dadb43f32 100644 --- a/core/src/processing/core/PShape.java +++ b/core/src/processing/core/PShape.java @@ -734,7 +734,9 @@ public class PShape implements PConstants { - + //////////////////////////////////////////////////////////////////////// + // + // The new copy methods to put an SVG into a PShape3D, for example public PShape copy(PGraphics g) { PShape res = null; @@ -844,6 +846,7 @@ public class PShape implements PConstants { } } + //////////////////////////////////////////////////////////////////////// /**