mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 18:16:40 +00:00
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.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -16,20 +16,186 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<CIRCLE_SEGS; i++) {
|
||||
float angle = angle_d * i;
|
||||
head[i] = vec4(cos(angle), sin(angle), 0, 0) * size;
|
||||
}
|
||||
}
|
||||
|
||||
bool check_counterclockwise(in vec4 A, in vec4 B, in vec4 C) {
|
||||
return (C.y-A.y) * (B.x-A.x) > (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<CIRCLE_SEGS-1; i++) {
|
||||
do_edge_verts_win(pos_w + head[i], pos_w + head[i+1]);
|
||||
EmitVertex();
|
||||
}
|
||||
do_edge_verts_win( pos_w + head[CIRCLE_SEGS-1], pos_w + head[0] );
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
|
||||
class BaseShader:
|
||||
"""Wrapper for GPUShader
|
||||
To use for viewport decorations with geometry generated on GPU side.
|
||||
|
||||
The Geometry shader works in clipping coords (aftre projecting before division and window scaling).
|
||||
To make window-scale geometry, vectors should be calculated in window space and than back-projected to clipping spae.
|
||||
Provide `winSize` uniform vector with halfsize of window, and use W2C and C2W macros.
|
||||
Provide `half_winsize` uniform vector with halfsize of window, and use W2C and C2W macros.
|
||||
|
||||
Replace glsl code in derived classes.
|
||||
Beware of unused attributes and uniforms: glsl compiler will optimize them out and blender fail with an exception.
|
||||
@@ -44,9 +210,7 @@ class BaseShader:
|
||||
|
||||
TYPE = None # should be LINES|POINTS|etc
|
||||
|
||||
DEF_GLSL = """
|
||||
#define PI 3.141592653589793
|
||||
"""
|
||||
DEF_GLSL = BASE_DEF_GLSL
|
||||
|
||||
VERT_GLSL = """
|
||||
uniform mat4 viewMatrix;
|
||||
@@ -64,46 +228,45 @@ class BaseShader:
|
||||
"""
|
||||
|
||||
# prepended to geom_glsl
|
||||
LIB_GLSL = """
|
||||
uniform vec2 winSize;
|
||||
// convert camera to window
|
||||
#define C2W(v) vec4(v.x * winSize.x / v.w, v.y * winSize.y / v.w, v.z / v.w, 1)
|
||||
// convert window to camera
|
||||
#define W2C(v) vec4(v.x * v.w / winSize.x, v.y * v.w / 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();
|
||||
}
|
||||
"""
|
||||
LIB_GLSL = BASE_LIB_GLSL
|
||||
|
||||
GEOM_GLSL = """
|
||||
uniform float viewportDrawingScale;
|
||||
|
||||
layout(lines) in;
|
||||
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;
|
||||
do_edge_verts(p0, p1);
|
||||
EndPrimitive();
|
||||
}
|
||||
"""
|
||||
|
||||
FRAG_GLSL = """
|
||||
uniform vec4 color;
|
||||
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);
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
# NB: libcode arg doesn't work
|
||||
# TODO: rename to .shader
|
||||
self.prog = GPUShader(
|
||||
vertexcode=self.VERT_GLSL,
|
||||
fragcode=self.FRAG_GLSL,
|
||||
@@ -122,25 +285,28 @@ class BaseShader:
|
||||
|
||||
def glenable(self):
|
||||
gpu.state.blend_set("ALPHA")
|
||||
bgl.glBlendFunc(bgl.GL_SRC_ALPHA, bgl.GL_ONE_MINUS_SRC_ALPHA)
|
||||
bgl.glBlendEquation(bgl.GL_FUNC_ADD)
|
||||
|
||||
# gpu.state.depth_test_set('LESS_EQUAL')
|
||||
# bgl.glDepthMask(True)
|
||||
gpu.state.depth_test_set("LESS_EQUAL")
|
||||
|
||||
def uniform_region(self, ctx):
|
||||
region = ctx.region
|
||||
region3d = ctx.region_data
|
||||
try:
|
||||
self.prog.uniform_float("viewMatrix", region3d.perspective_matrix)
|
||||
except ValueError: # unused uniform
|
||||
pass
|
||||
try:
|
||||
self.prog.uniform_float("winSize", (region.width / 2, region.height / 2))
|
||||
except ValueError: # unused uniform
|
||||
pass
|
||||
|
||||
uniform_floats = {
|
||||
"viewMatrix": region3d.perspective_matrix,
|
||||
"winsize": (region.width, region.height),
|
||||
"lineWidth": 2.5,
|
||||
}
|
||||
|
||||
for name, value in uniform_floats.items():
|
||||
try:
|
||||
self.prog.uniform_float(name, value)
|
||||
# TODO: shouldn't just try'n'catch them
|
||||
# because they may indicate errors in code
|
||||
except ValueError: # unused uniform
|
||||
pass
|
||||
|
||||
|
||||
# TODO: add smoothing if this shades is going to be used
|
||||
class BaseLinesShader(BaseShader):
|
||||
"""Draws line segments with gaps around vertices at endpoints"""
|
||||
|
||||
@@ -185,7 +351,6 @@ class BaseLinesShader(BaseShader):
|
||||
|
||||
def glenable(self):
|
||||
super().glenable()
|
||||
bgl.glEnable(bgl.GL_LINE_SMOOTH)
|
||||
|
||||
|
||||
class GizmoShader(BaseShader):
|
||||
@@ -207,6 +372,7 @@ class GizmoShader(BaseShader):
|
||||
"""
|
||||
|
||||
|
||||
# TODO: add smoothing if this shader is going to be used
|
||||
class DotsGizmoShader(GizmoShader):
|
||||
"""Draws circles of radius 1 around points"""
|
||||
|
||||
@@ -274,24 +440,26 @@ class ExtrusionGuidesShader(GizmoShader):
|
||||
uniform mat4 ModelViewProjectionMatrix;
|
||||
|
||||
layout(lines) in;
|
||||
layout(line_strip, max_vertices=10) out;
|
||||
layout(triangle_strip, max_vertices=MAX_POINTS) out;
|
||||
|
||||
void main() {
|
||||
vec4 p0 = gl_in[0].gl_Position, p1 = gl_in[1].gl_Position;
|
||||
vec4 p0w = C2W(p0), p1w = C2W(p1);
|
||||
// default setup for macro to work
|
||||
vec2 EDGE_DIR;
|
||||
|
||||
emitSegment(p0, p1);
|
||||
vec4 p0 = gl_in[0].gl_Position, p1 = gl_in[1].gl_Position;
|
||||
do_edge_verts(p0, p1);
|
||||
EndPrimitive();
|
||||
|
||||
vec4 bx = ModelViewProjectionMatrix[0] * CROSS_SIZE;
|
||||
vec4 by = ModelViewProjectionMatrix[1] * CROSS_SIZE;
|
||||
|
||||
emitSegment(p0 - bx, p0 + bx);
|
||||
emitSegment(p0 - by, p0 + by);
|
||||
emitSegment(p1 - bx, p1 + bx);
|
||||
emitSegment(p1 - by, p1 + by);
|
||||
do_edge_verts(p0 - bx, p0 + bx);
|
||||
EndPrimitive();
|
||||
do_edge_verts(p0 - by, p0 + by);
|
||||
EndPrimitive();
|
||||
do_edge_verts(p1 - bx, p1 + bx);
|
||||
EndPrimitive();
|
||||
do_edge_verts(p1 - by, p1 + by);
|
||||
EndPrimitive();
|
||||
}
|
||||
"""
|
||||
|
||||
def glenable(self):
|
||||
super().glenable()
|
||||
bgl.glEnable(bgl.GL_LINE_SMOOTH)
|
||||
|
||||
Reference in New Issue
Block a user