diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index fbbb169a64a..f9d9c77bc25 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -3593,6 +3593,7 @@ class VIEW3D_PT_overlay(Panel): col.prop(overlay, "show_all_objects_origin") col.prop(overlay, "show_relationship_lines") col.prop(overlay, "show_face_orientation") + col.prop(overlay, "show_wireframes") col.prop(overlay, "show_backface_culling") col = layout.column() diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 96dd9356109..0eeb6bae09e 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -274,6 +274,8 @@ data_to_c_simple(modes/shaders/edit_normals_vert.glsl SRC) data_to_c_simple(modes/shaders/edit_normals_geom.glsl SRC) data_to_c_simple(modes/shaders/overlay_face_orientation_frag.glsl SRC) data_to_c_simple(modes/shaders/overlay_face_orientation_vert.glsl SRC) +data_to_c_simple(modes/shaders/overlay_face_wireframe_vert.glsl SRC) +data_to_c_simple(modes/shaders/overlay_face_wireframe_frag.glsl SRC) data_to_c_simple(modes/shaders/object_empty_image_frag.glsl SRC) data_to_c_simple(modes/shaders/object_empty_image_vert.glsl SRC) data_to_c_simple(modes/shaders/object_outline_resolve_frag.glsl SRC) diff --git a/source/blender/draw/modes/overlay_mode.c b/source/blender/draw/modes/overlay_mode.c index c4b64e03c48..505472460a1 100644 --- a/source/blender/draw/modes/overlay_mode.c +++ b/source/blender/draw/modes/overlay_mode.c @@ -25,6 +25,8 @@ #include "DNA_view3d_types.h" +#include "BKE_object.h" + #include "GPU_shader.h" #include "DRW_render.h" @@ -37,6 +39,7 @@ typedef struct OVERLAY_StorageList { typedef struct OVERLAY_PassList { struct DRWPass *face_orientation_pass; + struct DRWPass *face_wireframe_pass; } OVERLAY_PassList; typedef struct OVERLAY_Data { @@ -56,12 +59,18 @@ typedef struct OVERLAY_PrivateData { static struct { /* Face orientation shader */ struct GPUShader *face_orientation_sh; + /* Wireframe shader */ + struct GPUShader *face_wireframe_sh; } e_data = {NULL}; /* Shaders */ extern char datatoc_overlay_face_orientation_frag_glsl[]; extern char datatoc_overlay_face_orientation_vert_glsl[]; +extern char datatoc_overlay_face_wireframe_vert_glsl[]; +extern char datatoc_overlay_face_wireframe_frag_glsl[]; + +extern struct GlobalsUboStorage ts; /* draw_common.c */ /* Functions */ static void overlay_engine_init(void *vedata) @@ -77,7 +86,14 @@ static void overlay_engine_init(void *vedata) if (!e_data.face_orientation_sh) { /* Face orientation */ e_data.face_orientation_sh = DRW_shader_create( - datatoc_overlay_face_orientation_vert_glsl, NULL, datatoc_overlay_face_orientation_frag_glsl, "\n"); + datatoc_overlay_face_orientation_vert_glsl, NULL, + datatoc_overlay_face_orientation_frag_glsl, NULL); + } + + if (!e_data.face_wireframe_sh) { + e_data.face_wireframe_sh = DRW_shader_create( + datatoc_overlay_face_wireframe_vert_glsl, NULL, + datatoc_overlay_face_wireframe_frag_glsl, NULL); } } @@ -99,11 +115,15 @@ static void overlay_cache_init(void *vedata) /* Face Orientation Pass */ if (stl->g_data->overlay.flag & V3D_OVERLAY_FACE_ORIENTATION) { - int state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_BLEND; + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_BLEND; psl->face_orientation_pass = DRW_pass_create("Face Orientation", state); stl->g_data->face_orientation_shgrp = DRW_shgroup_create( e_data.face_orientation_sh, psl->face_orientation_pass); } + if (stl->g_data->overlay.flag & V3D_OVERLAY_WIREFRAMES) { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_BLEND; + psl->face_wireframe_pass = DRW_pass_create("Face Wires", state); + } } static void overlay_cache_populate(void *vedata, Object *ob) @@ -111,17 +131,41 @@ static void overlay_cache_populate(void *vedata, Object *ob) OVERLAY_Data * data = (OVERLAY_Data *)vedata; OVERLAY_StorageList *stl = data->stl; OVERLAY_PrivateData *pd = stl->g_data; + OVERLAY_PassList *psl = data->psl; + const DRWContextState *draw_ctx = DRW_context_state_get(); if (!DRW_object_is_renderable(ob)) return; - struct Gwn_Batch *geom = DRW_cache_object_surface_get(ob); - if (geom) { - /* Face Orientation */ - if (stl->g_data->overlay.flag & V3D_OVERLAY_FACE_ORIENTATION) { + if (stl->g_data->overlay.flag & V3D_OVERLAY_FACE_ORIENTATION) { + struct Gwn_Batch *geom = DRW_cache_object_surface_get(ob); + if (geom) { DRW_shgroup_call_add(pd->face_orientation_shgrp, geom, ob->obmat); } } + + if (stl->g_data->overlay.flag & V3D_OVERLAY_WIREFRAMES) { + /* Don't do that in edit mode. */ + if ((ob != draw_ctx->object_edit) && !BKE_object_is_in_editmode(ob)) { + int tri_count; + GPUTexture *verts = NULL, *faceids; + DRW_cache_object_face_wireframe_get(ob, &verts, &faceids, &tri_count); + if (verts) { + float *rim_col = ts.colorWire; + if ((ob->base_flag & BASE_SELECTED) != 0) { + rim_col = (ob == draw_ctx->obact) ? ts.colorActive : ts.colorSelect; + } + /* TODO(fclem): Compare performance with a geom shader based approach. */ + DRWShadingGroup *shgrp = DRW_shgroup_create(e_data.face_wireframe_sh, psl->face_wireframe_pass); + DRW_shgroup_uniform_texture(shgrp, "vertData", verts); + DRW_shgroup_uniform_texture(shgrp, "faceIds", faceids); + DRW_shgroup_uniform_vec3(shgrp, "wireColor", ts.colorWire, 1); + DRW_shgroup_uniform_vec3(shgrp, "rimColor", rim_col, 1); + DRW_shgroup_uniform_vec2(shgrp, "viewportSize", DRW_viewport_size_get(), 1); + DRW_shgroup_call_procedural_triangles_add(shgrp, tri_count, ob->obmat); + } + } + } } static void overlay_cache_finish(void *UNUSED(vedata)) @@ -138,11 +182,15 @@ static void overlay_draw_scene(void *vedata) if (pd->overlay.flag & V3D_OVERLAY_FACE_ORIENTATION) { DRW_draw_pass(psl->face_orientation_pass); } + if (pd->overlay.flag & V3D_OVERLAY_WIREFRAMES) { + DRW_draw_pass(psl->face_wireframe_pass); + } } static void overlay_engine_free(void) { DRW_SHADER_FREE_SAFE(e_data.face_orientation_sh); + DRW_SHADER_FREE_SAFE(e_data.face_wireframe_sh); } static const DrawEngineDataSize overlay_data_size = DRW_VIEWPORT_DATA_SIZE(OVERLAY_Data); diff --git a/source/blender/draw/modes/shaders/overlay_face_wireframe_frag.glsl b/source/blender/draw/modes/shaders/overlay_face_wireframe_frag.glsl new file mode 100644 index 00000000000..86e3bb959e9 --- /dev/null +++ b/source/blender/draw/modes/shaders/overlay_face_wireframe_frag.glsl @@ -0,0 +1,36 @@ +uniform vec3 wireColor; +uniform vec3 rimColor; + +flat in vec3 ssVec0; +flat in vec3 ssVec1; +flat in vec3 ssVec2; +in float facing; + +out vec4 fragColor; + +float min_v3(vec3 v) { return min(v.x, min(v.y, v.z)); } + +/* In pixels */ +const float wire_size = 0.0; /* Expands the core of the wire (part that is 100% wire color) */ +const float wire_smooth = 1.4; /* Smoothing distance after the 100% core. */ + +/* Alpha constants could be exposed in the future. */ +const float front_alpha = 0.55; +const float rim_alpha = 0.75; + +void main() +{ + vec3 ss_pos = vec3(gl_FragCoord.xy, 1.0); + vec3 dist_to_edge = vec3( + dot(ss_pos, ssVec0), + dot(ss_pos, ssVec1), + dot(ss_pos, ssVec2) + ); + + float fac = smoothstep(wire_size, wire_size + wire_smooth, min_v3(abs(dist_to_edge))); + float facing_clamped = clamp((gl_FrontFacing) ? facing : -facing, 0.0, 1.0); + + vec3 final_front_col = rimColor * 0.5 + wireColor * 0.5; + fragColor = mix(vec4(rimColor, rim_alpha), vec4(final_front_col, front_alpha), facing_clamped); + fragColor.a *= (1.0 - fac); +} diff --git a/source/blender/draw/modes/shaders/overlay_face_wireframe_vert.glsl b/source/blender/draw/modes/shaders/overlay_face_wireframe_vert.glsl new file mode 100644 index 00000000000..67f4a5c7668 --- /dev/null +++ b/source/blender/draw/modes/shaders/overlay_face_wireframe_vert.glsl @@ -0,0 +1,106 @@ +uniform mat4 ModelViewProjectionMatrix; +uniform mat4 ModelViewMatrix; +uniform mat4 ProjectionMatrix; +uniform mat3 NormalMatrix; + +uniform vec2 viewportSize; +uniform float nearDist; + +uniform samplerBuffer vertData; +uniform isamplerBuffer faceIds; + +flat out vec3 ssVec0; +flat out vec3 ssVec1; +flat out vec3 ssVec2; +out float facing; + +/* project to screen space */ +vec2 proj(vec4 pos) +{ + return (0.5 * (pos.xy / pos.w) + 0.5) * viewportSize; +} + +vec3 compute_vec(vec2 v0, vec2 v1) +{ + vec2 v = normalize(v1 - v0); + v = vec2(-v.y, v.x); + return vec3(v, -dot(v, v0)); +} + +float short_to_unit_float(uint s) +{ + int value = int(s) & 0x7FFF; + if ((s & 0x8000u) != 0u) { + value |= ~0x7FFF; + } + return float(value) / float(0x7FFF); +} + +vec3 get_vertex_nor(int v_id) +{ + v_id *= 5; /* See vertex format for explanation. */ + /* Fetch compressed normal as float and unpack them. */ + vec2 data; + data.x = texelFetch(vertData, v_id + 3).r; + data.y = texelFetch(vertData, v_id + 4).r; + + uvec2 udata = floatBitsToUint(data); + + vec3 nor; + nor.x = short_to_unit_float(udata.x & 0xFFFFu); + nor.y = short_to_unit_float(udata.x >> 16u); + nor.z = short_to_unit_float(udata.y & 0xFFFFu); + return nor; +} + +vec3 get_vertex_pos(int v_id) +{ + v_id *= 5; /* See vertex format for explanation. */ + vec3 pos; + pos.x = texelFetch(vertData, v_id).r; + pos.y = texelFetch(vertData, v_id + 1).r; + pos.z = texelFetch(vertData, v_id + 2).r; + return pos; +} + +#define NO_EDGE vec3(10000.0); + +void main() +{ + + int v_0 = (gl_VertexID / 3) * 3; + int v_n = gl_VertexID % 3; + /* Getting the same positions for each of the 3 verts. */ + ivec3 v_id; + v_id.x = texelFetch(faceIds, v_0).r; + v_id.y = texelFetch(faceIds, v_0 + 1).r; + v_id.z = texelFetch(faceIds, v_0 + 2).r; + + bvec3 do_edge = lessThan(v_id, ivec3(0)); + v_id = abs(v_id) - 1; + + vec3 pos[3]; + pos[0] = get_vertex_pos(v_id.x); + pos[1] = get_vertex_pos(v_id.y); + pos[2] = get_vertex_pos(v_id.z); + + vec4 p_pos[3]; + p_pos[0] = ModelViewProjectionMatrix * vec4(pos[0], 1.0); + p_pos[1] = ModelViewProjectionMatrix * vec4(pos[1], 1.0); + p_pos[2] = ModelViewProjectionMatrix * vec4(pos[2], 1.0); + + vec2 ss_pos[3]; + ss_pos[0] = proj(p_pos[0]); + ss_pos[1] = proj(p_pos[1]); + ss_pos[2] = proj(p_pos[2]); + + /* Compute the edges screen vectors */ + ssVec0 = do_edge.x ? compute_vec(ss_pos[0], ss_pos[1]) : NO_EDGE; + ssVec1 = do_edge.y ? compute_vec(ss_pos[1], ss_pos[2]) : NO_EDGE; + ssVec2 = do_edge.z ? compute_vec(ss_pos[2], ss_pos[0]) : NO_EDGE; + + gl_Position = p_pos[v_n]; + + vec3 nor = get_vertex_nor(v_id[v_n]); + facing = (NormalMatrix * nor).z; +} diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 533ea2dbd84..d0a68a9343c 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -357,6 +357,7 @@ enum { V3D_OVERLAY_HIDE_CURSOR = (1 << 1), V3D_OVERLAY_BONE_SELECTION = (1 << 2), V3D_OVERLAY_LOOK_DEV = (1 << 3), + V3D_OVERLAY_WIREFRAMES = (1 << 4), }; /* View3DOverlay->edit_flag */ diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 4c7b5b3f0a6..19fd87c0735 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -2484,6 +2484,12 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Look Dev", "Show Look Development Balls and Palette"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + prop = RNA_def_property(srna, "show_wireframes", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "overlay.flag", V3D_OVERLAY_WIREFRAMES); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Wireframes", "Show face edges wires"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + prop = RNA_def_property(srna, "show_paint_wire", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "overlay.paint_flag", V3D_OVERLAY_PAINT_WIRE); RNA_def_property_ui_text(prop, "Show Wire", "Use wireframe display in painting modes");