mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-26 10:11:46 +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 bpy
|
||||||
import blf
|
import blf
|
||||||
import math
|
import gpu
|
||||||
import gpu, bgl
|
|
||||||
from bpy import types
|
from bpy import types
|
||||||
from mathutils import Vector, Matrix
|
from mathutils import Vector
|
||||||
from mathutils import geometry
|
from mathutils import geometry
|
||||||
from bpy_extras import view3d_utils
|
from bpy_extras import view3d_utils
|
||||||
from blenderbim.bim.module.drawing.shaders import DotsGizmoShader, ExtrusionGuidesShader, BaseLinesShader
|
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 = self.guides = self.gizmos.new("BIM_GT_extrusion_guides")
|
||||||
gz.matrix_basis = basis
|
gz.matrix_basis = basis
|
||||||
gz.color = gz.color_highlight = tuple(theme.gizmo_secondary)
|
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.use_draw_modal = True
|
||||||
gz.target_set_prop("depth", prop, "value")
|
gz.target_set_prop("depth", prop, "value")
|
||||||
gz.scale_value = scale_value
|
gz.scale_value = scale_value
|
||||||
|
|||||||
@@ -16,20 +16,186 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# 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/>.
|
# 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.types import GPUShader
|
||||||
from gpu_extras.batch import batch_for_shader
|
from gpu_extras.batch import batch_for_shader
|
||||||
import gpu
|
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:
|
class BaseShader:
|
||||||
"""Wrapper for GPUShader
|
"""Wrapper for GPUShader
|
||||||
To use for viewport decorations with geometry generated on GPU side.
|
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).
|
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.
|
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.
|
Replace glsl code in derived classes.
|
||||||
Beware of unused attributes and uniforms: glsl compiler will optimize them out and blender fail with an exception.
|
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
|
TYPE = None # should be LINES|POINTS|etc
|
||||||
|
|
||||||
DEF_GLSL = """
|
DEF_GLSL = BASE_DEF_GLSL
|
||||||
#define PI 3.141592653589793
|
|
||||||
"""
|
|
||||||
|
|
||||||
VERT_GLSL = """
|
VERT_GLSL = """
|
||||||
uniform mat4 viewMatrix;
|
uniform mat4 viewMatrix;
|
||||||
@@ -64,46 +228,45 @@ class BaseShader:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# prepended to geom_glsl
|
# prepended to geom_glsl
|
||||||
LIB_GLSL = """
|
LIB_GLSL = BASE_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();
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
GEOM_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 = """
|
FRAG_GLSL = """
|
||||||
uniform vec4 color;
|
uniform vec4 color;
|
||||||
out vec4 fragColor;
|
uniform float lineWidth;
|
||||||
|
|
||||||
|
in float smoothline;
|
||||||
|
out vec4 fragColor;
|
||||||
void main() {
|
void main() {
|
||||||
|
vec2 co = gl_FragCoord.xy;
|
||||||
|
|
||||||
fragColor = color;
|
fragColor = color;
|
||||||
|
if (lineSmooth) {
|
||||||
|
fragColor.a *= clamp((lineWidth + SMOOTH_WIDTH) * 0.5 - abs(smoothline), 0.0, 1.0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# NB: libcode arg doesn't work
|
# NB: libcode arg doesn't work
|
||||||
|
# TODO: rename to .shader
|
||||||
self.prog = GPUShader(
|
self.prog = GPUShader(
|
||||||
vertexcode=self.VERT_GLSL,
|
vertexcode=self.VERT_GLSL,
|
||||||
fragcode=self.FRAG_GLSL,
|
fragcode=self.FRAG_GLSL,
|
||||||
@@ -122,25 +285,28 @@ class BaseShader:
|
|||||||
|
|
||||||
def glenable(self):
|
def glenable(self):
|
||||||
gpu.state.blend_set("ALPHA")
|
gpu.state.blend_set("ALPHA")
|
||||||
bgl.glBlendFunc(bgl.GL_SRC_ALPHA, bgl.GL_ONE_MINUS_SRC_ALPHA)
|
gpu.state.depth_test_set("LESS_EQUAL")
|
||||||
bgl.glBlendEquation(bgl.GL_FUNC_ADD)
|
|
||||||
|
|
||||||
# gpu.state.depth_test_set('LESS_EQUAL')
|
|
||||||
# bgl.glDepthMask(True)
|
|
||||||
|
|
||||||
def uniform_region(self, ctx):
|
def uniform_region(self, ctx):
|
||||||
region = ctx.region
|
region = ctx.region
|
||||||
region3d = ctx.region_data
|
region3d = ctx.region_data
|
||||||
try:
|
|
||||||
self.prog.uniform_float("viewMatrix", region3d.perspective_matrix)
|
uniform_floats = {
|
||||||
except ValueError: # unused uniform
|
"viewMatrix": region3d.perspective_matrix,
|
||||||
pass
|
"winsize": (region.width, region.height),
|
||||||
try:
|
"lineWidth": 2.5,
|
||||||
self.prog.uniform_float("winSize", (region.width / 2, region.height / 2))
|
}
|
||||||
except ValueError: # unused uniform
|
|
||||||
pass
|
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):
|
class BaseLinesShader(BaseShader):
|
||||||
"""Draws line segments with gaps around vertices at endpoints"""
|
"""Draws line segments with gaps around vertices at endpoints"""
|
||||||
|
|
||||||
@@ -185,7 +351,6 @@ class BaseLinesShader(BaseShader):
|
|||||||
|
|
||||||
def glenable(self):
|
def glenable(self):
|
||||||
super().glenable()
|
super().glenable()
|
||||||
bgl.glEnable(bgl.GL_LINE_SMOOTH)
|
|
||||||
|
|
||||||
|
|
||||||
class GizmoShader(BaseShader):
|
class GizmoShader(BaseShader):
|
||||||
@@ -207,6 +372,7 @@ class GizmoShader(BaseShader):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: add smoothing if this shader is going to be used
|
||||||
class DotsGizmoShader(GizmoShader):
|
class DotsGizmoShader(GizmoShader):
|
||||||
"""Draws circles of radius 1 around points"""
|
"""Draws circles of radius 1 around points"""
|
||||||
|
|
||||||
@@ -274,24 +440,26 @@ class ExtrusionGuidesShader(GizmoShader):
|
|||||||
uniform mat4 ModelViewProjectionMatrix;
|
uniform mat4 ModelViewProjectionMatrix;
|
||||||
|
|
||||||
layout(lines) in;
|
layout(lines) in;
|
||||||
layout(line_strip, max_vertices=10) out;
|
layout(triangle_strip, max_vertices=MAX_POINTS) out;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 p0 = gl_in[0].gl_Position, p1 = gl_in[1].gl_Position;
|
// default setup for macro to work
|
||||||
vec4 p0w = C2W(p0), p1w = C2W(p1);
|
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 bx = ModelViewProjectionMatrix[0] * CROSS_SIZE;
|
||||||
vec4 by = ModelViewProjectionMatrix[1] * CROSS_SIZE;
|
vec4 by = ModelViewProjectionMatrix[1] * CROSS_SIZE;
|
||||||
|
|
||||||
emitSegment(p0 - bx, p0 + bx);
|
do_edge_verts(p0 - bx, p0 + bx);
|
||||||
emitSegment(p0 - by, p0 + by);
|
EndPrimitive();
|
||||||
emitSegment(p1 - bx, p1 + bx);
|
do_edge_verts(p0 - by, p0 + by);
|
||||||
emitSegment(p1 - by, p1 + 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