diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index cffd135369a..27daab90234 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -221,6 +221,8 @@ data_to_c_simple(modes/shaders/common_globals_lib.glsl SRC) data_to_c_simple(modes/shaders/common_view_lib.glsl SRC) data_to_c_simple(modes/shaders/common_fxaa_lib.glsl SRC) data_to_c_simple(modes/shaders/common_fullscreen_vert.glsl SRC) +data_to_c_simple(modes/shaders/armature_shape_outline_vert.glsl SRC) +data_to_c_simple(modes/shaders/armature_shape_outline_geom.glsl SRC) data_to_c_simple(modes/shaders/edit_mesh_overlay_frag.glsl SRC) data_to_c_simple(modes/shaders/edit_mesh_overlay_vert.glsl SRC) data_to_c_simple(modes/shaders/edit_mesh_overlay_geom_tri.glsl SRC) diff --git a/source/blender/draw/intern/draw_armature.c b/source/blender/draw/intern/draw_armature.c index fe87e7f17fd..195acfb7c1f 100644 --- a/source/blender/draw/intern/draw_armature.c +++ b/source/blender/draw/intern/draw_armature.c @@ -75,8 +75,10 @@ static struct { /* Reset when changing current_armature */ DRWShadingGroup *bone_octahedral_solid; DRWShadingGroup *bone_octahedral_wire; + DRWShadingGroup *bone_octahedral_outline; DRWShadingGroup *bone_box_solid; DRWShadingGroup *bone_box_wire; + DRWShadingGroup *bone_box_outline; DRWShadingGroup *bone_wire_wire; DRWShadingGroup *bone_envelope_solid; DRWShadingGroup *bone_envelope_distance; @@ -88,6 +90,7 @@ static struct { DRWShadingGroup *relationship_lines; DRWPass *pass_bone_solid; + DRWPass *pass_bone_outline; DRWPass *pass_bone_wire; DRWPass *pass_bone_envelope; } g_data = {NULL}; @@ -114,10 +117,13 @@ static void drw_shgroup_bone_octahedral_wire(const float (*bone_mat)[4], const f if (g_data.bone_octahedral_wire == NULL) { struct Gwn_Batch *geom = DRW_cache_bone_octahedral_wire_outline_get(); g_data.bone_octahedral_wire = shgroup_instance_wire(g_data.pass_bone_wire, geom); + geom = DRW_cache_bone_octahedral_get(); + g_data.bone_octahedral_outline = shgroup_instance_armature_shape_outline(g_data.pass_bone_outline, geom); } float final_bonemat[4][4]; mul_m4_m4m4(final_bonemat, g_data.ob->obmat, bone_mat); DRW_shgroup_call_dynamic_add(g_data.bone_octahedral_wire, final_bonemat, color); + DRW_shgroup_call_dynamic_add(g_data.bone_octahedral_outline, final_bonemat, color); } /* Box / B-Bone */ @@ -137,10 +143,13 @@ static void drw_shgroup_bone_box_wire(const float (*bone_mat)[4], const float co if (g_data.bone_box_wire == NULL) { struct Gwn_Batch *geom = DRW_cache_bone_box_wire_outline_get(); g_data.bone_box_wire = shgroup_instance_wire(g_data.pass_bone_wire, geom); + geom = DRW_cache_bone_box_get(); + g_data.bone_box_outline = shgroup_instance_armature_shape_outline(g_data.pass_bone_outline, geom); } float final_bonemat[4][4]; mul_m4_m4m4(final_bonemat, g_data.ob->obmat, bone_mat); DRW_shgroup_call_dynamic_add(g_data.bone_box_wire, final_bonemat, color); + DRW_shgroup_call_dynamic_add(g_data.bone_box_outline, final_bonemat, color); } /* Wire */ @@ -1416,13 +1425,16 @@ static void draw_armature_pose(Object *ob, const float const_color[4]) * This function set the object space to use for all subsequent `DRW_shgroup_bone_*` calls. */ static void drw_shgroup_armature( - Object *ob, DRWPass *pass_bone_solid, DRWPass *pass_bone_wire, DRWPass *pass_bone_envelope, + Object *ob, + DRWPass *pass_bone_solid, DRWPass *pass_bone_outline, + DRWPass *pass_bone_wire, DRWPass *pass_bone_envelope, DRWShadingGroup *shgrp_relationship_lines) { memset(&g_data, 0x0, sizeof(g_data)); g_data.ob = ob; g_data.pass_bone_solid = pass_bone_solid; + g_data.pass_bone_outline = pass_bone_outline; g_data.pass_bone_wire = pass_bone_wire; g_data.pass_bone_envelope = pass_bone_envelope; g_data.relationship_lines = shgrp_relationship_lines; @@ -1431,29 +1443,35 @@ static void drw_shgroup_armature( } void DRW_shgroup_armature_object( - Object *ob, ViewLayer *view_layer, DRWPass *pass_bone_solid, DRWPass *pass_bone_wire, DRWPass *UNUSED(pass_bone_envelope), + Object *ob, ViewLayer *view_layer, + DRWPass *pass_bone_solid, DRWPass *pass_bone_outline, + DRWPass *pass_bone_wire, DRWPass *UNUSED(pass_bone_envelope), DRWShadingGroup *shgrp_relationship_lines) { float *color; DRW_object_wire_theme_get(ob, view_layer, &color); - drw_shgroup_armature(ob, pass_bone_solid, pass_bone_wire, NULL, shgrp_relationship_lines); + drw_shgroup_armature(ob, pass_bone_solid, pass_bone_outline, pass_bone_wire, NULL, shgrp_relationship_lines); draw_armature_pose(ob, color); } void DRW_shgroup_armature_pose( - Object *ob, DRWPass *pass_bone_solid, DRWPass *pass_bone_wire, DRWPass *pass_bone_envelope, + Object *ob, + DRWPass *pass_bone_solid, DRWPass *pass_bone_outline, + DRWPass *pass_bone_wire, DRWPass *pass_bone_envelope, DRWShadingGroup *shgrp_relationship_lines) { - drw_shgroup_armature(ob, pass_bone_solid, pass_bone_wire, pass_bone_envelope, shgrp_relationship_lines); + drw_shgroup_armature(ob, pass_bone_solid, pass_bone_outline, pass_bone_wire, pass_bone_envelope, shgrp_relationship_lines); draw_armature_pose(ob, NULL); } void DRW_shgroup_armature_edit( - Object *ob, DRWPass *pass_bone_solid, DRWPass *pass_bone_wire, DRWPass *pass_bone_envelope, + Object *ob, + DRWPass *pass_bone_solid, DRWPass *pass_bone_outline, + DRWPass *pass_bone_wire, DRWPass *pass_bone_envelope, DRWShadingGroup *shgrp_relationship_lines) { - drw_shgroup_armature(ob, pass_bone_solid, pass_bone_wire, pass_bone_envelope, shgrp_relationship_lines); + drw_shgroup_armature(ob, pass_bone_solid, pass_bone_outline, pass_bone_wire, pass_bone_envelope, shgrp_relationship_lines); draw_armature_edit(ob); } diff --git a/source/blender/draw/intern/draw_common.c b/source/blender/draw/intern/draw_common.c index 6bcf7fd6531..3a2aa970a9b 100644 --- a/source/blender/draw/intern/draw_common.c +++ b/source/blender/draw/intern/draw_common.c @@ -156,6 +156,14 @@ void DRW_globals_update(void) /* ********************************* SHGROUP ************************************* */ +extern char datatoc_armature_shape_outline_vert_glsl[]; +extern char datatoc_armature_shape_outline_geom_glsl[]; +extern char datatoc_gpu_shader_flat_color_frag_glsl[]; + +static struct { + struct GPUShader *shape_outline; +} g_armature_shaders = {NULL}; + static struct { struct Gwn_VertFormat *instance_screenspace; struct Gwn_VertFormat *instance_color; @@ -178,6 +186,11 @@ void DRW_globals_free(void) for (int i = 0; i < sizeof(g_formats) / sizeof(void *); ++i, ++format) { MEM_SAFE_FREE(*format); } + + struct GPUShader **shader = &g_armature_shaders.shape_outline; + for (int i = 0; i < sizeof(g_armature_shaders) / sizeof(void *); ++i, ++shader) { + DRW_SHADER_FREE_SAFE(*shader); + } } DRWShadingGroup *shgroup_dynlines_uniform_color(DRWPass *pass, float color[4]) @@ -454,6 +467,32 @@ DRWShadingGroup *shgroup_instance_mball_handles(DRWPass *pass, struct Gwn_Batch return grp; } +/* Only works with batches with adjacency infos. */ +DRWShadingGroup *shgroup_instance_armature_shape_outline(DRWPass *pass, struct Gwn_Batch *geom) +{ + if (g_armature_shaders.shape_outline == NULL) { + g_armature_shaders.shape_outline = DRW_shader_create( + datatoc_armature_shape_outline_vert_glsl, + datatoc_armature_shape_outline_geom_glsl, + datatoc_gpu_shader_flat_color_frag_glsl, + NULL); + } + + /* TODO own format? */ + DRW_shgroup_instance_format(g_formats.instance_color, { + {"InstanceModelMatrix", DRW_ATTRIB_FLOAT, 16}, + {"color" , DRW_ATTRIB_FLOAT, 4} + }); + + DRWShadingGroup *grp = DRW_shgroup_instance_create(g_armature_shaders.shape_outline, + pass, geom, g_formats.instance_color); + DRW_shgroup_uniform_vec2(grp, "viewportSize", DRW_viewport_size_get(), 1); + + return grp; +} + + + /* ******************************************** COLOR UTILS *********************************************** */ diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h index c70a1a38e62..8b418135342 100644 --- a/source/blender/draw/intern/draw_common.h +++ b/source/blender/draw/intern/draw_common.h @@ -121,6 +121,7 @@ struct DRWShadingGroup *shgroup_spot_instance(struct DRWPass *pass, struct Gwn_B struct DRWShadingGroup *shgroup_instance_bone_envelope_wire(struct DRWPass *pass, struct Gwn_Batch *geom); struct DRWShadingGroup *shgroup_instance_bone_envelope_solid(struct DRWPass *pass, struct Gwn_Batch *geom); struct DRWShadingGroup *shgroup_instance_mball_handles(struct DRWPass *pass, struct Gwn_Batch *geom); +struct DRWShadingGroup *shgroup_instance_armature_shape_outline(struct DRWPass *pass, struct Gwn_Batch *geom); int DRW_object_wire_theme_get( struct Object *ob, struct ViewLayer *view_layer, float **r_color); @@ -129,17 +130,20 @@ float *DRW_color_background_blend_get(int theme_id); /* draw_armature.c */ void DRW_shgroup_armature_object( struct Object *ob, struct ViewLayer *view_layer, - struct DRWPass *pass_bone_solid, struct DRWPass *pass_bone_wire, struct DRWPass *pass_bone_envelope, + struct DRWPass *pass_bone_solid, struct DRWPass *pass_bone_outline, + struct DRWPass *pass_bone_wire, struct DRWPass *pass_bone_envelope, struct DRWShadingGroup *shgrp_relationship_lines); void DRW_shgroup_armature_pose( struct Object *ob, - struct DRWPass *pass_bone_solid, struct DRWPass *pass_bone_wire, struct DRWPass *pass_bone_envelope, + struct DRWPass *pass_bone_solid, struct DRWPass *pass_bone_outline, + struct DRWPass *pass_bone_wire, struct DRWPass *pass_bone_envelope, struct DRWShadingGroup *shgrp_relationship_lines); void DRW_shgroup_armature_edit( struct Object *ob, - struct DRWPass *pass_bone_solid, struct DRWPass *pass_bone_wire, struct DRWPass *pass_bone_envelope, + struct DRWPass *pass_bone_solid, struct DRWPass *pass_bone_outline, + struct DRWPass *pass_bone_wire, struct DRWPass *pass_bone_envelope, struct DRWShadingGroup *shgrp_relationship_lines); /* pose_mode.c */ diff --git a/source/blender/draw/modes/edit_armature_mode.c b/source/blender/draw/modes/edit_armature_mode.c index 3c0c33b1daa..c1859537be9 100644 --- a/source/blender/draw/modes/edit_armature_mode.c +++ b/source/blender/draw/modes/edit_armature_mode.c @@ -38,6 +38,7 @@ extern GlobalsUboStorage ts; typedef struct EDIT_ARMATURE_PassList { struct DRWPass *bone_solid; struct DRWPass *bone_wire; + struct DRWPass *bone_outline; struct DRWPass *bone_envelope; struct DRWPass *relationship; } EDIT_ARMATURE_PassList; @@ -78,6 +79,12 @@ static void EDIT_ARMATURE_cache_init(void *vedata) psl->bone_solid = DRW_pass_create("Bone Solid Pass", state); } + { + /* Bones Outline */ + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS; + psl->bone_outline = DRW_pass_create("Bone Outline Pass", state); + } + { /* Wire bones */ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_BLEND; @@ -112,7 +119,8 @@ static void EDIT_ARMATURE_cache_populate(void *vedata, Object *ob) if (ob->type == OB_ARMATURE) { if (arm->edbo) { DRW_shgroup_armature_edit( - ob, psl->bone_solid, psl->bone_wire, psl->bone_envelope, stl->g_data->relationship_lines); + ob, psl->bone_solid, psl->bone_outline, psl->bone_wire, + psl->bone_envelope, stl->g_data->relationship_lines); } } } @@ -125,6 +133,7 @@ static void EDIT_ARMATURE_draw_scene(void *vedata) MULTISAMPLE_SYNC_ENABLE(dfbl) DRW_draw_pass(psl->bone_envelope); + DRW_draw_pass(psl->bone_outline); DRW_draw_pass(psl->bone_solid); DRW_draw_pass(psl->bone_wire); DRW_draw_pass(psl->relationship); diff --git a/source/blender/draw/modes/object_mode.c b/source/blender/draw/modes/object_mode.c index 0ddbad27f7d..babe93b5506 100644 --- a/source/blender/draw/modes/object_mode.c +++ b/source/blender/draw/modes/object_mode.c @@ -101,6 +101,7 @@ typedef struct OBJECT_PassList { struct DRWPass *outlines_resolve; struct DRWPass *grid; struct DRWPass *bone_solid; + struct DRWPass *bone_outline; struct DRWPass *bone_wire; struct DRWPass *bone_envelope; struct DRWPass *particle; @@ -1040,6 +1041,7 @@ static void OBJECT_cache_init(void *vedata) /* Solid bones */ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS; psl->bone_solid = DRW_pass_create("Bone Solid Pass", state); + psl->bone_outline = DRW_pass_create("Bone Outline Pass", state); } { @@ -2141,7 +2143,7 @@ static void OBJECT_cache_populate(void *vedata, Object *ob) if (arm->edbo == NULL) { if (DRW_state_is_select() || !DRW_pose_mode_armature(ob, draw_ctx->obact)) { DRW_shgroup_armature_object( - ob, view_layer, psl->bone_solid, psl->bone_wire, psl->bone_envelope, + ob, view_layer, psl->bone_solid, psl->bone_outline, psl->bone_wire, psl->bone_envelope, stl->g_data->relationship_lines); } } diff --git a/source/blender/draw/modes/pose_mode.c b/source/blender/draw/modes/pose_mode.c index 6570df2d33c..12c78e85500 100644 --- a/source/blender/draw/modes/pose_mode.c +++ b/source/blender/draw/modes/pose_mode.c @@ -44,6 +44,7 @@ extern GlobalsUboStorage ts; typedef struct POSE_PassList { struct DRWPass *bone_solid; + struct DRWPass *bone_outline; struct DRWPass *bone_wire; struct DRWPass *bone_envelope; struct DRWPass *relationship; @@ -87,6 +88,12 @@ static void POSE_cache_init(void *vedata) psl->bone_solid = DRW_pass_create("Bone Solid Pass", state); } + { + /* Bones Outline */ + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS; + psl->bone_outline = DRW_pass_create("Bone Outline Pass", state); + } + { /* Wire bones */ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_BLEND; @@ -125,7 +132,7 @@ static void POSE_cache_populate(void *vedata, Object *ob) if (ob->type == OB_ARMATURE) { if (DRW_pose_mode_armature(ob, draw_ctx->obact)) { DRW_shgroup_armature_pose( - ob, psl->bone_solid, psl->bone_wire, psl->bone_envelope, + ob, psl->bone_solid, psl->bone_outline, psl->bone_wire, psl->bone_envelope, stl->g_data->relationship_lines); } } @@ -161,6 +168,7 @@ static void POSE_draw_scene(void *vedata) POSE_PassList *psl = ((POSE_Data *)vedata)->psl; DRW_draw_pass(psl->bone_envelope); + DRW_draw_pass(psl->bone_outline); DRW_draw_pass(psl->bone_wire); DRW_draw_pass(psl->bone_solid); DRW_draw_pass(psl->relationship); diff --git a/source/blender/draw/modes/shaders/armature_shape_outline_geom.glsl b/source/blender/draw/modes/shaders/armature_shape_outline_geom.glsl new file mode 100644 index 00000000000..3d3cb692634 --- /dev/null +++ b/source/blender/draw/modes/shaders/armature_shape_outline_geom.glsl @@ -0,0 +1,95 @@ + +/* TODO: See perf with multiple invocations. */ +layout(triangles_adjacency) in; +layout(triangle_strip, max_vertices = 16) out; + +in vec4 pPos[]; +in float vZ[]; +in float vFacing[]; +in vec2 ssPos[]; +in vec2 ssNor[]; +in vec4 vCol[]; + +flat out vec4 finalColor; +uniform mat4 ProjectionMatrix; +uniform vec2 viewportSize; +uniform float lineThickness = 3.0; + +vec2 compute_dir(vec2 v0, vec2 v1) +{ + vec2 dir = normalize(v1 - v0); + dir = vec2(-dir.y, dir.x); + return dir; +} + +void emit_edge(const ivec3 edges, vec2 thick, bool is_persp) +{ + vec2 edge_dir = compute_dir(ssPos[edges.x], ssPos[edges.y]); + vec2 hidden_dir = normalize(ssPos[edges.z] - ssPos[edges.x]); + + float fac = dot(-hidden_dir, edge_dir); + + vec2 t = thick * (is_persp ? vZ[edges.x] : 1.0); + gl_Position = pPos[edges.x]; + EmitVertex(); + gl_Position.xy += t * edge_dir * sign(fac); + EmitVertex(); + + t = thick * (is_persp ? vZ[edges.y] : 1.0); + gl_Position = pPos[edges.y]; + EmitVertex(); + gl_Position.xy += t * edge_dir * sign(fac); + EmitVertex(); +} + +void emit_corner(const int e, vec2 thick, bool is_persp) +{ + vec2 corner_dir = ssNor[e]; + vec2 t = thick * (is_persp ? vZ[e] : 1.0); + + gl_Position = pPos[e] + vec4(t * corner_dir, 0.0, 0.0); + EmitVertex(); +} + +void main(void) +{ + finalColor = vCol[0]; + + vec2 thick = lineThickness / viewportSize; + bool is_persp = (ProjectionMatrix[3][3] == 0.0); + + const ivec3 edges = ivec3(0, 2, 4); + vec4 facing = vec4(vFacing[1], vFacing[3], vFacing[5], vFacing[0]); + bvec4 do_edge = greaterThanEqual(facing, vec4(0.0)); + + /* Only generate outlines from backfaces. */ + if (do_edge.w) + return; + + if (do_edge.x) { + emit_corner(edges.x, thick, is_persp); + emit_edge(edges.xyz, thick, is_persp); + } + + if (any(do_edge.xy)) { + emit_corner(edges.y, thick, is_persp); + } + + if (do_edge.y) { + emit_edge(edges.yzx, thick, is_persp); + } + else { + EndPrimitive(); + } + + if (any(do_edge.yz)) { + emit_corner(edges.z, thick, is_persp); + } + + if (do_edge.z) { + emit_edge(edges.zxy, thick, is_persp); + emit_corner(edges.x, thick, is_persp); + } + + EndPrimitive(); +} diff --git a/source/blender/draw/modes/shaders/armature_shape_outline_vert.glsl b/source/blender/draw/modes/shaders/armature_shape_outline_vert.glsl new file mode 100644 index 00000000000..2bfac594493 --- /dev/null +++ b/source/blender/draw/modes/shaders/armature_shape_outline_vert.glsl @@ -0,0 +1,53 @@ + +uniform mat3 NormalMatrix; + +uniform mat4 ViewMatrix; +uniform mat4 ProjectionMatrix; +uniform vec2 viewportSize; + +/* ---- Instanciated Attribs ---- */ +in vec3 pos; +in vec3 nor; +in vec3 snor; + +/* ---- Per instance Attribs ---- */ +in mat4 InstanceModelMatrix; +in vec4 color; + +out vec4 pPos; +out float vZ; +out float vFacing; +out vec2 ssPos; +out vec2 ssNor; +out vec4 vCol; + +/* project to screen space */ +vec2 proj(vec4 pos) +{ + return (0.5 * (pos.xy / pos.w) + 0.5) * viewportSize; +} + +void main() +{ + /* This is slow and run per vertex, but it's still faster than + * doing it per instance on CPU and sending it on via instance attrib */ + mat3 NormalMatrix = transpose(inverse(mat3(ViewMatrix * InstanceModelMatrix))); + + vec4 viewpos = ViewMatrix * (InstanceModelMatrix * vec4(pos, 1.0)); + pPos = ProjectionMatrix * viewpos; + vZ = abs(viewpos.z); + + /* if perspective */ + vec3 V = (ProjectionMatrix[3][3] == 0.0) ? normalize(-viewpos.xyz) : vec3(0.0, 0.0, 1.0); + + /* TODO FIX: there is still a problem with this vector + * when the bone is scaled or in persp mode. But it's + * barelly visible at the outline corners. */ + ssNor = normalize((NormalMatrix * snor).xy); + + vFacing = dot(V, normalize(NormalMatrix * nor)); + + ssPos = proj(pPos); + + vCol = color; +}