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);