From f9ea64b0baede1af60e33c2f5af23b373fcaad07 Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Thu, 30 May 2024 15:51:30 +0200 Subject: [PATCH] Anim: Per bone wire width for custom shapes The setting adds the "Custom Shape Wire Width" option to the "Viewport Display/Custom Shape" section of a pose bone. As the setting says, this controls how thick the wire is drawn in the viewport. This is done by adding a geometry shader that makes two triangles out of a line. The Anti-Aliasing is controlled by the setting Viewport->Quality->Smooth Wires->Overlay in the user preferences. ## Artifacts When increasing the line width, the lines start to separate at their vertices. This comes from extruding each edge along the normal of its direction. This could be solved by adding round caps in a later PR. Pull Request: https://projects.blender.org/blender/blender/pulls/120176 --- scripts/startup/bl_ui/properties_data_bone.py | 1 + .../blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenkernel/intern/action.cc | 1 + .../blenloader/intern/versioning_400.cc | 11 +++ source/blender/draw/CMakeLists.txt | 3 + .../draw/engines/overlay/overlay_armature.cc | 34 +++++-- .../engines/overlay/overlay_shader_shared.h | 3 + .../shaders/infos/overlay_armature_info.hh | 40 +++++++- .../overlay_armature_shape_wire_frag.glsl | 54 ++++++++++ .../overlay_armature_shape_wire_geom.glsl | 75 ++++++++++++++ .../overlay_armature_shape_wire_vert.glsl | 8 +- ...rlay_armature_shape_wire_vert_no_geom.glsl | 98 +++++++++++++++++++ .../gpu/intern/gpu_shader_create_info.cc | 1 + source/blender/makesdna/DNA_action_types.h | 3 +- source/blender/makesrna/intern/rna_pose.cc | 10 ++ 15 files changed, 328 insertions(+), 16 deletions(-) create mode 100644 source/blender/draw/engines/overlay/shaders/overlay_armature_shape_wire_frag.glsl create mode 100644 source/blender/draw/engines/overlay/shaders/overlay_armature_shape_wire_geom.glsl create mode 100644 source/blender/draw/engines/overlay/shaders/overlay_armature_shape_wire_vert_no_geom.glsl diff --git a/scripts/startup/bl_ui/properties_data_bone.py b/scripts/startup/bl_ui/properties_data_bone.py index 4be324ca176..b1df001f6d0 100644 --- a/scripts/startup/bl_ui/properties_data_bone.py +++ b/scripts/startup/bl_ui/properties_data_bone.py @@ -413,6 +413,7 @@ class BONE_PT_display_custom_shape(BoneButtonsPanel, Panel): sub.separator() sub.prop(bone, "show_wire", text="Wireframe") + sub.prop(pchan, "custom_shape_wire_width") class BONE_PT_inverse_kinematics(BoneButtonsPanel, Panel): diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 8ac1575586e..d5ea5fbc7a0 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -29,7 +29,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 47 +#define BLENDER_FILE_SUBVERSION 48 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to diff --git a/source/blender/blenkernel/intern/action.cc b/source/blender/blenkernel/intern/action.cc index 8b5264ee319..8703ec5348e 100644 --- a/source/blender/blenkernel/intern/action.cc +++ b/source/blender/blenkernel/intern/action.cc @@ -818,6 +818,7 @@ bPoseChannel *BKE_pose_channel_ensure(bPose *pose, const char *name) copy_v3_fl(chan->custom_scale_xyz, 1.0f); zero_v3(chan->custom_translation); zero_v3(chan->custom_rotation_euler); + chan->custom_shape_wire_width = 1.0f; /* init vars to prevent math errors */ unit_qt(chan->quat); diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index 0022e9cbe8c..c7ce25b8db3 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -3675,6 +3675,17 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) } } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 48)) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + if (!ob->pose) { + continue; + } + LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) { + pchan->custom_shape_wire_width = 1.0; + } + } + } + /** * Always bump subversion in BKE_blender_version.h when adding versioning * code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check. diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 5157e64ba56..e3b789250b7 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -752,6 +752,9 @@ set(GLSL_SRC engines/overlay/shaders/overlay_armature_shape_solid_frag.glsl engines/overlay/shaders/overlay_armature_shape_solid_vert.glsl engines/overlay/shaders/overlay_armature_shape_wire_vert.glsl + engines/overlay/shaders/overlay_armature_shape_wire_vert_no_geom.glsl + engines/overlay/shaders/overlay_armature_shape_wire_frag.glsl + engines/overlay/shaders/overlay_armature_shape_wire_geom.glsl engines/overlay/shaders/overlay_armature_sphere_outline_vert.glsl engines/overlay/shaders/overlay_armature_sphere_solid_frag.glsl engines/overlay/shaders/overlay_armature_sphere_solid_vert.glsl diff --git a/source/blender/draw/engines/overlay/overlay_armature.cc b/source/blender/draw/engines/overlay/overlay_armature.cc index 3b91524d3fc..f276919790a 100644 --- a/source/blender/draw/engines/overlay/overlay_armature.cc +++ b/source/blender/draw/engines/overlay/overlay_armature.cc @@ -432,11 +432,16 @@ void OVERLAY_armature_cache_init(OVERLAY_Data *vedata) else { cb->transp.point_outline = cb->solid.point_outline; } + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + GPUTexture **depth_tex = &dtxl->depth; + const bool do_smooth_wire = U.gpu_flag & USER_GPU_FLAG_OVERLAY_SMOOTH_WIRE; sh = OVERLAY_shader_armature_shape(true); cb->solid.custom_outline = grp = DRW_shgroup_create(sh, armature_ps); DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); DRW_shgroup_uniform_float_copy(grp, "alpha", 1.0f); + DRW_shgroup_uniform_bool_copy(grp, "do_smooth_wire", do_smooth_wire); + DRW_shgroup_uniform_texture_ref(grp, "depthTex", depth_tex); cb->solid.box_outline = BUF_INSTANCE(grp, format, DRW_cache_bone_box_wire_get()); cb->solid.octa_outline = BUF_INSTANCE(grp, format, DRW_cache_bone_octahedral_wire_get()); @@ -458,10 +463,12 @@ void OVERLAY_armature_cache_init(OVERLAY_Data *vedata) cb->solid.custom_wire = grp = DRW_shgroup_create(sh, armature_ps); DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); DRW_shgroup_uniform_float_copy(grp, "alpha", 1.0f); + DRW_shgroup_uniform_bool_copy(grp, "do_smooth_wire", do_smooth_wire); + DRW_shgroup_uniform_texture_ref(grp, "depthTex", depth_tex); + DRW_shgroup_state_enable(grp, DRW_STATE_BLEND_ALPHA); if (use_wire_alpha) { cb->transp.custom_wire = grp = DRW_shgroup_create(sh, armature_ps); - DRW_shgroup_state_enable(grp, DRW_STATE_BLEND_ALPHA); DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); DRW_shgroup_uniform_float_copy(grp, "alpha", wire_alpha); } @@ -847,6 +854,7 @@ static void drw_shgroup_bone_custom_solid_mesh(const ArmatureDrawContext *ctx, const float bone_color[4], const float hint_color[4], const float outline_color[4], + const float wire_width, Object &custom) { using namespace blender::draw; @@ -880,7 +888,8 @@ static void drw_shgroup_bone_custom_solid_mesh(const ArmatureDrawContext *ctx, if (loose_edges) { buf = custom_bone_instance_shgroup(ctx, ctx->custom_wire, loose_edges); OVERLAY_bone_instance_data_set_color_hint(&inst_data, outline_color); - OVERLAY_bone_instance_data_set_color(&inst_data, outline_color); + inst_data.color_a = encode_2f_to_float(outline_color[0], outline_color[1]); + inst_data.color_b = encode_2f_to_float(outline_color[2], wire_width / WIRE_WIDTH_COMPRESSION); DRW_buffer_add_entry_struct(buf, inst_data.mat); } @@ -892,6 +901,7 @@ static void drw_shgroup_bone_custom_mesh_wire(const ArmatureDrawContext *ctx, Mesh &mesh, const float (*bone_mat)[4], const float color[4], + const float wire_width, Object &custom) { using namespace blender::draw; @@ -905,7 +915,8 @@ static void drw_shgroup_bone_custom_mesh_wire(const ArmatureDrawContext *ctx, BoneInstanceData inst_data; mul_m4_m4m4(inst_data.mat, ctx->ob->object_to_world().ptr(), bone_mat); OVERLAY_bone_instance_data_set_color_hint(&inst_data, color); - OVERLAY_bone_instance_data_set_color(&inst_data, color); + inst_data.color_a = encode_2f_to_float(color[0], color[1]); + inst_data.color_b = encode_2f_to_float(color[2], wire_width / WIRE_WIDTH_COMPRESSION); DRW_buffer_add_entry_struct(buf, inst_data.mat); } @@ -953,6 +964,7 @@ static void drw_shgroup_bone_custom_solid(const ArmatureDrawContext *ctx, const float bone_color[4], const float hint_color[4], const float outline_color[4], + const float wire_width, Object *custom) { /* The custom object is not an evaluated object, so its object->data field hasn't been replaced @@ -962,7 +974,7 @@ static void drw_shgroup_bone_custom_solid(const ArmatureDrawContext *ctx, Mesh *mesh = BKE_object_get_evaluated_mesh_no_subsurf(custom); if (mesh != nullptr) { drw_shgroup_bone_custom_solid_mesh( - ctx, *mesh, bone_mat, bone_color, hint_color, outline_color, *custom); + ctx, *mesh, bone_mat, bone_color, hint_color, outline_color, wire_width, *custom); return; } @@ -975,12 +987,13 @@ static void drw_shgroup_bone_custom_solid(const ArmatureDrawContext *ctx, static void drw_shgroup_bone_custom_wire(const ArmatureDrawContext *ctx, const float (*bone_mat)[4], const float color[4], + const float wire_width, Object *custom) { /* See comments in #drw_shgroup_bone_custom_solid. */ Mesh *mesh = BKE_object_get_evaluated_mesh_no_subsurf(custom); if (mesh != nullptr) { - drw_shgroup_bone_custom_mesh_wire(ctx, *mesh, bone_mat, color, *custom); + drw_shgroup_bone_custom_mesh_wire(ctx, *mesh, bone_mat, color, wire_width, *custom); return; } @@ -2122,10 +2135,17 @@ class ArmatureBoneDrawStrategyCustomShape : public ArmatureBoneDrawStrategy { } } if ((boneflag & BONE_DRAWWIRE) == 0 && (boneflag & BONE_DRAW_LOCKED_WEIGHT) == 0) { - drw_shgroup_bone_custom_solid(ctx, disp_mat, col_solid, col_hint, col_wire, pchan->custom); + drw_shgroup_bone_custom_solid(ctx, + disp_mat, + col_solid, + col_hint, + col_wire, + pchan->custom_shape_wire_width, + pchan->custom); } else { - drw_shgroup_bone_custom_wire(ctx, disp_mat, col_wire, pchan->custom); + drw_shgroup_bone_custom_wire( + ctx, disp_mat, col_wire, pchan->custom_shape_wire_width, pchan->custom); } if (select_id != -1) { diff --git a/source/blender/draw/engines/overlay/overlay_shader_shared.h b/source/blender/draw/engines/overlay/overlay_shader_shared.h index 11752d306ef..c160d125b06 100644 --- a/source/blender/draw/engines/overlay/overlay_shader_shared.h +++ b/source/blender/draw/engines/overlay/overlay_shader_shared.h @@ -51,6 +51,9 @@ ENUM_OPERATORS(OVERLAY_GridBits, CUSTOM_GRID) /* Match: #SI_GRID_STEPS_LEN */ #define OVERLAY_GRID_STEPS_LEN 8 +/* Due to the encoding clamping the passed in floats, the wire width needs to be scaled down. */ +#define WIRE_WIDTH_COMPRESSION 16 + struct OVERLAY_GridData { float4 steps[OVERLAY_GRID_STEPS_LEN]; /* float arrays are padded to float4 in std130. */ float4 size; /* float3 padded to float4. */ diff --git a/source/blender/draw/engines/overlay/shaders/infos/overlay_armature_info.hh b/source/blender/draw/engines/overlay/shaders/infos/overlay_armature_info.hh index 3317d39e053..aefab3234b2 100644 --- a/source/blender/draw/engines/overlay/shaders/infos/overlay_armature_info.hh +++ b/source/blender/draw/engines/overlay/shaders/infos/overlay_armature_info.hh @@ -136,21 +136,55 @@ GPU_SHADER_CREATE_INFO(overlay_armature_shape_solid_clipped) .do_static_compilation(true) .additional_info("overlay_armature_shape_solid", "drw_clipped"); +GPU_SHADER_INTERFACE_INFO(overlay_armature_shape_wire_iface, "geometry_in") + .smooth(Type::VEC4, "finalColor") + .flat(Type::FLOAT, "wire_width"); + +GPU_SHADER_INTERFACE_INFO(overlay_armature_shape_wire_geom_iface, "geometry_out") + .flat(Type::VEC4, "finalColor") + .flat(Type::FLOAT, "wire_width"); + +GPU_SHADER_INTERFACE_INFO(overlay_armature_shape_wire_geom_noperspective_iface, + "geometry_noperspective_out") + .no_perspective(Type::FLOAT, "edgeCoord"); + GPU_SHADER_CREATE_INFO(overlay_armature_shape_wire) .do_static_compilation(true) + .push_constant(Type::BOOL, "do_smooth_wire") + .sampler(0, ImageType::DEPTH_2D, "depthTex") .vertex_in(0, Type::VEC3, "pos") - .vertex_in(1, Type::VEC3, "nor") /* Per instance. */ .vertex_in(2, Type::MAT4, "inst_obmat") - .vertex_out(overlay_armature_wire_iface) + .vertex_out(overlay_armature_shape_wire_iface) .vertex_source("overlay_armature_shape_wire_vert.glsl") - .fragment_source("overlay_armature_wire_frag.glsl") + .geometry_out(overlay_armature_shape_wire_geom_iface) + .geometry_out(overlay_armature_shape_wire_geom_noperspective_iface) + .geometry_layout(PrimitiveIn::LINES, PrimitiveOut::TRIANGLE_STRIP, 4) + .geometry_source("overlay_armature_shape_wire_geom.glsl") + .fragment_source("overlay_armature_shape_wire_frag.glsl") + .typedef_source("overlay_shader_shared.h") .additional_info("overlay_frag_output", "overlay_armature_common", "draw_globals"); GPU_SHADER_CREATE_INFO(overlay_armature_shape_wire_clipped) .do_static_compilation(true) .additional_info("overlay_armature_shape_wire", "drw_clipped"); +#ifdef WITH_METAL_BACKEND +GPU_SHADER_CREATE_INFO(overlay_armature_shape_wire_no_geom) + .metal_backend_only(true) + .do_static_compilation(true) + .push_constant(Type::BOOL, "do_smooth_wire") + .sampler(0, ImageType::DEPTH_2D, "depthTex") + .vertex_in(0, Type::VEC3, "pos") + .vertex_in(2, Type::MAT4, "inst_obmat") + .vertex_out(overlay_armature_shape_wire_geom_iface) + .vertex_out(overlay_armature_shape_wire_geom_noperspective_iface) + .vertex_source("overlay_armature_shape_wire_vert_no_geom.glsl") + .fragment_source("overlay_armature_shape_wire_frag.glsl") + .typedef_source("overlay_shader_shared.h") + .additional_info("overlay_frag_output", "overlay_armature_common", "draw_globals"); +#endif + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/draw/engines/overlay/shaders/overlay_armature_shape_wire_frag.glsl b/source/blender/draw/engines/overlay/shaders/overlay_armature_shape_wire_frag.glsl new file mode 100644 index 00000000000..1fb27b996d4 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/overlay_armature_shape_wire_frag.glsl @@ -0,0 +1,54 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(select_lib.glsl) + +/** + * We want to know how much a pixel is covered by a line. + * We replace the square pixel with a circle of the same area and try to find the intersection + * area. The area we search is the circular segment. https://en.wikipedia.org/wiki/Circular_segment + * The formula for the area uses inverse trig function and is quite complex. Instead, + * we approximate it by using the smooth-step function and a 1.05 factor to the disc radius. + */ + +#define M_1_SQRTPI 0.5641895835477563 /* `1/sqrt(pi)`. */ + +#define DISC_RADIUS (M_1_SQRTPI * 1.05) +#define GRID_LINE_SMOOTH_START (0.5 - DISC_RADIUS) +#define GRID_LINE_SMOOTH_END (0.5 + DISC_RADIUS) + +bool test_occlusion() +{ + return gl_FragCoord.z > texelFetch(depthTex, ivec2(gl_FragCoord.xy), 0).r; +} + +float edge_step(float dist) +{ + if (do_smooth_wire) { + return smoothstep(GRID_LINE_SMOOTH_START, GRID_LINE_SMOOTH_END, dist); + } + else { + return step(0.5, dist); + } +} + +void main() +{ + float wire_width = geometry_out.wire_width; + if (do_smooth_wire) { + wire_width -= 0.5; + } + + float half_size = wire_width / 2.0; + + float dist = abs(geometry_noperspective_out.edgeCoord) - half_size; + const float mix_w = clamp(edge_step(dist), 0.0, 1.0); + + fragColor = mix(vec4(geometry_out.finalColor.rgb, alpha), vec4(0), mix_w); + fragColor.a *= 1.0 - mix_w; + fragColor.a *= test_occlusion() ? alpha : 1.0; + select_id_output(select_id); + lineOutput = vec4(0); +} diff --git a/source/blender/draw/engines/overlay/shaders/overlay_armature_shape_wire_geom.glsl b/source/blender/draw/engines/overlay/shaders/overlay_armature_shape_wire_geom.glsl new file mode 100644 index 00000000000..9202f6d226f --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/overlay_armature_shape_wire_geom.glsl @@ -0,0 +1,75 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + +void do_vertex(vec4 color, vec4 pos, float coord, vec2 offset) +{ + geometry_out.finalColor = color; + geometry_noperspective_out.edgeCoord = coord; + gl_Position = pos; + /* Multiply offset by 2 because gl_Position range is [-1..1]. */ + gl_Position.xy += offset * 2.0 * pos.w; + /* Correct but fails due to an AMD compiler bug, see: #62792. + * Do inline instead. */ +#if 0 + view_clipping_distances_set(gl_in[i]); +#endif + gpu_EmitVertex(); +} + +void main() +{ + /* Clip line against near plane to avoid deformed lines. */ + vec4 pos0 = gl_in[0].gl_Position; + vec4 pos1 = gl_in[1].gl_Position; + const vec2 pz_ndc = vec2(pos0.z / pos0.w, pos1.z / pos1.w); + const bvec2 clipped = lessThan(pz_ndc, vec2(-1.0)); + if (all(clipped)) { + /* Totally clipped. */ + return; + } + + const vec4 pos01 = pos0 - pos1; + const float ofs = abs((pz_ndc.y + 1.0) / (pz_ndc.x - pz_ndc.y)); + if (clipped.y) { + pos1 += pos01 * ofs; + } + else if (clipped.x) { + pos0 -= pos01 * (1.0 - ofs); + } + + vec2 screen_space_pos[2]; + screen_space_pos[0] = pos0.xy / pos0.w; + screen_space_pos[1] = pos1.xy / pos1.w; + + /* `sizeEdge` is defined as the distance from the center to the outer edge. As such to get the + total width it needs to be doubled. */ + const float wire_width = geometry_in[0].wire_width * (sizeEdge * 2); + geometry_out.wire_width = wire_width; + float half_size = max(wire_width / 2.0, 0.5); + + if (do_smooth_wire) { + /* Add 1px for AA */ + half_size += 0.5; + } + + const vec2 line = (screen_space_pos[0] - screen_space_pos[1]) * sizeViewport.xy; + const vec2 line_norm = normalize(vec2(line[1], -line[0])); + vec2 edge_ofs = (half_size * line_norm) * sizeViewportInv; + + /* Due to an AMD glitch, this line was moved out of the `do_vertex` + * function (see #62792). */ + view_clipping_distances_set(gl_in[0]); + const vec4 final_color = geometry_in[0].finalColor; + do_vertex(final_color, pos0, half_size, edge_ofs); + do_vertex(final_color, pos0, -half_size, -edge_ofs); + + view_clipping_distances_set(gl_in[1]); + do_vertex(final_color, pos1, half_size, edge_ofs); + do_vertex(final_color, pos1, -half_size, -edge_ofs); + + EndPrimitive(); +} diff --git a/source/blender/draw/engines/overlay/shaders/overlay_armature_shape_wire_vert.glsl b/source/blender/draw/engines/overlay/shaders/overlay_armature_shape_wire_vert.glsl index aed7c084ba7..cbeaf9d7377 100644 --- a/source/blender/draw/engines/overlay/shaders/overlay_armature_shape_wire_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/overlay_armature_shape_wire_vert.glsl @@ -13,10 +13,10 @@ void main() vec3 world_pos = (model_mat * vec4(pos, 1.0)).xyz; gl_Position = point_world_to_ndc(world_pos); - finalColor.rgb = mix(state_color.rgb, bone_color.rgb, 0.5); - finalColor.a = 1.0; - - edgeStart = edgePos = ((gl_Position.xy / gl_Position.w) * 0.5 + 0.5) * sizeViewport.xy; + geometry_in.finalColor.rgb = mix(state_color.rgb, bone_color.rgb, 0.5); + geometry_in.finalColor.a = 1.0; + /* Because the packing clamps the value, the wire width is passed in compressed. */ + geometry_in.wire_width = bone_color.a * WIRE_WIDTH_COMPRESSION; view_clipping_distances(world_pos); } diff --git a/source/blender/draw/engines/overlay/shaders/overlay_armature_shape_wire_vert_no_geom.glsl b/source/blender/draw/engines/overlay/shaders/overlay_armature_shape_wire_vert_no_geom.glsl new file mode 100644 index 00000000000..25f987d19e5 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/overlay_armature_shape_wire_vert_no_geom.glsl @@ -0,0 +1,98 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma USE_SSBO_VERTEX_FETCH(TriangleList, 6) +#pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + +vec4 vertex_main(mat4 model_mat, vec3 in_pos) +{ + vec3 world_pos = (model_mat * vec4(in_pos, 1.0)).xyz; + view_clipping_distances(world_pos); + return point_world_to_ndc(world_pos); +} + +void do_vertex(vec4 pos, vec2 ofs, float coord, float flip) +{ + geometry_noperspective_out.edgeCoord = coord; + gl_Position = pos; + /* Multiply offset by 2 because gl_Position range is [-1..1]. */ + gl_Position.xy += flip * ofs * 2.0 * pos.w; +} + +void main() +{ + /* Fetch per-instance data (matrix) and extract data from it. */ + mat4 in_inst_obmat = vertex_fetch_attribute(gl_VertexID, inst_obmat, mat4); + vec4 bone_color, state_color; + mat4 model_mat = extract_matrix_packed_data(in_inst_obmat, state_color, bone_color); + + geometry_out.finalColor.rgb = mix(state_color.rgb, bone_color.rgb, 0.5); + geometry_out.finalColor.a = 1.0; + /* Because the packing clamps the value, the wire width is passed in compressed. + `sizeEdge` is defined as the distance from the center to the outer edge. As such to get the total + width it needs to be doubled. */ + float wire_width = bone_color.a * WIRE_WIDTH_COMPRESSION * (sizeEdge * 2); + geometry_out.wire_width = wire_width; + + /* Fetch vertex positions and transform to clip space ("vertex shader"). */ + int quad_id = gl_VertexID / 6; + int quad_vertex_id = gl_VertexID % 6; + uint src_index_a = quad_id * 2; + uint src_index_b = quad_id * 2 + 1; + + vec3 in_pos[2]; + in_pos[0] = vertex_fetch_attribute(src_index_a, pos, vec3); + in_pos[1] = vertex_fetch_attribute(src_index_b, pos, vec3); + vec4 out_pos[2]; + out_pos[0] = vertex_main(model_mat, in_pos[0]); + out_pos[1] = vertex_main(model_mat, in_pos[1]); + + /* Clip line against near plane to avoid deformed lines. */ + vec4 pos0 = out_pos[0]; + vec4 pos1 = out_pos[1]; + const vec2 pz_ndc = vec2(pos0.z / pos0.w, pos1.z / pos1.w); + const bvec2 clipped = lessThan(pz_ndc, vec2(-1.0)); + if (all(clipped)) { + /* Totally clipped. */ + gl_Position = vec4(0.0); + return; + } + + const vec4 pos01 = pos0 - pos1; + const float ofs = abs((pz_ndc.y + 1.0) / (pz_ndc.x - pz_ndc.y)); + if (clipped.y) { + pos1 += pos01 * ofs; + } + else if (clipped.x) { + pos0 -= pos01 * (1.0 - ofs); + } + + vec2 screen_space_pos[2]; + screen_space_pos[0] = pos0.xy / pos0.w; + screen_space_pos[1] = pos1.xy / pos1.w; + + float half_size = max(wire_width / 2.0, 0.5); + if (do_smooth_wire) { + /* Add 1px for AA */ + half_size += 0.5; + } + + const vec2 line = (screen_space_pos[0] - screen_space_pos[1]) * sizeViewport.xy; + const vec2 line_norm = normalize(vec2(line[1], -line[0])); + vec2 edge_ofs = (half_size * line_norm) * sizeViewportInv; + + if (quad_vertex_id == 0) { + do_vertex(pos0, edge_ofs, half_size, 1.0); + } + else if (quad_vertex_id == 1 || quad_vertex_id == 3) { + do_vertex(pos0, edge_ofs, -half_size, -1.0); + } + else if (quad_vertex_id == 2 || quad_vertex_id == 5) { + do_vertex(pos1, edge_ofs, half_size, 1.0); + } + else if (quad_vertex_id == 4) { + do_vertex(pos1, edge_ofs, -half_size, -1.0); + } +} diff --git a/source/blender/gpu/intern/gpu_shader_create_info.cc b/source/blender/gpu/intern/gpu_shader_create_info.cc index f39a20fdd85..30d4a7c1e4d 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.cc +++ b/source/blender/gpu/intern/gpu_shader_create_info.cc @@ -499,6 +499,7 @@ void gpu_shader_create_info_init() /* Overlay Armature Shape outline. */ overlay_armature_shape_outline = overlay_armature_shape_outline_no_geom; overlay_armature_shape_outline_clipped = overlay_armature_shape_outline_clipped_no_geom; + overlay_armature_shape_wire = overlay_armature_shape_wire_no_geom; /* Overlay Motion Path Line. */ overlay_motion_path_line = overlay_motion_path_line_no_geom; diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index 042c45f6cf6..d456b75af56 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -335,6 +335,7 @@ typedef struct bPoseChannel { float custom_scale_xyz[3]; float custom_translation[3]; float custom_rotation_euler[3]; + float custom_shape_wire_width; /** Transforms - written in by actions or transform. */ float loc[3]; @@ -352,7 +353,7 @@ typedef struct bPoseChannel { float rotAxis[3], rotAngle; /** #eRotationModes - rotation representation to use. */ short rotmode; - char _pad[2]; + char _pad[6]; /** * Matrix result of location/rotation/scale components, and evaluation of diff --git a/source/blender/makesrna/intern/rna_pose.cc b/source/blender/makesrna/intern/rna_pose.cc index 03486682a32..8a94b3040c1 100644 --- a/source/blender/makesrna/intern/rna_pose.cc +++ b/source/blender/makesrna/intern/rna_pose.cc @@ -1162,6 +1162,16 @@ static void rna_def_pose_channel(BlenderRNA *brna) prop, nullptr, "rna_PoseChannel_custom_shape_transform_set", nullptr, nullptr); RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update"); + prop = RNA_def_property(srna, "custom_shape_wire_width", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, nullptr, "custom_shape_wire_width"); + RNA_def_property_ui_text( + prop, "Custom Shape Wire Width", "Adjust the line thickness of custom shapes"); + /* When changing the upper limit of the range, also adjust the WIRE_WIDTH_COMPRESSION in + * overlay_shader_shared.h */ + RNA_def_property_range(prop, 1.0f, 16.0f); + RNA_def_property_ui_range(prop, 1.0f, 10.0f, 1, 1); + RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Pose_update"); + prop = RNA_def_property(srna, "color", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "BoneColor"); RNA_def_property_clear_flag(prop, PROP_EDITABLE);