From 408e32c11b53bc1fbdd2125b90827c525d699a39 Mon Sep 17 00:00:00 2001 From: codeanticode Date: Wed, 30 May 2012 02:01:49 +0000 Subject: [PATCH] Implemented bevel joins in 3D lines, added displacement trick in line vertex shader to minimize polygon fighting. --- .../src/processing/opengl/LineShaderVert.glsl | 57 ++++--- .../processing/opengl/PGraphicsOpenGL.java | 140 ++++++++++++++---- 2 files changed, 147 insertions(+), 50 deletions(-) diff --git a/java/libraries/opengl/src/processing/opengl/LineShaderVert.glsl b/java/libraries/opengl/src/processing/opengl/LineShaderVert.glsl index eb366849a..ca7d07293 100644 --- a/java/libraries/opengl/src/processing/opengl/LineShaderVert.glsl +++ b/java/libraries/opengl/src/processing/opengl/LineShaderVert.glsl @@ -43,34 +43,43 @@ vec4 windowToClipVector(vec2 window, vec4 viewport, float clip_w) { void main() { vec4 pos_p = inVertex; - vec4 pos_q = vec4(inLine.xyz, 1); - - vec4 v_p = modelviewMatrix * pos_p; + vec4 v_p = modelviewMatrix * pos_p; + // Moving vertices slightly toward the camera + // to avoid depth-fighting with the fill triangles. + // Discussed here: + // http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=252848 + v_p.xyz = v_p.xyz * 0.99; vec4 clip_p = projectionMatrix * v_p; - - vec4 v_q = modelviewMatrix * pos_q; - vec4 clip_q = projectionMatrix * v_q; - - vec3 window_p = clipToWindow(clip_p, viewport); - vec3 window_q = clipToWindow(clip_q, viewport); - vec3 tangent = window_q - window_p; - - float segment_length = length(tangent.xy); - vec2 perp = normalize(vec2(-tangent.y, tangent.x)); float thickness = inLine.w; - vec2 window_offset = perp * thickness; + + if (thickness != 0.0) { + vec4 pos_q = vec4(inLine.xyz, 1); + vec4 v_q = modelviewMatrix * pos_q; + v_q.xyz = v_q.xyz * 0.99; + vec4 clip_q = projectionMatrix * v_q; + + vec3 window_p = clipToWindow(clip_p, viewport); + vec3 window_q = clipToWindow(clip_q, viewport); + vec3 tangent = window_q - window_p; + + float segment_length = length(tangent.xy); + vec2 perp = normalize(vec2(-tangent.y, tangent.x)); + vec2 window_offset = perp * thickness; - if (0 < perspective) { - // Perspective correction (lines will look thiner as they move away - // from the view position). - gl_Position.xy = clip_p.xy + window_offset.xy; - gl_Position.zw = clip_p.zw; + if (0 < perspective) { + // Perspective correction (lines will look thiner as they move away + // from the view position). + gl_Position.xy = clip_p.xy + window_offset.xy; + gl_Position.zw = clip_p.zw; + } else { + // No perspective correction. + float clip_p_w = clip_p.w; + vec4 offset_p = windowToClipVector(window_offset, viewport, clip_p_w); + gl_Position = clip_p + offset_p; + } } else { - // No perspective correction. - float clip_p_w = clip_p.w; - vec4 offset_p = windowToClipVector(window_offset, viewport, clip_p_w); - gl_Position = clip_p + offset_p; - } + gl_Position = clip_p; + } vertColor = inColor; } \ No newline at end of file diff --git a/java/libraries/opengl/src/processing/opengl/PGraphicsOpenGL.java b/java/libraries/opengl/src/processing/opengl/PGraphicsOpenGL.java index 41376a80e..6aa50217b 100644 --- a/java/libraries/opengl/src/processing/opengl/PGraphicsOpenGL.java +++ b/java/libraries/opengl/src/processing/opengl/PGraphicsOpenGL.java @@ -2542,6 +2542,8 @@ public class PGraphicsOpenGL extends PGraphics { int icount = cache.indexCount[n]; int voffset = cache.vertexOffset[n]; + // TODO: fix this, as it doesn't consider the vertices now taken up + // by the bevel joins. 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 @@ -6567,12 +6569,32 @@ public class PGraphicsOpenGL extends PGraphics { return vertices[3 * (vertexCount - 1) + 2]; } - int getNumEdgeVertices() { - 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 getNumEdgeIndices() { - 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) { @@ -8490,6 +8512,38 @@ public class PGraphicsOpenGL extends PGraphics { // // Add line geometry + void setLineVertex(int tessIdx, InGeometry in, int inIdx0, int rgba) { + int index; + + index = 3 * inIdx0; + float x0 = in.vertices[index++]; + float y0 = in.vertices[index++]; + float z0 = in.vertices[index ]; + + if (renderMode == IMMEDIATE && flushMode == FLUSH_WHEN_FULL && !hints[DISABLE_TRANSFORM_CACHE]) { + PMatrix3D mm = modelview; + + 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; @@ -9328,7 +9382,7 @@ public class PGraphicsOpenGL extends PGraphics { for (int ln = 0; ln < lineCount; ln++) { int i0 = first + 2 * ln + 0; int i1 = first + 2 * ln + 1; - index = addLine3D(i0, i1, index, false); + index = addLine3D(i0, i1, index, null, false); } lastLineIndexCache = index; } @@ -9376,18 +9430,19 @@ public class PGraphicsOpenGL extends PGraphics { } } - void tessellateLineStrip3D(int lineCount) { - int nvert = lineCount * 4; - int nind = lineCount * 2 * 3; - + 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, false); + index = addLine3D(i0, i1, index, lastInd, false); i0 = i1; } lastLineIndexCache = index; @@ -9437,20 +9492,23 @@ public class PGraphicsOpenGL extends PGraphics { } void tessellateLineLoop3D(int lineCount) { - int nvert = lineCount * 4; - int nind = lineCount * 2 * 3; + // FIXME: This calculation doesn't add the bevel join between + // the first and last vertex. + 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, false); + index = addLine3D(i0, i1, index, lastInd, false); i0 = i1; } - index = addLine3D(in.lastVertex, in.firstVertex, index, false); + index = addLine3D(in.lastVertex, in.firstVertex, index, lastInd, false); lastLineIndexCache = index; } @@ -9498,24 +9556,31 @@ public class PGraphicsOpenGL extends PGraphics { } void tessellateEdges3D() { - int nInVert = in.getNumEdgeVertices(); - int nInInd = in.getNumEdgeIndices(); + 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]; - index = addLine3D(edge[0], edge[1], index, true); + 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(); + int nInVert = in.getNumEdgeVertices(false); if (noCapsJoins(nInVert)) { - int nInInd = in.getNumEdgeIndices(); + int nInInd = in.getNumEdgeIndices(false); tess.polyVertexCheck(nInVert); tess.polyIndexCheck(nInInd); @@ -9557,25 +9622,27 @@ public class PGraphicsOpenGL extends PGraphics { tessellateLinePath(path); } } - + // Adding the data that defines a quad starting at vertex i0 and // ending at i1. - int addLine3D(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]; - if (PGL.MAX_VERTEX_INDEX1 <= count + 4) { + 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; + int color, color0; float weight; - color = constStroke ? strokeColor : in.strokeColors[i0]; + color0 = color = constStroke ? strokeColor : in.strokeColors[i0]; weight = constStroke ? strokeWeight : in.strokeWeights[i0]; + // TODO: vidx++ could be directly in the setLineVertex() call. tess.setLineVertex(vidx, in, i0, i1, color, +weight/2); tess.lineIndices[iidx++] = (short) (count + 0); @@ -9599,9 +9666,30 @@ public class PGraphicsOpenGL extends PGraphics { tess.lineIndices[iidx++] = (short) (count + 3); cache.incCounts(index, 6, 4); - return index; - } + + if (lastInd != null) { + if (-1 < lastInd[0] && -1 < lastInd[1]) { + // Adding bevel triangles + tess.setLineVertex(vidx + 1, 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).