summaryrefslogtreecommitdiff
path: root/src/location/quickmapitems/shaders/polyline_extruded.vert
blob: 27b69b60f153808e25bc881d932f70a32e3fdae2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#version 440

layout(location = 0) in vec4 vertex;
layout(location = 1) in vec4 previous;
layout(location = 2) in vec4 next;
layout(location = 3) in float direction;
layout(location = 4) in float triangletype;
layout(location = 5) in float vertextype;  // -1.0 if it is the "left" end of the segment, 1.0 if it is the "right" end.
layout(location = 0) out vec4 primitivecolor;

layout(std140, binding = 0) uniform buf {
    mat4 qt_Matrix;
    mat4 mapProjection;
    vec4 center;
    vec4 center_lowpart;
    float lineWidth;
    float aspect;
    int miter; // currently unused
    vec4 color;
    float wrapOffset;
};


vec4 wrapped(in vec4 v) { return vec4(v.x + wrapOffset, v.y, 0.0, 1.0); }
void main() { // ln 22
  primitivecolor = color;
  vec2 aspectVec = vec2(aspect, 1.0);
  mat4 projViewModel = qt_Matrix * mapProjection;
  vec4 cur = wrapped(vertex) - center;
  cur = cur - center_lowpart;
  vec4 prev = wrapped(previous) - center;
  prev = prev - center_lowpart;
  vec4 nex = wrapped(next) - center;
  nex = nex - center_lowpart;

  vec4 centerProjected = projViewModel * (center + vec4(0.0, 0.0, 0.0, 1.0));
  vec4 previousProjected = projViewModel * prev;
  vec4 currentProjected = projViewModel * cur;
  vec4 nextProjected = projViewModel * nex;

  //get 2D screen space with W divide and aspect correction
  vec2 currentScreen = (currentProjected.xy / currentProjected.w) * aspectVec;
  vec2 previousScreen = (previousProjected.xy / previousProjected.w) * aspectVec;
  vec2 nextScreen = (nextProjected.xy / nextProjected.w) * aspectVec;
  float len = (lineWidth);
  float orientation = direction;
  bool clipped = false;
  bool otherEndBelowFrustum = false;
  //starting point uses (next - current)
  vec2 dir = vec2(0.0);
  if (vertextype < 0.0) {
    dir = normalize(nextScreen - currentScreen);
    if (nextProjected.z < 0.0) dir = -dir;
  } else {
    dir = normalize(currentScreen - previousScreen);
    if (previousProjected.z < 0.0) dir = -dir;
  }
// first, clip current, and make sure currentProjected.z is > 0
  if (currentProjected.z < 0.0) {
    if ((nextProjected.z > 0.0 && vertextype < 0.0) || (vertextype > 0.0 && previousProjected.z > 0.0)) {
      dir = -dir;
      clipped = true;
      if (vertextype < 0.0 && nextProjected.y / nextProjected.w < -1.0) otherEndBelowFrustum = true;
      else if (vertextype > 0.0 && previousProjected.y / previousProjected.w < -1.0) otherEndBelowFrustum = true;
    } else {
        primitivecolor = vec4(0.0,0.0,0.0,0.0);
        gl_Position = vec4(-10000000.0, -1000000000.0, -1000000000.0, 1); // get the vertex out of the way if the segment is fully invisible
        return;
    }
  } else if (triangletype < 2.0) { // vertex in the view, try to miter
    //get directions from (C - B) and (B - A)
    vec2 dirA = normalize((currentScreen - previousScreen));
    if (previousProjected.z < 0.0) dirA = -dirA;
    vec2 dirB = normalize((nextScreen - currentScreen));
    //now compute the miter join normal and length
    if (nextProjected.z < 0.0) dirB = -dirB;
    vec2 tangent = normalize(dirA + dirB);
    vec2 perp = vec2(-dirA.y, dirA.x);
    vec2 vmiter = vec2(-tangent.y, tangent.x);
    len = lineWidth / dot(vmiter, perp);
// The following is an attempt to have a segment-length based miter threshold.
// A mediocre workaround until better mitering will be added.
    float lenTreshold = clamp( min(length((currentProjected.xy - previousProjected.xy) / aspectVec),
                               length((nextProjected.xy - currentProjected.xy) / aspectVec)), 3.0, 6.0 ) * 0.5;
    if (len < lineWidth * lenTreshold && len > -lineWidth * lenTreshold) {
       dir = tangent;
    } else {
       len = lineWidth;
    }
  }
  vec4 offset;
  if (!clipped) {
    vec2 normal = normalize(vec2(-dir.y, dir.x));
    normal *= len; // fracZL apparently was needed before the (-2.0 / qt_Matrix[1][1]) factor was introduced
    normal /= aspectVec;  // straighten the normal up again
    float scaleFactor =  currentProjected.w / centerProjected.w;
    offset = vec4(normal * orientation * scaleFactor * (centerProjected.w / (-2.0 / qt_Matrix[1][1])), 0.0, 0.0); // ToDo: figure out why (-2.0 / qt_Matrix[1][1]), that is empirically what works
    gl_Position = currentProjected + offset;
  } else {
     if (otherEndBelowFrustum) offset = vec4((dir * 1.0) / aspectVec, 0.0, 0.0);  // the if is necessary otherwise it seems the direction vector still flips in some obscure cases.
     else offset = vec4((dir * 500000000000.0) / aspectVec, 0.0, 0.0); // Hack alert: just 1 triangle, long enough to look like a rectangle.
     if (vertextype < 0.0) gl_Position = nextProjected - offset; else gl_Position = previousProjected + offset;
  }
}