diff --git a/core/src/processing/core/PShape.java b/core/src/processing/core/PShape.java index 12fdea986..dae0a6a43 100644 --- a/core/src/processing/core/PShape.java +++ b/core/src/processing/core/PShape.java @@ -1245,15 +1245,11 @@ public class PShape implements PConstants { } - public float[] getVertex(int index) { - if (index < 0 || index >= vertexCount) { - String msg = "No vertex " + index + " for this shape, " + - "only vertices 0 through " + (vertexCount-1) + "."; - throw new IllegalArgumentException(msg); - } - return vertices[index]; - } - + + public PVector getVertex(int index) { + return getVertex(index, null); + } + public PVector getVertex(int index, PVector vec) { if (vec == null) { @@ -1280,16 +1276,24 @@ public class PShape implements PConstants { return vertices[index][Z]; } + 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) { vertices[index][X] = x; vertices[index][Y] = y; vertices[index][Z] = z; } + + public PVector getNormal(int index) { + return getNormal(index, null); + } + + public PVector getNormal(int index, PVector vec) { if (vec == null) { vec = new PVector(); @@ -1300,17 +1304,21 @@ public class PShape implements PConstants { return vec; } + public float getNormalX(int index) { return vertices[index][NX]; } + public float getNormalY(int index) { return vertices[index][NY]; - } + } + public float getNormalZ(int index) { - return vertices[index][NZ]; + return vertices[index][NZ]; } + public void setNormal(int index, float nx, float ny, float nz) { vertices[index][NX] = nx; @@ -1318,28 +1326,23 @@ public class PShape implements PConstants { vertices[index][NZ] = nz; } - public PVector getVertexUV(int index, PVector vec) { - if (vec == null) { - vec = new PVector(); - } - vec.x = vertices[index][U]; - vec.y = vertices[index][V]; - return vec; - } - public float getVertexU(int index) { + public float getTextureU(int index) { return vertices[index][U]; } - public float getVertexV(int index) { + + public float getTextureV(int index) { return vertices[index][V]; } - public void setVertexUV(int index, float u, float v) { + + public void setTextureUV(int index, float u, float v) { vertices[index][U] = u; vertices[index][V] = v; } + public int[] getVertexCodes() { if (vertexCodes == null) { return null; diff --git a/java/libraries/opengl/src/processing/opengl/PGraphicsOpenGL.java b/java/libraries/opengl/src/processing/opengl/PGraphicsOpenGL.java index d2dbcb56b..8251d4026 100644 --- a/java/libraries/opengl/src/processing/opengl/PGraphicsOpenGL.java +++ b/java/libraries/opengl/src/processing/opengl/PGraphicsOpenGL.java @@ -994,7 +994,7 @@ public class PGraphicsOpenGL extends PGraphics { } // All OpenGL resources ----------------------------------------------- - + protected void deleteFinalizedGLResources() { deleteFinalizedTextureObjects(); deleteFinalizedVertexBufferObjects(); @@ -1006,17 +1006,6 @@ public class PGraphicsOpenGL extends PGraphics { } -// protected void deleteAllGLResources() { -// deleteAllTextureObjects(); -// deleteAllVertexBufferObjects(); -// deleteAllFrameBufferObjects(); -// deleteAllRenderBufferObjects(); -// deleteAllGLSLProgramObjects(); -// deleteAllGLSLVertShaderObjects(); -// deleteAllGLSLFragShaderObjects(); -// } - - ////////////////////////////////////////////////////////////// // FRAMEBUFFERS @@ -6183,6 +6172,265 @@ public class PGraphicsOpenGL extends PGraphics { } } + // 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[][] lineIndices; + int firstLineIndex; + int[][] fillIndices; + float[][] fillWeights; + int firstFillIndex; + + TessMap() { + in = null; + tess = null; + pointIndices = null; + lineIndices = 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]; + lineIndices = 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; + lineIndices = 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 addLineIndex(int inIdx, int tessIdx) { + int[] indices = lineIndices[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(lineIndices[inIdx], indices, len); + pos = len; + } + indices[pos] = tessIdx; + lineIndices[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); + } + + void setVertex(int inIdx, float x, float y, float z) { + int[] indices; + float[] vertices; + + indices = pointIndices[inIdx]; + vertices = tess.pointVertices; + for (int i = 0; i < indices.length; i++) { + int tessIdx = indices[i]; + vertices[3 * tessIdx + 0] = x; + vertices[3 * tessIdx + 1] = y; + vertices[3 * tessIdx + 2] = z; + } + + indices = lineIndices[inIdx]; + vertices = tess.lineVertices; + for (int i = 0; i < indices.length; i++) { + int tessIdx = indices[i]; + vertices[3 * tessIdx + 0] = x; + vertices[3 * tessIdx + 1] = y; + vertices[3 * tessIdx + 2] = z; + } + + vertices = tess.fillVertices; + if (-1 < firstFillIndex) { + // 1-1 mapping, only need to offset the input index + int tessIdx = firstFillIndex + inIdx; + vertices[3 * tessIdx + 0] = x; + vertices[3 * tessIdx + 1] = y; + vertices[3 * tessIdx + 2] = z; + } else { + // Multi-valued mapping. Going through all the tess + // vertices affected by inIdx. + float x0 = in.vertices[3 * inIdx + 0]; + float y0 = in.vertices[3 * inIdx + 1]; + float z0 = in.vertices[3 * inIdx + 2]; + indices = fillIndices[inIdx]; + float[] weigths = fillWeights[inIdx]; + 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 = weigths[i]; + float tx0 = vertices[3 * tessIdx + 0]; + float ty0 = vertices[3 * tessIdx + 1]; + float tz0 = vertices[3 * tessIdx + 2]; + vertices[3 * tessIdx + 0] = tx0 + weight * (x - x0); + vertices[3 * tessIdx + 1] = ty0 + weight * (y - y0); + vertices[3 * tessIdx + 2] = tz0 + weight * (z - z0); + } + } + } + + void setNormal(int inIdx, float nx, float ny, float nz) { + float[] normals = tess.fillNormals; + + if (-1 < firstFillIndex) { + int tessIdx = firstFillIndex + inIdx; + normals[3 * tessIdx + 0] = nx; + normals[3 * tessIdx + 1] = ny; + normals[3 * tessIdx + 2] = nz; + } else { + float nx0 = in.normals[3 * inIdx + 0]; + float ny0 = in.normals[3 * inIdx + 1]; + float nz0 = in.normals[3 * inIdx + 2]; + int[] indices = fillIndices[inIdx]; + float[] weigths = fillWeights[inIdx]; + for (int i = 0; i < indices.length; i++) { + int tessIdx = indices[i]; + float weight = weigths[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; + } + } + } + + void setTexcoords(int inIdx, float u, float v) { + float[] texcoords = tess.fillTexcoords; + + if (-1 > 24) & 0xFF; -// int fr = (in.colors[i] >> 16) & 0xFF; -// int fg = (in.colors[i] >> 8) & 0xFF; -// int fb = (in.colors[i] >> 0) & 0xFF; - - -// in.colors[3 * index + 2]; - - /* - data[R] = 3; // actual rgb, after lighting - data[G] = 4; // fill stored here, transform in place - data[B] = 5; // TODO don't do that anymore (?) - data[A] = 6; - - data[U] = 7; // texture - data[V] = 8; - - data[NX] = 9; // normal - data[NY] = 10; - data[NZ] = 11; - - // stroke - - data[SR] = 13; - data[SG] = 14; - data[SB] = 15; - data[SA] = 16; - - data[SW = 17; - - // material properties - data[AR] = 25; - data[AG] = 26; - data[AB] = 27; - - data[SPR] = 28; - data[SPG] = 29; - data[SPB] = 30; - - data[SHINE] = 31; - - data[ER] = 32; - data[EG] = 33; - data[EB] = 34; - - data[HAS_NORMAL] = 36; - */ - - - return data; - } - - -// public PVector getVertex(int index) { -// return getVertex(index, null); -// } - - public PVector getVertex(int index, PVector vec) { if (family == GROUP) { PGraphics.showWarning("GROUP shapes don't have any vertices"); @@ -1969,56 +1899,14 @@ public class PShape3D extends PShape { } updateTessellation(); - if (hasPoints) { - int[] indices = inGeo.pointIndices[index]; - for (int i = 0; i < indices.length; i++) { - int tessIdx = indices[i]; - tessGeo.pointVertices[3 * tessIdx + 0] = x; - tessGeo.pointVertices[3 * tessIdx + 1] = y; - tessGeo.pointVertices[3 * tessIdx + 2] = z; - } - modifiedPointVertices = true; - } - - if (hasLines) { - int[] indices = inGeo.lineIndices[index]; - for (int i = 0; i < indices.length; i++) { - int tessIdx = indices[i]; - tessGeo.lineVertices[3 * tessIdx + 0] = x; - tessGeo.lineVertices[3 * tessIdx + 1] = y; - tessGeo.lineVertices[3 * tessIdx + 2] = z; - } - modifiedLineVertices = true; - } - - if (hasFill) { - if (-1 < inGeo.firstFillIndex) { - int tessIdx = inGeo.firstFillIndex + index; - tessGeo.fillVertices[3 * tessIdx + 0] = x; - tessGeo.fillVertices[3 * tessIdx + 1] = y; - tessGeo.fillVertices[3 * tessIdx + 2] = z; - } else { - float x0 = inGeo.vertices[3 * index + 0]; - float y0 = inGeo.vertices[3 * index + 1]; - float z0 = inGeo.vertices[3 * index + 2]; - int[] indices = inGeo.fillIndices[index]; - float[] weigths = inGeo.fillWeights[index]; - for (int i = 0; i < indices.length; i++) { - int tessIdx = indices[i]; - float weight = weigths[i]; - float tx0 = tessGeo.fillVertices[3 * tessIdx + 0]; - float ty0 = tessGeo.fillVertices[3 * tessIdx + 1]; - float tz0 = tessGeo.fillVertices[3 * tessIdx + 2]; - tessGeo.fillVertices[3 * tessIdx + 0] = tx0 + weight * (x - x0); - tessGeo.fillVertices[3 * tessIdx + 1] = ty0 + weight * (y - y0); - tessGeo.fillVertices[3 * tessIdx + 2] = tz0 + weight * (z - z0); - } - } - modifiedFillVertices = true; - } + inGeo.tessMap.setVertex(index, x, y, z); inGeo.vertices[3 * index + 0] = x; inGeo.vertices[3 * index + 1] = y; inGeo.vertices[3 * index + 2] = z; + + if (hasPoints) modifiedPointVertices = true; + if (hasLines) modifiedLineVertices = true; + if (hasFill) modifiedFillVertices = true; modified(); } @@ -2080,67 +1968,17 @@ public class PShape3D extends PShape { } updateTessellation(); - if (hasFill) { - if (-1 < inGeo.firstFillIndex) { - int tessIdx = inGeo.firstFillIndex + index; - tessGeo.fillNormals[3 * tessIdx + 0] = nx; - tessGeo.fillNormals[3 * tessIdx + 1] = ny; - tessGeo.fillNormals[3 * tessIdx + 2] = nz; - } 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.fillIndices[index]; - float[] weigths = inGeo.fillWeights[index]; - for (int i = 0; i < indices.length; i++) { - int tessIdx = indices[i]; - float weight = weigths[i]; - float tnx0 = tessGeo.fillNormals[3 * tessIdx + 0]; - float tny0 = tessGeo.fillNormals[3 * tessIdx + 1]; - float tnz0 = tessGeo.fillNormals[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; - - tessGeo.fillNormals[3 * tessIdx + 0] = tnx; - tessGeo.fillNormals[3 * tessIdx + 1] = tny; - tessGeo.fillNormals[3 * tessIdx + 2] = tnz; - } - } - modifiedFillNormals = true; - } + inGeo.tessMap.setNormal(index, nx, ny, nz); inGeo.normals[3 * index + 0] = nx; inGeo.normals[3 * index + 1] = ny; inGeo.normals[3 * index + 2] = nz; - modified(); - } - - - public PVector getVertexUV(int index, PVector vec) { - if (family == GROUP) { - PGraphics.showWarning("GROUP shapes don't have any vertices"); - return null; - } - updateTessellation(); - if (vec == null) { - vec = new PVector(); - } - vec.x = inGeo.texcoords[2 * index + 0]; - vec.y = inGeo.texcoords[2 * index + 1]; - return vec; + if (hasFill) modifiedFillNormals = true; + modified(); } - public float getVertexU(int index) { + public float getTextureU(int index) { if (family == GROUP) { PGraphics.showWarning("GROUP shapes don't have any vertices"); return 0; @@ -2151,7 +1989,7 @@ public class PShape3D extends PShape { } - public float getVertexV(int index) { + public float getTextureV(int index) { if (family == GROUP) { PGraphics.showWarning("GROUP shapes don't have any vertices"); return 0; @@ -2162,39 +2000,19 @@ public class PShape3D extends PShape { } - public void setVertexUV(int index, float u, float v) { + public void setTextureUV(int index, float u, float v) { if (family == GROUP) { PGraphics.showWarning("GROUP shapes don't have any vertices"); return; } updateTessellation(); - if (hasFill) { - if (-1 < inGeo.firstFillIndex) { - int tessIdx = inGeo.firstFillIndex + index; - tessGeo.fillTexcoords[2 * tessIdx + 0] = u; - tessGeo.fillTexcoords[2 * tessIdx + 1] = v; - } else { - float u0 = inGeo.normals[2 * index + 0]; - float v0 = inGeo.normals[2 * index + 1]; - int[] indices = inGeo.fillIndices[index]; - float[] weigths = inGeo.fillWeights[index]; - for (int i = 0; i < indices.length; i++) { - int tessIdx = indices[i]; - float weight = weigths[i]; - float tu0 = tessGeo.fillTexcoords[2 * tessIdx + 0]; - float tv0 = tessGeo.fillTexcoords[2 * tessIdx + 1]; - float tu = tu0 + weight * (u - u0); - float tv = tv0 + weight * (v - v0); - tessGeo.fillTexcoords[2 * tessIdx + 0] = tu; - tessGeo.fillTexcoords[2 * tessIdx + 1] = tv; - } - } - modifiedFillTexCoords = true; - } + inGeo.tessMap.setTexcoords(index, u, v); + inGeo.texcoords[2 * index + 0] = u; + inGeo.texcoords[2 * index + 1] = v; - inGeo.texcoords[3 * index + 0] = u; - inGeo.texcoords[3 * index + 1] = v; + if (hasFill) modifiedFillTexCoords = true; + modified(); } @@ -2572,7 +2390,7 @@ public class PShape3D extends PShape { // 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.initTessMaps(); + inGeo.initTessMap(tessGeo); if (kind == POINTS) { tessellator.tessellatePoints(); @@ -2646,7 +2464,7 @@ public class PShape3D extends PShape { tessGeo.applyMatrix(matrix); } - inGeo.compactTessMaps(); + inGeo.tessMap.compact(); } } @@ -2671,7 +2489,7 @@ public class PShape3D extends PShape { ambientColor, specularColor, emissiveColor, shininess); inGeo.setNormal(normalX, normalY, normalZ); inGeo.addPoint(x, y, z, fill, stroke); - inGeo.initTessMaps(); + inGeo.initTessMap(tessGeo); tessellator.tessellatePoints(); } @@ -2699,7 +2517,7 @@ public class PShape3D extends PShape { inGeo.addLine(x1, y1, z1, x2, y2, z2, fill, stroke); - inGeo.initTessMaps(); + inGeo.initTessMap(tessGeo); tessellator.tessellateLines(); } @@ -2724,7 +2542,7 @@ public class PShape3D extends PShape { x2, y2, 0, x3, y3, 0, fill, stroke); - inGeo.initTessMaps(); + inGeo.initTessMap(tessGeo); tessellator.tessellateTriangles(); } @@ -2753,7 +2571,7 @@ public class PShape3D extends PShape { x3, y3, 0, x4, y4, 0, fill, stroke); - inGeo.initTessMaps(); + inGeo.initTessMap(tessGeo); tessellator.tessellateQuads(); } @@ -2794,12 +2612,12 @@ public class PShape3D extends PShape { inGeo.addRect(a, b, c, d, tl, tr, br, bl, fill, stroke, bezierDetail, rectMode); - inGeo.initTessMaps(); + inGeo.initTessMap(tessGeo); tessellator.tessellatePolygon(false, true, true); } else { inGeo.addRect(a, b, c, d, fill, stroke, rectMode); - inGeo.initTessMaps(); + inGeo.initTessMap(tessGeo); tessellator.tessellateQuads(); } } @@ -2818,7 +2636,7 @@ public class PShape3D extends PShape { ambientColor, specularColor, emissiveColor, shininess); inGeo.setNormal(normalX, normalY, normalZ); inGeo.addEllipse(a, b, c, d, fill, stroke, ellipseMode); - inGeo.initTessMaps(); + inGeo.initTessMap(tessGeo); tessellator.tessellateTriangleFan(); } @@ -2839,7 +2657,7 @@ public class PShape3D extends PShape { ambientColor, specularColor, emissiveColor, shininess); inGeo.setNormal(normalX, normalY, normalZ); inGeo.addArc(a, b, c, d, start, stop, fill, stroke, ellipseMode); - inGeo.initTessMaps(); + inGeo.initTessMap(tessGeo); tessellator.tessellateTriangleFan(); } @@ -2857,7 +2675,7 @@ public class PShape3D extends PShape { inGeo.setColors(fillColor, strokeColor, strokeWeight, ambientColor, specularColor, emissiveColor, shininess); inGeo.addBox(w, h, d, fill, stroke); - inGeo.initTessMaps(); + inGeo.initTessMap(tessGeo); tessellator.tessellateQuads(); } @@ -2874,7 +2692,7 @@ public class PShape3D extends PShape { inGeo.setColors(fillColor, strokeColor, strokeWeight, ambientColor, specularColor, emissiveColor, shininess); int[] indices = inGeo.addSphere(r, nu, nv, fill, stroke); - inGeo.initTessMaps(); + inGeo.initTessMap(tessGeo); tessellator.tessellateTriangles(indices); } @@ -2980,7 +2798,7 @@ public class PShape3D extends PShape { } if (stroke) inGeo.addPolygonEdges(isClosed); - inGeo.initTessMaps(); + inGeo.initTessMap(tessGeo); tessellator.tessellatePolygon(false, isClosed, true); } @@ -3368,9 +3186,9 @@ public class PShape3D extends PShape { protected void modeCheck() { if (root.mode == STATIC && root.prevMode == DYNAMIC) { - root.freeCaches(); - root.freeTessData(); - root.freeTessMaps(); + root.disposeCaches(); + root.disposeTessData(); + root.disposeTessMaps(); } root.prevMode = root.mode; } @@ -4071,7 +3889,7 @@ public class PShape3D extends PShape { } - protected void freeCaches() { + protected void disposeCaches() { fillVerticesCache = null; fillColorsCache = null; fillNormalsCache = null; @@ -4091,7 +3909,7 @@ public class PShape3D extends PShape { } - protected void freeTessData() { + protected void disposeTessData() { // The dispose() call will destroy all the geometry // arrays but will keep the index caches that are // needed by the render methods. @@ -4100,21 +3918,21 @@ public class PShape3D extends PShape { if (family == GROUP) { for (int i = 0; i < childCount; i++) { PShape3D child = (PShape3D)children[i]; - child.freeTessData(); + child.disposeTessData(); } } } - protected void freeTessMaps() { + protected void disposeTessMaps() { if (family == GROUP) { for (int i = 0; i < childCount; i++) { PShape3D child = (PShape3D)children[i]; - child.freeTessMaps(); + child.disposeTessMaps(); } } else { if (inGeo != null) { - inGeo.freeTessMaps(); + inGeo.disposeTessMap(); } } }