diff --git a/GLMakie/assets/shader/line_segment.geom b/GLMakie/assets/shader/line_segment.geom index 7fa912ac193..1c1083159b7 100644 --- a/GLMakie/assets/shader/line_segment.geom +++ b/GLMakie/assets/shader/line_segment.geom @@ -89,7 +89,6 @@ void main(void) // get start and end point of line segment // restrict to visible area (see lines.geom) vec3 p1, p2; - vec2 clip_factor = vec2(0); { vec4 _p1 = gl_in[0].gl_Position, _p2 = gl_in[1].gl_Position; @@ -106,12 +105,10 @@ void main(void) vec4 v1 = _p2 - _p1; if (_p1.w < 0.0) { - clip_factor.x = (-_p1.w - _p1.z) / (v1.z + v1.w); - _p1 = _p1 + clip_factor.x * v1; + _p1 = _p1 + (-_p1.w - _p1.z) / (v1.z + v1.w) * v1; } if (_p2.w < 0.0) { - clip_factor.y = (-_p2.w - _p2.z) / (v1.z + v1.w); - _p2 = _p2 + clip_factor.y * v1; + _p2 = _p2 + (-_p2.w - _p2.z) / (v1.z + v1.w) * v1; } // clip -> pixel/screen projection diff --git a/GLMakie/assets/shader/lines.geom b/GLMakie/assets/shader/lines.geom index e2068bc1fa0..1e834f1a32c 100644 --- a/GLMakie/assets/shader/lines.geom +++ b/GLMakie/assets/shader/lines.geom @@ -49,6 +49,10 @@ uniform int linecap; uniform int joinstyle; uniform float miter_limit; +uniform mat4 view, projection, projectionview; +uniform int _num_clip_planes; +uniform vec4 clip_planes[8]; + // Constants const float AA_RADIUS = 0.8; const float AA_THICKNESS = 4.0 * AA_RADIUS; @@ -90,6 +94,35 @@ vec2 normal_vector(in vec2 v) { return vec2(-v.y, v.x); } vec2 normal_vector(in vec3 v) { return vec2(-v.y, v.x); } float sign_no_zero(float value) { return value >= 0.0 ? 1.0 : -1.0; } +bool process_clip_planes(inout vec4 p1, inout vec4 p2, inout bool[4] isvalid) +{ + float d1, d2; + for(int i = 0; i < _num_clip_planes; i++) + { + // distance from clip planes with negative clipped + d1 = dot(p1.xyz, clip_planes[i].xyz) - clip_planes[i].w; + d2 = dot(p2.xyz, clip_planes[i].xyz) - clip_planes[i].w; + + // both outside - clip everything + if (d1 < 0.0 && d2 < 0.0) { + p2 = p1; + isvalid[1] = false; + isvalid[2] = false; + return true; + // one outside - shorten segment + } else if (d1 < 0.0) { + // solve 0 = m * t + b = (d2 - d1) * t + d1 with t in (0, 1) + p1 = p1 - d1 * (p2 - p1) / (d2 - d1); + isvalid[0] = false; + } else if (d2 < 0.0) { + p2 = p2 - d2 * (p1 - p2) / (d1 - d2); + isvalid[3] = false; + } + } + + return false; +} + //////////////////////////////////////////////////////////////////////////////// // Linestyle Support // @@ -231,12 +264,22 @@ void main(void) // moving them to the edge of the visible area. vec3 p0, p1, p2, p3; { - // All in clip space + // Not in clip space yet vec4 clip_p0 = gl_in[0].gl_Position; // start of previous segment vec4 clip_p1 = gl_in[1].gl_Position; // end of previous segment, start of current segment vec4 clip_p2 = gl_in[2].gl_Position; // end of current segment, start of next segment vec4 clip_p3 = gl_in[3].gl_Position; // end of next segment + // Shorten segments to fit clip planes + // returns true if segments are fully clipped + if (process_clip_planes(clip_p1, clip_p2, isvalid)) + return; + + clip_p0 = projectionview * clip_p0; + clip_p1 = projectionview * clip_p1; + clip_p2 = projectionview * clip_p2; + clip_p3 = projectionview * clip_p3; + vec4 v1 = clip_p2 - clip_p1; // With our perspective projection matrix clip.w = -view.z with diff --git a/GLMakie/assets/shader/lines.vert b/GLMakie/assets/shader/lines.vert index 052c47f0380..520aed0f0ab 100644 --- a/GLMakie/assets/shader/lines.vert +++ b/GLMakie/assets/shader/lines.vert @@ -14,7 +14,7 @@ in float lastlen; {{thickness_type}} thickness; {{color_type}} color; -uniform mat4 projectionview, model; +uniform mat4 inverse_projectionview, model; uniform uint objectid; uniform int total_length; uniform float px_per_unit; @@ -34,22 +34,6 @@ int get_valid_vertex(Nothing se){return 1;} uniform float depth_shift; -uniform int num_clip_planes; -uniform vec4 clip_planes[8]; -out float g_clip_distance[8]; - -void process_clip_planes(vec3 world_pos) -{ - // distance = dot(world_pos - plane.point, plane.normal) - // precalculated: dot(plane.point, plane.normal) -> plane.w - for (int i = 0; i < num_clip_planes; i++) - g_clip_distance[i] = dot(world_pos, clip_planes[i].xyz) - clip_planes[i].w; - - // TODO: can be skipped? - for (int i = num_clip_planes; i < 8; i++) - g_clip_distance[i] = 1.0; -} - void main() { g_lastlen = lastlen; @@ -59,12 +43,10 @@ void main() g_thickness = px_per_unit * thickness; g_color = color; - vec4 world_pos = model * to_vec4(vertex); - process_clip_planes(world_pos.xyz); #ifdef FAST_PATH - gl_Position = projectionview * world_pos; + gl_Position = model * to_vec4(vertex); #else - gl_Position = to_vec4(vertex); + gl_Position = inverse_projectionview * to_vec4(vertex); #endif gl_Position.z += gl_Position.w * depth_shift; } diff --git a/GLMakie/src/drawing_primitives.jl b/GLMakie/src/drawing_primitives.jl index 106a07138d0..7062ddbca00 100644 --- a/GLMakie/src/drawing_primitives.jl +++ b/GLMakie/src/drawing_primitives.jl @@ -474,6 +474,11 @@ function draw_atomic(screen::Screen, scene::Scene, @nospecialize(plot::Lines)) Vec2f(ppu * origin(viewport)) end + # Handled manually without using OpenGL clipping + data[:_num_clip_planes] = pop!(data, :num_clip_planes) + data[:num_clip_planes] = Observable(0) + data[:inverse_projectionview] = map(inv, data[:projectionview]) + space = plot.space if isnothing(to_value(linestyle)) data[:pattern] = nothing diff --git a/src/utilities/Plane.jl b/src/utilities/Plane.jl index 5f8d8508957..968c704da07 100644 --- a/src/utilities/Plane.jl +++ b/src/utilities/Plane.jl @@ -6,7 +6,9 @@ struct Plane{N, T} distance::T end -Plane(point::Point{N}, normal::Vec{N}) where N = Plane(normal, dot(point, normal)) +function Plane(point::Point{N}, normal::Vec{N}) where N + Plane(normal, dot(point, normal)) +end const Plane2{T} = Plane{2, T} const Plane3{T} = Plane{3, T}