Skip to content

Commit

Permalink
manually clip linesegments
Browse files Browse the repository at this point in the history
  • Loading branch information
ffreyer committed Jun 15, 2024
1 parent 4896bfb commit 078f3ed
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 35 deletions.
65 changes: 53 additions & 12 deletions GLMakie/assets/shader/line_segment.geom
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ uniform int linecap;
in {{stripped_color_type}} g_color[];
in uvec2 g_id[];
in float g_thickness[];
in float g_clip_distance[][8];

out vec3 f_quad_sdf;
out vec2 f_truncation;
out float f_linestart;
out float f_linelength;
out vec3 o_view_pos;
out vec3 o_view_normal;

flat out float f_linewidth;
flat out vec4 f_pattern_overwrite;
Expand All @@ -34,21 +35,47 @@ flat out float f_cumulative_length;
flat out ivec2 f_capmode;
flat out vec4 f_linepoints;
flat out vec4 f_miter_vecs;
out float gl_ClipDistance[8];

const float AA_RADIUS = 0.8;
const float AA_THICKNESS = 2.0 * AA_RADIUS;

uniform mat4 projectionview;
uniform int _num_clip_planes;
uniform vec4 clip_planes[8];

bool process_clip_planes(inout vec4 p1, inout vec4 p2)
{
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;
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);
else if (d2 < 0.0)
p2 = p2 - d2 * (p1 - p2) / (d1 - d2);
}

return false;
}


vec3 screen_space(vec4 vertex) {
return vec3((0.5 * vertex.xy / vertex.w + 0.5) * resolution, vertex.z / vertex.w);
}

vec2 normal_vector(in vec2 v) { return vec2(-v.y, v.x); }
vec2 normal_vector(in vec3 v) { return vec2(-v.y, v.x); }

out vec3 o_view_pos;
out vec3 o_view_normal;

void main(void)
{
o_view_pos = vec3(0);
Expand All @@ -62,15 +89,32 @@ 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;

// Shorten segments to fit clip planes
// returns true if segments are fully clipped
if (process_clip_planes(_p1, _p2))
return;

// remaining world -> clip projection
_p1 = projectionview * _p1;
_p2 = projectionview * _p2;

// Handle near/far clip planes
vec4 v1 = _p2 - _p1;

if (_p1.w < 0.0)
_p1 = _p1 + (-_p1.w - _p1.z) / (v1.z + v1.w) * v1;
if (_p2.w < 0.0)
_p2 = _p2 + (-_p2.w - _p2.z) / (v1.z + v1.w) * v1;
if (_p1.w < 0.0) {
clip_factor.x = (-_p1.w - _p1.z) / (v1.z + v1.w);
_p1 = _p1 + clip_factor.x * v1;
}
if (_p2.w < 0.0) {
clip_factor.y = (-_p2.w - _p2.z) / (v1.z + v1.w);
_p2 = _p2 + clip_factor.y * v1;
}

// clip -> pixel/screen projection
p1 = screen_space(_p1);
p2 = screen_space(_p2);
}
Expand Down Expand Up @@ -110,9 +154,6 @@ void main(void)
f_linewidth = halfwidth;
f_id = g_id[x];

for (int i = 0; i < 8; i++)
gl_ClipDistance[i] = g_clip_distance[x][i];

for (int y = 0; y < 2; y++) {
// Get offset in y direction & compute vertex position
float n_offset = (2 * y - 1) * (halfwidth + AA_THICKNESS);
Expand Down
22 changes: 1 addition & 21 deletions GLMakie/assets/shader/line_segment.vert
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,10 @@ out float g_thickness;
vec4 to_vec4(vec3 v){return vec4(v, 1);}
vec4 to_vec4(vec2 v){return vec4(v, 0, 1);}

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_id = uvec2(objectid, gl_VertexID + 1);
g_color = color;
g_thickness = px_per_unit * thickness;
vec4 world_pos = model * to_vec4(vertex);
process_clip_planes(world_pos.xyz);
gl_Position = projectionview * world_pos;
gl_Position.z += gl_Position.w * depth_shift;
gl_Position = model * to_vec4(vertex);
}
7 changes: 5 additions & 2 deletions GLMakie/src/drawing_primitives.jl
Original file line number Diff line number Diff line change
Expand Up @@ -503,8 +503,6 @@ function draw_atomic(screen::Screen, scene::Scene, @nospecialize(plot::Lines))
data[:color] = pop!(data, :intensity)
end

data[:num_clip_planes] = Observable(8)

return draw_lines(screen, positions, data)
end
end
Expand All @@ -517,6 +515,11 @@ function draw_atomic(screen::Screen, scene::Scene, @nospecialize(plot::LineSegme
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)


positions = handle_view(plot[1], data)
# positions = lift(apply_transform, plot, transform_func_obs(plot), positions, plot.space)
positions = apply_transform_and_f32_conversion(scene, plot, positions)
Expand Down
19 changes: 19 additions & 0 deletions src/utilities/Plane.jl
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,23 @@ function unclipped_indices(clip_planes::Vector{<: Plane3}, positions::AbstractAr
else
return eachindex(positions)
end
end

function perpendicular_vector(v::Vec3)
# https://math.stackexchange.com/a/4112622
return Vec3(
copysign(v[3], v[1]),
copysign(v[3], v[2]),
-copysign(abs(v[1]) + abs(v[2]), v[3]),
)
end

function to_mesh(plane::Plane3{T}; origin = plane.distance * plane.normal, size = 1) where T
scale = size isa VecTypes ? size : Vec2f(size)
v1 = scale[1] * normalize(perpendicular_vector(plane.normal))
v2 = scale[2] * normalize(cross(v1, plane.normal))
ps = Point3f[origin - v1 - v2, origin - v1 + v2, origin + v1 - v2, origin + v1 + v2]
ns = [plane.normal for _ in 1:4]
fs = GLTriangleFace[(1,2,3), (2, 3, 4)]
return GeometryBasics.Mesh(GeometryBasics.meta(ps; normals=ns), fs)
end

0 comments on commit 078f3ed

Please sign in to comment.