From 638eb346802928277574f0b5e89e525b569969af Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Fri, 31 Mar 2023 19:33:54 +0500 Subject: [PATCH] annotation and gizmo shaders rework for smoothed lines without bgl #2897 Reworked annotation and gizmo shaders to work without `bgl` and support smooth lines. Moved LIB_GLSL and DEF_GLSL to shaders module since it's basically the same code and this way it'll be easier to keep track of it. --- .../bim/module/drawing/decoration.py | 698 +++++------------- .../blenderbim/bim/module/drawing/gizmos.py | 7 +- .../blenderbim/bim/module/drawing/shaders.py | 284 +++++-- 3 files changed, 418 insertions(+), 571 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/drawing/decoration.py b/src/blenderbim/blenderbim/bim/module/drawing/decoration.py index 2792bdff5a..a717c59a02 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/decoration.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/decoration.py @@ -20,21 +20,18 @@ import os import gpu import bpy import blf -import bgl import math import bmesh import ifcopenshell import blenderbim.tool as tool import blenderbim.bim.module.drawing.helper as helper from math import acos, pi, atan, degrees -from functools import reduce -from itertools import chain from bpy.types import SpaceView3D -from mathutils import Vector, Matrix, geometry +from mathutils import Vector, Matrix from bpy_extras.view3d_utils import location_3d_to_region_2d from gpu.types import GPUShader, GPUBatch, GPUIndexBuf, GPUVertBuf, GPUVertFormat -from gpu_extras.batch import batch_for_shader from blenderbim.bim.module.drawing.data import DecoratorData +from blenderbim.bim.module.drawing.shaders import BASE_LIB_GLSL, BASE_DEF_GLSL def ccw(A, B, C): @@ -46,78 +43,8 @@ class BaseDecorator: # base name of objects to decorate objecttype = "NOTDEFINED" - DEF_GLSL = """ - #define PI 3.141592653589793 - #define MAX_POINTS 64 - #define CIRCLE_SEGS 12 - """ - - LIB_GLSL = """ - #define matCLIP2WIN() vec4(winsize.x/2, winsize.y/2, 1, 1) - #define matWIN2CLIP() vec4(2/winsize.x, 2/winsize.y, 1, 1) - #define CLIP2WIN(v) (clip2win * v / v.w) - #define WIN2CLIP(v) (win2clip * v * v.w) - - void arrow_head(in vec4 dir, in float size, in float angle, out vec4 head[3]) { - vec4 nose = dir * size; - float c = cos(angle), s = sin(angle); - head[0] = nose; - head[1] = vec4(mat2(c, -s, +s, c) * nose.xy, 0, 0); - head[2] = vec4(mat2(c, +s, -s, c) * nose.xy, 0, 0); - } - - void circle_head(in float size, out vec4 head[CIRCLE_SEGS]) { - float angle_d = PI * 2 / CIRCLE_SEGS; - for(int i = 0; i (B.y-A.y) * (C.x-A.x); - } - - void angle_circle_head( - in vec4 circle_start, in float circle_angle, - in bool counterclockwise, - out vec4 head[CIRCLE_SEGS+1], out float angle_segs) { - - // 1 added to CIRCLE_SEGS because we're number of vertices - // for n segments is n+1 - - float angle_d; - angle_d = PI * 2 / CIRCLE_SEGS; // 30d - // need to bottom clamp it to 1, otherwise it causes Blender crash at extruding the curve - angle_segs = max(1, ceil(circle_angle / angle_d)); - angle_d = circle_angle / angle_segs; - - for(int i = 0; i < (angle_segs + 1); i++) { - float angle = angle_d * i; - if (counterclockwise) { - head[i] = vec4( - circle_start.x * cos(-angle) + circle_start.y * sin(-angle), - circle_start.x * -sin(-angle) + circle_start.y * cos(-angle), - 0, 0 - ); - } else { - head[i] = vec4( - circle_start.x * cos(angle) + circle_start.y * sin(angle), - circle_start.x * -sin(angle) + circle_start.y * cos(angle), - 0, 0 - ); - } - } - } - - void cross_head(in vec4 dir, in float size, out vec4 head[3]) { - vec4 nose = dir * size; - float c = cos(PI/2), s = sin(PI/2); - head[0] = nose; - head[1] = vec4(mat2(c, -s, +s, c) * nose.xy, 0, 0); - head[2] = vec4(mat2(c, +s, -s, c) * nose.xy, 0, 0); - } - """ + DEF_GLSL = BASE_DEF_GLSL + LIB_GLSL = BASE_LIB_GLSL VERT_GLSL = """ uniform mat4 viewMatrix; @@ -135,40 +62,36 @@ class BaseDecorator: """ GEOM_GLSL = """ - uniform vec2 winsize; uniform float viewportDrawingScale; layout(lines) in; - layout(line_strip, max_vertices=2) out; + layout(triangle_strip, max_vertices = 4) out; void main() { + // default setup for macro to work vec4 clip2win = matCLIP2WIN(); vec4 win2clip = matWIN2CLIP(); + vec2 EDGE_DIR; vec4 p0 = gl_in[0].gl_Position, p1 = gl_in[1].gl_Position; - - vec4 p0w = CLIP2WIN(p0), p1w = CLIP2WIN(p1); - vec4 edge = p1w - p0w, dir = normalize(edge); - vec4 gap = dir * 16.0; - - vec4 p; - - p = p0w + gap; - gl_Position = WIN2CLIP(p); - EmitVertex(); - p = p1w - gap; - gl_Position = WIN2CLIP(p); - EmitVertex(); + do_edge_verts(p0, p1); EndPrimitive(); } """ FRAG_GLSL = """ uniform vec4 color; - out vec4 fragColor; + uniform float lineWidth; + in float smoothline; + out vec4 fragColor; void main() { + vec2 co = gl_FragCoord.xy; + fragColor = color; + if (lineSmooth) { + fragColor.a *= clamp((lineWidth + SMOOTH_WIDTH) * 0.5 - abs(smoothline), 0.0, 1.0); //test + } } """ @@ -219,6 +142,8 @@ class BaseDecorator: returns: iterable of blender objects """ results = [] + # NOTE: if the ObjectType is not on the list + # it will be also drawn with MiscDecorator decoration_presets = ( "DIMENSION", "TEXT_LEADER", @@ -231,6 +156,7 @@ class BaseDecorator: "ELEVATION", "SECTION", "TEXT", + "BATTING", ) for obj in collection.all_objects: element = tool.Ifc.get_entity(obj) @@ -307,7 +233,6 @@ class BaseDecorator: def get_editmesh_geom(self, obj): """Parses editmode mesh geometry into line segments""" - # TODO: avoid bmesh here? bm = bmesh.from_edit_mesh(obj.data) indices = [[v.index for v in edge.verts] for edge in bm.edges] vertices = [obj.matrix_world @ v.co for v in bm.verts] @@ -327,10 +252,12 @@ class BaseDecorator: is_scale_dependant=True, fill_next_vertices=False, extra_float_kwargs={}, + smoothing=True, ): - # use is_scale_dependant = False if shader is not using uniform viewportDrawingScale - # otherwise uniform will be discarded during the optimization process - # and you will get "ValueError: GPUShader.uniform_float: uniform viewportDrawingScale not found" + """use `is_scale_dependant` = `False` if shader is not using uniform viewportDrawingScale + otherwise uniform will be discarded during the optimization process + and you will get `ValueError: GPUShader.uniform_float: uniform viewportDrawingScale not found` + """ region = context.region region3d = context.region_data @@ -356,16 +283,12 @@ class BaseDecorator: batch = GPUBatch(type="LINES", buf=vbo, elem=ibo) - bgl.glEnable(bgl.GL_LINE_SMOOTH) - bgl.glHint(bgl.GL_LINE_SMOOTH_HINT, bgl.GL_NICEST) - bgl.glEnable(bgl.GL_BLEND) - bgl.glBlendFunc(bgl.GL_SRC_ALPHA, bgl.GL_ONE_MINUS_SRC_ALPHA) - self.shader.bind() - self.shader.uniform_float self.shader.uniform_float("viewMatrix", region3d.perspective_matrix) self.shader.uniform_float("winsize", (region.width, region.height)) self.shader.uniform_float("color", color) + if smoothing: + self.shader.uniform_float("lineWidth", 1.0) if is_scale_dependant: # Horrific prototype code @@ -380,6 +303,7 @@ class BaseDecorator: for kwarg, value in extra_float_kwargs.items(): self.shader.uniform_float(kwarg, value) + gpu.state.blend_set("ALPHA") batch.draw(self.shader) def draw_label( @@ -516,14 +440,14 @@ class DimensionDecorator(BaseDecorator): ) GEOM_GLSL = """ - uniform vec2 winsize; uniform float viewportDrawingScale; uniform float use_oblique_style; layout(lines) in; - layout(line_strip, max_vertices=MAX_POINTS) out; + layout(triangle_strip, max_vertices=MAX_POINTS) out; - void oblique_dimension_head(in vec4 dir, in float size, in float angle, out vec4 head[OBLIQUE_HEAD_VERTS]) { + void oblique_dimension_head(in vec4 dir, in float size, + in float angle, out vec4 head[OBLIQUE_HEAD_VERTS]) { float c = cos(angle), s = sin(angle); vec4 ortho = vec4(-dir.y, dir.x, 0, 0); head[0] = -dir * size*0.66; @@ -544,69 +468,49 @@ class DimensionDecorator(BaseDecorator): vec4 p0w = CLIP2WIN(p0), p1w = CLIP2WIN(p1); vec4 edge = p1w - p0w, dir = normalize(edge); - vec4 p; + vec4 p, p_next; + vec2 EDGE_DIR; if (use_oblique_style > 0) { vec4 head[OBLIQUE_HEAD_VERTS]; oblique_dimension_head(dir, viewportDrawingScale * OLBIQUE_SYMBOL_SIZE, OBLIQUE_SYMBOL_ANGLE, head); // start edge arrow - for (int i = 0; i < OBLIQUE_HEAD_VERTS; i++) { - gl_Position = WIN2CLIP((p0w + head[i])); - EmitVertex(); + p_next = WIN2CLIP( (p0w + head[0]) ); + for (int i = 0; i < OBLIQUE_HEAD_VERTS-1; i++) { + do_edge_verts_win( (p0w + head[i]), (p0w + head[i+1]) ); } EndPrimitive(); // end edge arrow - for (int i = 0; i < OBLIQUE_HEAD_VERTS; i++) { - gl_Position = WIN2CLIP((p1w - head[i])); - EmitVertex(); + p_next = WIN2CLIP( (p1w + head[0]) ); + for (int i = 0; i < OBLIQUE_HEAD_VERTS-1; i++) { + do_edge_verts_win( (p1w + head[i]), (p1w + head[i+1]) ); } EndPrimitive(); // stem - gl_Position = p0; - EmitVertex(); - gl_Position = p1; - EmitVertex(); - EndPrimitive(); + do_edge_verts(p0, p1); + EndPrimitive(); + } else { vec4 head[3]; arrow_head(dir, viewportDrawingScale * ARROW_SIZE, ARROW_ANGLE, head); // start edge arrow - gl_Position = p0; - EmitVertex(); - p = p0w + head[1]; - gl_Position = WIN2CLIP(p); - EmitVertex(); - p = p0w + head[2]; - gl_Position = WIN2CLIP(p); - EmitVertex(); - gl_Position = p0; - EmitVertex(); + do_edge_verts( p0, WIN2CLIP( (p0w + head[1]) ) ); + do_edge_verts_win( p0w + head[1], p0w + head[2] ); + do_edge_verts( WIN2CLIP( p0w + head[2] ), p0 ); EndPrimitive(); // end edge arrow - gl_Position = p1; - EmitVertex(); - p = p1w - head[1]; - gl_Position = WIN2CLIP(p); - EmitVertex(); - p = p1w - head[2]; - gl_Position = WIN2CLIP(p); - EmitVertex(); - gl_Position = p1; - EmitVertex(); + do_edge_verts( p1, WIN2CLIP( p1w - head[1] ) ); + do_edge_verts_win( p1w - head[1], p1w - head[2] ); + do_edge_verts( WIN2CLIP( p1w - head[2] ), p1 ); EndPrimitive(); // stem, with gaps for arrows - p = p0w + head[0]; - gl_Position = WIN2CLIP(p); - EmitVertex(); - p = p1w - head[0]; - gl_Position = WIN2CLIP(p); - EmitVertex(); + do_edge_verts_win( p0w + head[0], p1w - head[0] ); EndPrimitive(); } } @@ -655,18 +559,19 @@ class AngleDecorator(BaseDecorator): ) GEOM_GLSL = """ - uniform vec2 winsize; uniform float viewportDrawingScale; layout(lines) in; - layout(line_strip, max_vertices=MAX_POINTS) out; + layout(triangle_strip, max_vertices=MAX_POINTS) out; in uint type[]; in vec4 v_next_vert[]; // per edge shader void main() { + // default setup for macro to work vec4 clip2win = matCLIP2WIN(); vec4 win2clip = matWIN2CLIP(); + vec2 EDGE_DIR; vec4 p0 = gl_in[0].gl_Position, p1 = gl_in[1].gl_Position; vec4 p2 = v_next_vert[1]; @@ -675,15 +580,10 @@ class AngleDecorator(BaseDecorator): vec4 p0w = CLIP2WIN(p0), p1w = CLIP2WIN(p1); vec4 p2w = CLIP2WIN(p2); vec4 edge0 = p1w - p0w, dir = normalize(edge0); - vec4 p; + // draw a segment line - p = p0w; - gl_Position = WIN2CLIP(p); - EmitVertex(); - p = p1w; - gl_Position = WIN2CLIP(p); - EmitVertex(); + do_edge_verts(p0, p1); EndPrimitive(); // end edge with angle circle for the non-last segment @@ -710,9 +610,8 @@ class AngleDecorator(BaseDecorator): circle_head_data, angle_segs); - for(int i=0; i 0) { - for(int i=0; i 0) { // start edge triangle - triangle_head(side, edge_ortho_dir, viewportDrawingScale * TRIANGLE_L, viewportDrawingScale * TRIANGLE_W, viewportDrawingScale * CIRCLE_SIZE, head5); - p = p0w + head5[0]; - gl_Position = WIN2CLIP(p); - EmitVertex(); - p = p0w + head5[1]; - gl_Position = WIN2CLIP(p); - EmitVertex(); - p = p0w + head5[2]; - gl_Position = WIN2CLIP(p); - EmitVertex(); - p = p0w + head5[3]; - gl_Position = WIN2CLIP(p); - EmitVertex(); - p = p0w + head5[4]; - gl_Position = WIN2CLIP(p); - EmitVertex(); + do_triangle_head(p0w, head5); EndPrimitive(); } // end edge circle if (display_end_circle > 0) { - for(int i=0; i 0) { - triangle_head(side, edge_ortho_dir, viewportDrawingScale * TRIANGLE_L, viewportDrawingScale * TRIANGLE_W, viewportDrawingScale * CIRCLE_SIZE, head5); - p = p1w + head5[0]; - gl_Position = WIN2CLIP(p); - EmitVertex(); - p = p1w + head5[1]; - gl_Position = WIN2CLIP(p); - EmitVertex(); - p = p1w + head5[2]; - gl_Position = WIN2CLIP(p); - EmitVertex(); - p = p1w + head5[3]; - gl_Position = WIN2CLIP(p); - EmitVertex(); - p = p1w + head5[4]; - gl_Position = WIN2CLIP(p); - EmitVertex(); + do_triangle_head(p1w, head5); EndPrimitive(); } - float divider_line_size; - if (connect_markers > 0) { - divider_line_size = CIRCLE_SIZE; - } else { - divider_line_size = CIRCLE_SIZE * 3; + vec4 divider_line_size[2]; + divider_line_size[0] = side * viewportDrawingScale * CIRCLE_SIZE; + divider_line_size[1] = side * viewportDrawingScale * CIRCLE_SIZE; + if (connect_markers == 0) { + divider_line_size[0] = divider_line_size[0] * 3; } // NOTE: basically we consider first vertex to be @@ -2041,36 +1738,25 @@ class SectionDecorator(LevelDecorator): // which is a bit inconsistent with svg // but not that anyone will use multiple edge section annotation + vec4 gap[2]; + // start reference divider if (display_start_symbol > 0) { - p = p0w + side * viewportDrawingScale * divider_line_size; - gl_Position = WIN2CLIP(p); - EmitVertex(); - p = p0w - side * viewportDrawingScale * CIRCLE_SIZE; - gl_Position = WIN2CLIP(p); - EmitVertex(); + do_edge_verts_win( p0w + divider_line_size[0], p0w - divider_line_size[1] ); EndPrimitive(); } // end reference divider if (display_end_symbol > 0) { - p = p1w + side * viewportDrawingScale * CIRCLE_SIZE; - gl_Position = WIN2CLIP(p); - EmitVertex(); - p = p1w - side * viewportDrawingScale * divider_line_size; - gl_Position = WIN2CLIP(p); - EmitVertex(); + do_edge_verts_win( p1w + divider_line_size[1], p1w - divider_line_size[0] ); EndPrimitive(); } // stem if (connect_markers > 0) { - p = p0w + (display_start_symbol > 0 ? gap : vec4(0)); - gl_Position = WIN2CLIP(p); - EmitVertex(); - p = p1w - (display_end_symbol > 0 ? gap : vec4(0)); - gl_Position = WIN2CLIP(p); - EmitVertex(); + gap[0] = (display_start_symbol > 0 ? (side * viewportDrawingScale * CIRCLE_SIZE) : vec4(0)); + gap[1] = (display_end_symbol > 0 ? (side * viewportDrawingScale * CIRCLE_SIZE) : vec4(0)); + do_edge_verts_win( p0w + gap[0], p1w - gap[1] ); EndPrimitive(); } } @@ -2082,9 +1768,7 @@ class SectionDecorator(LevelDecorator): else: verts, idxs = self.get_mesh_geom(obj) - # TODO: add dashed line to shader - # mind the vertices limit - # ref: https://docs.blender.org/api/main/gpu.html#custom-shader-for-dotted-3d-line + # TODO: add dashed line to shader with frag shader display_data = DecoratorData.get_section_markers_display_data(obj) self.draw_lines( context, @@ -2117,11 +1801,10 @@ class TextDecorator(BaseDecorator): ) GEOM_GLSL = """ - uniform vec2 winsize; uniform float viewportDrawingScale; layout(lines) in; - layout(line_strip, max_vertices=MAX_POINTS) out; + layout(triangle_strip, max_vertices=MAX_POINTS) out; void circle_head_asterisk(in float size, out vec4 head[CIRCLE_SEGS_ASTERISK]) { float angle_d = PI * 2 / CIRCLE_SEGS_ASTERISK; @@ -2132,8 +1815,10 @@ class TextDecorator(BaseDecorator): } void main() { + // default setup for macro to work vec4 clip2win = matCLIP2WIN(); vec4 win2clip = matWIN2CLIP(); + vec2 EDGE_DIR; vec4 p0 = gl_in[0].gl_Position; vec4 p0w = CLIP2WIN(p0); @@ -2143,12 +1828,7 @@ class TextDecorator(BaseDecorator): circle_head_asterisk(viewportDrawingScale * CIRCLE_SIZE, head); for (int i=0; i < CIRCLE_SEGS_ASTERISK/2; i++) { - p = p0w + head[i]; - gl_Position = WIN2CLIP(p); - EmitVertex(); - p = p0w + head[i+CIRCLE_SEGS_ASTERISK/2]; - gl_Position = WIN2CLIP(p); - EmitVertex(); + do_edge_verts_win( p0w + head[i], p0w + head[i+CIRCLE_SEGS_ASTERISK/2] ); EndPrimitive(); } } diff --git a/src/blenderbim/blenderbim/bim/module/drawing/gizmos.py b/src/blenderbim/blenderbim/bim/module/drawing/gizmos.py index a286ed0966..783486cdff 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/gizmos.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/gizmos.py @@ -18,10 +18,9 @@ import bpy import blf -import math -import gpu, bgl +import gpu from bpy import types -from mathutils import Vector, Matrix +from mathutils import Vector from mathutils import geometry from bpy_extras import view3d_utils from blenderbim.bim.module.drawing.shaders import DotsGizmoShader, ExtrusionGuidesShader, BaseLinesShader @@ -500,7 +499,7 @@ class ExtrusionWidget(types.GizmoGroup): gz = self.guides = self.gizmos.new("BIM_GT_extrusion_guides") gz.matrix_basis = basis gz.color = gz.color_highlight = tuple(theme.gizmo_secondary) - gz.alpha = gz.alpha_highlight = 0.5 + gz.alpha = gz.alpha_highlight = 0.75 gz.use_draw_modal = True gz.target_set_prop("depth", prop, "value") gz.scale_value = scale_value diff --git a/src/blenderbim/blenderbim/bim/module/drawing/shaders.py b/src/blenderbim/blenderbim/bim/module/drawing/shaders.py index 9e4e940fea..7024325496 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/shaders.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/shaders.py @@ -16,20 +16,186 @@ # You should have received a copy of the GNU General Public License # along with BlenderBIM Add-on. If not, see . -import bgl -from mathutils import Matrix from gpu.types import GPUShader from gpu_extras.batch import batch_for_shader import gpu +BASE_DEF_GLSL = """ +#define PI 3.141592653589793 +#define MAX_POINTS 64 +#define CIRCLE_SEGS 12 +#define SMOOTH_WIDTH 1.0 +#define lineSmooth true +""" + +# since `bgl` is deprecated, creating smoothing lines became tricky +# Notes for creating shaders with smoothed lines: +# in geom shader - use triangle_strip, DEFAULT_SETUP, do_edge_verts or do_vertex to emit vertices +# in frag shader - use lineWidth uniform, smoothline flaot in, smoothing shader code from base shader +# mind the vertex limit since emitting vertices for smoothed lines produces twice as much vertices + +BASE_LIB_GLSL = """ +// TODO: redefine as macor instead +uniform vec2 winsize; +uniform float lineWidth; +#define half_winsize (winsize / 2) +// convert camera to window +#define C2W(v) vec4(v.x * half_winsize.x / v.w, v.y * half_winsize.y / v.w, v.z / v.w, 1) +// convert window to camera +#define W2C(v) vec4(v.x * v.w / half_winsize.x, v.y * v.w / half_winsize.y, v.z * v.w, 1) + +void emitSegment(vec4 p0, vec4 p1) { + gl_Position = p0; + EmitVertex(); + gl_Position = p1; + EmitVertex(); + EndPrimitive(); +} + +void emitTriangle(vec4 p1, vec4 p2, vec4 p3) { + gl_Position = p1; + EmitVertex(); + gl_Position = p2; + EmitVertex(); + gl_Position = p3; + EmitVertex(); + EndPrimitive(); +} + +#define matCLIP2WIN() vec4(winsize.x/2, winsize.y/2, 1, 1) +#define CLIP2WIN(v) (clip2win * (v) / (v).w) +#define matWIN2CLIP() vec4(2/winsize.x, 2/winsize.y, 1, 1) +#define WIN2CLIP(v) (win2clip * (v) * (v).w) +#define DEFAULT_SETUP() vec4 clip2win = matCLIP2WIN(), win2clip = matWIN2CLIP(); vec2 EDGE_DIR + +out float smoothline; + +void arrow_head(in vec4 dir, in float size, in float angle, out vec4 head[3]) { + vec4 nose = dir * size; + float c = cos(angle), s = sin(angle); + head[0] = nose; + head[1] = vec4(mat2(c, -s, +s, c) * nose.xy, 0, 0); + head[2] = vec4(mat2(c, +s, -s, c) * nose.xy, 0, 0); +} + +void circle_head(in float size, out vec4 head[CIRCLE_SEGS]) { + float angle_d = PI * 2 / CIRCLE_SEGS; + for(int i = 0; i (B.y-A.y) * (C.x-A.x); +} + +void angle_circle_head( + in vec4 circle_start, in float circle_angle, + in bool counterclockwise, + out vec4 head[CIRCLE_SEGS+1], out float angle_segs) { + + // 1 added to CIRCLE_SEGS because we're number of vertices + // for n segments is n+1 + + float angle_d; + angle_d = PI * 2 / CIRCLE_SEGS; // 30d + // need to bottom clamp it to 1, otherwise it causes Blender crash at extruding the curve + angle_segs = max(1, ceil(circle_angle / angle_d)); + angle_d = circle_angle / angle_segs; + + for(int i = 0; i < (angle_segs + 1); i++) { + float angle = angle_d * i; + if (counterclockwise) { + head[i] = vec4( + circle_start.x * cos(-angle) + circle_start.y * sin(-angle), + circle_start.x * -sin(-angle) + circle_start.y * cos(-angle), + 0, 0 + ); + } else { + head[i] = vec4( + circle_start.x * cos(angle) + circle_start.y * sin(angle), + circle_start.x * -sin(angle) + circle_start.y * cos(angle), + 0, 0 + ); + } + } +} + +void cross_head(in vec4 dir, in float size, out vec4 head[3]) { + vec4 nose = dir * size; + float c = cos(PI/2), s = sin(PI/2); + head[0] = nose; + head[1] = vec4(mat2(c, -s, +s, c) * nose.xy, 0, 0); + head[2] = vec4(mat2(c, +s, -s, c) * nose.xy, 0, 0); +} + +// do_vertex_util is custom EmitVertex method to support line smoothing +// `e` - edge xy direction in win/clip space (directions are invariant in both spaces) +#define do_vertex(pos, e) (do_vertex_util(pos, vec2(-(e).y, (e).x) / winsize.xy)) +#define do_vertex_win(pos, e) ( do_vertex( WIN2CLIP( pos ), e ) ) + +// if vertex is shared by two segments of the line still need to emit it twice +// to avoid smoothing artifacts +// don't forget to initialize `vec2 EDGE_DIR` for macro to work +// `pos0` / `pos1` - vertex position in clip space +#define do_edge_verts(pos0, pos1) ( EDGE_DIR = normalize( ( (pos1) - (pos0) ).xy ), do_vertex( pos0, EDGE_DIR ), do_vertex( pos1, EDGE_DIR) ) +#define do_edge_verts_win(pos0, pos1) ( do_edge_verts( WIN2CLIP(pos0), WIN2CLIP(pos1) ) ) +void do_vertex_util(vec4 pos, vec2 ofs) +{ + float final_line_width = lineWidth + SMOOTH_WIDTH * float(lineSmooth); + ofs *= final_line_width; + + smoothline = final_line_width * 0.5; + gl_Position = pos; + gl_Position.xy += ofs * pos.w; + EmitVertex(); + + smoothline = -final_line_width * 0.5; + gl_Position = pos; + gl_Position.xy -= ofs * pos.w; + EmitVertex(); +} + +// geometry utils +void triangle_head(in vec4 side, in vec4 dir, in float length, in float width, in float radius, out vec4 head[5]) { + vec4 nose = side * length; + vec4 ear = dir * width; + head[0] = side * -radius; + head[1] = nose * -.5; + head[2] = vec4(0) + ear; + head[3] = nose * .5; + head[4] = side * radius; +} + +void do_triangle_head(vec4 pos_w, vec4 head[5]) { + DEFAULT_SETUP(); + do_edge_verts_win(pos_w + head[0], pos_w + head[1]); + do_edge_verts_win(pos_w + head[1], pos_w + head[2]); + do_edge_verts_win(pos_w + head[2], pos_w + head[3]); + do_edge_verts_win(pos_w + head[3], pos_w + head[4]); +} + +void do_circle_head(vec4 pos_w, vec4 head[CIRCLE_SEGS]) { + DEFAULT_SETUP(); + for(int i=0; i