data/shaders/connector.gs (339 lines of code) (raw):

#version 120 #extension GL_EXT_geometry_shader4 : require #extension GL_EXT_gpu_shader4 : require varying out vec3 NORMAL ; varying out vec4 COLOR ; varying out float fog; varying out vec2 bgTextureLookup; varying out float lineEdge; varying out float aaCutoff; varying in vec4 a_center[]; varying in vec4 a_target[]; varying in vec4 COLORIn[]; varying in vec2 a_textSizeIn[]; varying in vec3 a_screenWorldOffsetIn[]; varying in vec2 a_indentFactorIn[]; varying in float a_relative_modeIn[]; varying in float a_draw_flagsIn[]; varying in vec4 a_bkgrd_colorIn[]; varying in float a_rel_ext_lengthIn[]; varying in float a_con_widthIn[]; uniform vec2 screenSize; uniform float screenOriginVertexScale; uniform float antialiasedLines; uniform float textureToLabelSize; uniform float front; uniform float clipRange; float PI = 3.14159265358979323846264; float mysmoothstep(float edge0, float edge1, float x){ float rets = step(edge0, edge1) * step(edge1, edge0); return rets * step(edge0, x) + (1.-rets) * smoothstep(edge0, edge1, x); } #include connector.shared struct lineSeg_t { vec2 p1; vec2 p2; }; bool lineSegIntersection(vec2 p1, vec2 p2, vec2 p3, vec2 p4, out vec2 po) { float distAB, theCos, theSin, newX, abpos; vec2 p2i, p3i, p4i; if ((p1.x==p2.x && p1.y==p2.y) || (p3.x==p4.x && p3.y==p4.y)) return false; if ((p1.x==p3.x && p1.y==p3.y) || (p2.x==p3.x && p2.y==p3.y) || (p1.x==p4.x && p1.y==p4.y) || (p2.x==p4.x && p2.y==p4.y)) { return false; } p2i.x = p2.x; p2i.y = p2.y; p3i.x = p3.x; p3i.y = p3.y; p4i.x = p4.x; p4i.y = p4.y; p2i.x -= p1.x; p2i.y -= p1.y; p3i.x -= p1.x; p3i.y -= p1.y; p4i.x -= p1.x; p4i.y -= p1.y; distAB = sqrt(p2i.x * p2i.x + p2i.y * p2i.y); theCos = p2i.x / distAB; theSin = p2i.y / distAB; newX = p3i.x * theCos + p3i.y * theSin; p3i.y = p3i.y * theCos - p3i.x * theSin; p3i.x = newX; newX = p4i.x * theCos + p4i.y * theSin; p4i.y = p4i.y * theCos - p4i.x * theSin; p4i.x = newX; if ((p3i.y < 0. && p4i.y<0.) || (p3i.y >= 0. && p4i.y >= 0.)) return false; abpos = p4i.x + (p3i.x - p4i.x) * p4i.y / (p4i.y - p3i.y); if (abpos<0. || abpos>distAB) return false; po.x = p1.x + abpos * theCos; po.y = p1.y + abpos * theSin; return true; } void setBG(){ bgTextureLookup = (gl_Position.xy/gl_Position.w) / 2.0 + 0.5; } void drawLineAsGeometryClipped(vec4 pt1, vec4 pt2, lineSeg_t labelTop, lineSeg_t labelBottom, lineSeg_t labelLeft, lineSeg_t labelRight, out vec2 dirv, float lineWidth, float aa){ vec4 pt1E = pt1; vec4 pt2E = pt2; pt1E.xy = floor(pt1.xy * screenSize) / screenSize; pt2E.xy = floor(pt2.xy * screenSize) / screenSize; vec3 diffV = pt1E.xyz - pt2E.xyz; vec2 dirvPnorm = vec2(normalize(cross(diffV, vec3(0.,0.,1.))).xy); vec2 dirvP = lineWidth*dirvPnorm; dirv = dirvP/screenSize; int l1V = 0 , l2V = 0; lineSeg_t l1, l2; vec2 isec1, isec2; l1.p1 = pt1.xy + dirv; l1.p2 = pt2.xy + dirv; l2.p1 = pt1.xy - dirv; l2.p2 = pt2.xy - dirv; if (lineSegIntersection(labelTop.p1, labelTop.p2, l1.p1, l1.p2, isec1)) { l1V = 1; } else if (lineSegIntersection(labelRight.p1, labelRight.p2, l1.p1, l1.p2, isec1)) { l1V = 2; } else if (lineSegIntersection(labelBottom.p1, labelBottom.p2, l1.p1, l1.p2, isec1)) { l1V = 3; } else if (lineSegIntersection(labelLeft.p1, labelLeft.p2, l1.p1, l1.p2, isec1)) { l1V = 4; } if (lineSegIntersection(labelTop.p1, labelTop.p2, l2.p1, l2.p2, isec2)) { l2V = 1; } else if (lineSegIntersection(labelRight.p1, labelRight.p2, l2.p1, l2.p2, isec2)) { l2V = 2; } else if (lineSegIntersection(labelBottom.p1, labelBottom.p2, l2.p1, l2.p2, isec2)) { l2V = 3; } else if (lineSegIntersection(labelLeft.p1, labelLeft.p2, l2.p1, l2.p2, isec2)) { l2V = 4; } // Both lines run through one side if ( (l1V > 0 && l2V > 0) && (l1V == l2V) ) { pt1E.xy = pt1.xy + dirv; pt1E.z = pt1.z; gl_Position = pt1E; lineEdge = 1.; aaCutoff = aa; setBG(); EmitVertex(); pt1E.xy = pt1.xy - dirv; pt1E.z = pt1.z; gl_Position = pt1E; lineEdge = -1.; setBG(); EmitVertex(); pt2E.xy = isec1; pt2E.z = pt2.z; gl_Position = pt2E; lineEdge = 1.; setBG(); EmitVertex(); pt2E.xy = isec2; pt2E.z = pt2.z; gl_Position = pt2E; lineEdge = -1.; setBG(); EmitVertex(); EndPrimitive(); lineEdge = 0.; aaCutoff = 0.; } else if ( (l1V > 0 && l2V > 0) && (l1V != l2V) ) { // Find the corner vec2 isecc; if ( (l1V == 1 && l2V == 2) || (l1V == 2 && l2V == 1) ) { // UR isecc = labelTop.p1; } else if ( (l1V == 2 && l2V == 3) || (l1V == 3 && l2V == 2) ) { // LR isecc = labelBottom.p2; } else if ( (l1V == 3 && l2V == 4) || (l1V == 4 && l2V == 3) ) { // LL isecc = labelBottom.p1; } else if ( (l1V == 4 && l2V == 1) || (l1V == 1 && l2V == 4) ) { // UL isecc = labelTop.p2; } pt2E.xy = isec1.xy; pt2E.z = pt2.z; gl_Position = pt2E; lineEdge = 1.; aaCutoff = aa; setBG(); EmitVertex(); pt1E.xy = pt1.xy + dirv; pt1E.z = pt1.z; gl_Position = pt1E; lineEdge = 1.; setBG(); EmitVertex(); pt2E.xy = isecc.xy; pt2E.z = pt2.z; gl_Position = pt2E; /* since we are adding a corner vertex, we also need to account for anti-aliasing by setting lineEdge for that vertex. */ float len1 = length(screenSize* (isecc.xy - isec1.xy)); // length between corner and first intersection float len2 = length(screenSize* (isecc.xy - isec2.xy)); // length between corner and second intersection float lenc = length(screenSize* (isec2.xy - isec1.xy)); // length between intersections, i.e., line width float ang = atan(len2 / len1); // angle between perpendicular vector and first edge float n2 = sin(ang) * len2; // second edge projected onto perpendicular vector float lenr = (n2 / lenc) * 2. - 1.; // figure out ratio and normalize it between -1 and 1 float lenrsign = 2. * (step(0., lenr) - .5); // sign for lenr used below lineEdge = lenrsign * (1. - mysmoothstep(0., aa, 1 - abs(lenr))); // take into account aa for corner vertex lineEdge setBG(); EmitVertex(); pt1E.xy = pt1.xy - dirv; pt1E.z = pt1.z; gl_Position = pt1E; lineEdge = -1.; setBG(); EmitVertex(); pt2E.xy = isec2.xy; pt2E.z = pt2.z; gl_Position = pt2E; lineEdge = -1.; setBG(); EmitVertex(); EndPrimitive(); } } void drawLineAsGeometry(vec4 pt1, vec4 pt2, out vec2 dirv, float lineWidth, float aa){ vec4 pt1E = pt1; vec4 pt2E = pt2; pt1E.xy = floor(pt1.xy * screenSize) / screenSize; pt2E.xy = floor(pt2.xy * screenSize) / screenSize; vec3 diffV = pt1E.xyz - pt2E.xyz; vec2 dirvPnorm = vec2(normalize(cross(diffV, vec3(0.,0.,1.))).xy); vec2 dirvP = lineWidth*dirvPnorm; dirv = dirvP/screenSize; pt1E.xy = pt1.xy + dirv; gl_Position = pt1E; lineEdge = -1.; aaCutoff = aa; setBG(); EmitVertex(); pt2E.xy = pt2.xy + dirv; gl_Position = pt2E; lineEdge = -1.; setBG(); EmitVertex(); pt1E.xy = pt1.xy - dirv; gl_Position = pt1E; lineEdge = 1.; setBG(); EmitVertex(); pt2E.xy = pt2.xy - dirv; gl_Position = pt2E; lineEdge = 1.; setBG(); EmitVertex(); EndPrimitive(); lineEdge = 0.; aaCutoff = 0.; } void drawLineAsGeometryWithOffsets(vec4 pt1, vec4 pt2, float topext, float bottomext){ vec4 pt1E = pt1; vec4 pt2E = pt2; vec3 diffV = pt1E.xyz - pt2E.xyz; vec2 linev = vec2(a_con_widthIn[0] * normalize(diffV).xy) / screenSize; vec2 dirv = vec2((a_con_widthIn[0]*normalize(cross(diffV, vec3(0.,0.,1.)).xy)))/screenSize; pt1E.xy = pt1.xy + dirv + topext * linev; gl_Position = pt1E; setBG(); EmitVertex(); pt2E.xy = pt2.xy + dirv - topext * linev; gl_Position = pt2E; setBG(); EmitVertex(); pt1E.xy = pt1.xy - dirv + bottomext * linev; gl_Position = pt1E; setBG(); EmitVertex(); pt2E.xy = pt2.xy - dirv - bottomext * linev; gl_Position = pt2E; setBG(); EmitVertex(); EndPrimitive(); } void main() { vec4 transformedCenter, transformedTarget; vec2 textSizeInScreen, offset, drawVector; float doNotDraw; float connector_mode_0, connector_mode_1, connector_mode_2, connector_mode_3, connector_mode_4, connector_mode_2_4, drawBackgroundOutline, drawBackground, drawConnector, isProjected, zValue, zTarget; lineEdge = 0.; aaCutoff = 0.; getDrawFlags(a_draw_flagsIn[0], connector_mode_0, connector_mode_1, connector_mode_2, connector_mode_3, connector_mode_4, drawBackgroundOutline, drawBackground, drawConnector); float negLabelSize = step(textureToLabelSize, 0.); float textureToLabelSizeA = abs(textureToLabelSize); float extLength = (1.-negLabelSize) * a_rel_ext_lengthIn[0] + negLabelSize * (-a_rel_ext_lengthIn[0]/(screenOriginVertexScale*2.) ); vec2 textSize = a_textSizeIn[0] / textureToLabelSizeA; connector_mode_2_4 = connector_mode_2 + connector_mode_4; calculatePreConnectorInfo(a_center[0], a_target[0], textSize, a_indentFactorIn[0], a_screenWorldOffsetIn[0], transformedCenter, transformedTarget, textSizeInScreen, offset, drawVector, doNotDraw, a_relative_modeIn[0], isProjected, zValue, a_con_widthIn[0], zTarget); vec4 endpointOnBBX, endpointExtendedOffBBX; calculateConnectorInfo(a_center[0], textSizeInScreen, textSize, drawVector, offset, fog, transformedCenter, transformedTarget, endpointOnBBX, endpointExtendedOffBBX, connector_mode_0, connector_mode_1, connector_mode_2, connector_mode_3, connector_mode_4, extLength, isProjected, zValue); NORMAL = vec3(0.,0.,1.); // if the zValue is not within the clipping planes, then set the transformedTarget z // to the zValue as well, to make sure that the connector doesn't get drawn float withinView = step(zValue, 1.) * step(-1., zValue); float ttz = (1.-zTarget) * (1.-isProjected); // not zTarget and not projected, then keep same, otherwise zValue transformedTarget.z = ttz * transformedTarget.z + (1.-ttz) * zValue; vec4 transformedPosition3 = transformedCenter; transformedPosition3.xy += offset; transformedPosition3.z = zValue; // bbx of the label vec4 upperLeft = transformedPosition3; upperLeft.xy += textSizeInScreen * vec2(-1.,1.); vec4 upperRight = transformedPosition3; upperRight.xy += textSizeInScreen * vec2(1.,1.); vec4 lowerLeft = transformedPosition3; lowerLeft.xy += textSizeInScreen * vec2(-1.,-1.); vec4 lowerRight = transformedPosition3; lowerRight.xy += textSizeInScreen * vec2(1.,-1.); lineSeg_t labelTop, labelBottom, labelLeft, labelRight; labelTop.p1 = upperRight.xy; labelTop.p2 = upperLeft.xy; labelBottom.p1 = lowerLeft.xy; labelBottom.p2 = lowerRight.xy; labelRight.p1 = upperRight.xy; labelRight.p2 = lowerRight.xy; labelLeft.p1 = upperLeft.xy; labelLeft.p2 = lowerLeft.xy; if (drawBackgroundOutline + drawBackground > 0.){ if (drawBackground > 0.){ if (drawBackgroundOutline > 0.){ vec2 lineWidth = a_con_widthIn[0] / (screenSize); vec4 upperLeftM = upperLeft + vec4(lineWidth.x, -lineWidth.y, 0., 0.), upperRightM = upperRight + vec4(-lineWidth.x, -lineWidth.y, 0., 0.), lowerLeftM = lowerLeft + vec4(lineWidth.x, lineWidth.y, 0., 0.), lowerRightM = lowerRight + vec4(-lineWidth.x, lineWidth.y, 0., 0.); COLOR = a_bkgrd_colorIn[0]; gl_Position = upperLeftM; setBG(); EmitVertex(); gl_Position = upperRightM; setBG(); EmitVertex(); gl_Position = lowerLeftM; setBG(); EmitVertex(); gl_Position = lowerRightM; setBG(); EmitVertex(); EndPrimitive(); } else { COLOR = a_bkgrd_colorIn[0]; gl_Position = upperLeft; setBG(); EmitVertex(); gl_Position = upperRight; setBG(); EmitVertex(); gl_Position = lowerLeft; setBG(); EmitVertex(); gl_Position = lowerRight; setBG(); EmitVertex(); EndPrimitive(); } } if (drawBackgroundOutline > 0.){ COLOR = COLORIn[0]; drawLineAsGeometryWithOffsets(upperLeft, upperRight, 1., 0.); drawLineAsGeometryWithOffsets(lowerRight, upperRight, 0., 1.); drawLineAsGeometryWithOffsets(lowerLeft, lowerRight, 0., 1.); drawLineAsGeometryWithOffsets(lowerLeft, upperLeft, 1., 0.); } } if (drawConnector > 0 && doNotDraw == 0.){ COLOR = COLORIn[0]; float aa = min(1., 2./a_con_widthIn[0]); float widthAdjustment = aa * a_con_widthIn[0]; float adjLineWidth = antialiasedLines * widthAdjustment + a_con_widthIn[0]; if (connector_mode_2_4 > 0.){ // create 2 lines, between transformedTarget -> endpointExtendedOffBBX, and endpointExtendedOffBBX -> endpointOnBBX vec2 dirv1, dirv2; drawLineAsGeometry(endpointExtendedOffBBX, endpointOnBBX, dirv2, a_con_widthIn[0], 0); drawLineAsGeometry(transformedTarget, endpointExtendedOffBBX, dirv1, adjLineWidth, aa); float norder = step(0., cross(vec3(dirv1.x, dirv1.y, 0.), vec3(dirv2.x, dirv2.y, 0.)).z); // need to draw 'fan' that creates a curved elbow between the lines lineEdge = 0.; aaCutoff = 0.; gl_Position = endpointExtendedOffBBX; setBG(); EmitVertex(); lineEdge = -1.; aaCutoff = aa; if (norder < .5){ vec4 pt = endpointExtendedOffBBX; pt.xy = endpointExtendedOffBBX.xy + dirv1; gl_Position = pt; setBG(); EmitVertex(); pt.xy = endpointExtendedOffBBX.xy + dirv2; gl_Position = pt; aaCutoff = 0.; setBG(); EmitVertex(); } else { vec4 pt = endpointExtendedOffBBX; pt.xy = endpointExtendedOffBBX.xy - dirv2; gl_Position = pt; aaCutoff = 0.; setBG(); EmitVertex(); pt.xy = endpointExtendedOffBBX.xy - dirv1; gl_Position = pt; aaCutoff = aa; setBG(); EmitVertex(); } EndPrimitive(); lineEdge = 0.; aaCutoff = 0.; } else if (connector_mode_1 > 0. && drawBackground > 0.){ // create 1 lines, between transformedTarget -> endpointOnBBX // replace endpointOnBBX with midpoint of midpoints vec4 midULUR = (upperLeft + upperRight) / 2.0; vec4 midLLLR = (lowerLeft + lowerRight) / 2.0; vec4 midMid = (midULUR + midLLLR) / 2.0; vec2 dirv; drawLineAsGeometryClipped(transformedTarget, midMid, labelTop, labelBottom, labelLeft, labelRight, dirv, adjLineWidth, aa); } else { //connector_mode 0 or 3, or connector_mode 1 without the background vec2 dirv; drawLineAsGeometry(transformedTarget, endpointOnBBX, dirv, adjLineWidth, aa); } } }