diff --git a/scripts/startup/bl_ui/properties_data_bone.py b/scripts/startup/bl_ui/properties_data_bone.py index 33c506e270c..247ffd59a0c 100644 --- a/scripts/startup/bl_ui/properties_data_bone.py +++ b/scripts/startup/bl_ui/properties_data_bone.py @@ -327,6 +327,7 @@ class BONE_PT_display(BoneButtonsPanel, Panel): hide_select_sub = col.column() hide_select_sub.active = not bone.hide hide_select_sub.prop(bone, "hide_select", invert_checkbox=True) + col.prop(bone, "display_type", text="Display As") # Figure out the pose bone. ob = context.object @@ -359,6 +360,7 @@ class BONE_PT_display(BoneButtonsPanel, Panel): hide_select_sub = col.column() hide_select_sub.active = not bone.hide hide_select_sub.prop(bone, "hide_select", invert_checkbox=True) + col.prop(bone, "display_type", text="Display As") layout.prop(bone.color, "palette", text="Bone Color") self.draw_bone_color_ui(layout, bone.color) diff --git a/source/blender/blenkernel/BKE_armature.hh b/source/blender/blenkernel/BKE_armature.hh index 6b15698f57e..0f6f1408c51 100644 --- a/source/blender/blenkernel/BKE_armature.hh +++ b/source/blender/blenkernel/BKE_armature.hh @@ -64,6 +64,7 @@ struct EditBone { */ int flag; int layer; + int drawtype; /* eArmature_Drawtype */ char inherit_scale_mode; /* Envelope distance & weight */ diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index e16402d5de5..4c24b0ecaca 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -27,7 +27,7 @@ /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 71 +#define BLENDER_FILE_SUBVERSION 72 /* 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/blenloader/intern/versioning_450.cc b/source/blender/blenloader/intern/versioning_450.cc index c2770670b0b..bec12feb31b 100644 --- a/source/blender/blenloader/intern/versioning_450.cc +++ b/source/blender/blenloader/intern/versioning_450.cc @@ -4339,6 +4339,15 @@ static void version_convert_sculpt_planar_brushes(Main *bmain) } } +static void version_set_default_bone_drawtype(Main *bmain) +{ + LISTBASE_FOREACH (bArmature *, arm, &bmain->armatures) { + blender::animrig::ANIM_armature_foreach_bone( + &arm->bonebase, [](Bone *bone) { bone->drawtype = ARM_BONE_DEFAULT; }); + BLI_assert_msg(!arm->edbo, "Armatures should not be saved in edit mode"); + } +} + void blo_do_versions_450(FileData * /*fd*/, Library * /*lib*/, Main *bmain) { @@ -5148,6 +5157,10 @@ void blo_do_versions_450(FileData * /*fd*/, Library * /*lib*/, Main *bmain) FOREACH_NODETREE_END; } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 405, 72)) { + version_set_default_bone_drawtype(bmain); + } + /* Always run this versioning (keep at the bottom of the function). Meshes are written with the * legacy format which always needs to be converted to the new format on file load. To be moved * to a subversion check in 5.0. */ diff --git a/source/blender/draw/engines/overlay/overlay_armature.cc b/source/blender/draw/engines/overlay/overlay_armature.cc index f89625882d4..f7c95a87ec5 100644 --- a/source/blender/draw/engines/overlay/overlay_armature.cc +++ b/source/blender/draw/engines/overlay/overlay_armature.cc @@ -189,56 +189,6 @@ class UnifiedBonePtr { } }; -/** - * Bone drawing strategy class. - * - * Depending on the armature display mode, a different subclass is used to - * manage drawing. These subclasses are defined further down in the file. This - * abstract class needs to be defined before any function that uses it, though. - */ -class ArmatureBoneDrawStrategy { - public: - virtual void update_display_matrix(UnifiedBonePtr bone) const = 0; - - virtual void draw_context_setup(Armatures::DrawContext *ctx, - const bool is_filled, - const bool do_envelope_dist) const = 0; - - virtual void draw_bone(const Armatures::DrawContext *ctx, - const UnifiedBonePtr bone, - const eBone_Flag boneflag, - const int select_id) const = 0; - - /** Should the relationship line between this bone and its parent be drawn? */ - virtual bool should_draw_relation_to_parent(const UnifiedBonePtr bone, - const eBone_Flag boneflag) const - { - const bool has_parent = bone.has_parent(); - - if (bone.is_editbone() && has_parent) { - /* Always draw for unconnected bones, regardless of selection, - * since riggers will want to know about the links between bones - */ - return (boneflag & BONE_CONNECTED) == 0; - } - - if (bone.is_posebone() && has_parent) { - /* Only draw between unconnected bones. */ - if (boneflag & BONE_CONNECTED) { - return false; - } - - /* Only draw if bone or its parent is selected - reduces viewport - * complexity with complex rigs */ - const bPoseChannel *pchan = bone.as_posebone(); - return (boneflag & BONE_SELECTED) || - (pchan->parent->bone && (pchan->parent->bone->flag & BONE_SELECTED)); - } - - return false; - } -}; - /* -------------------------------------------------------------------- */ /** \name Shading Groups * \{ */ @@ -992,7 +942,7 @@ static void draw_bone_update_disp_matrix_default(UnifiedBonePtr bone) float(*disp_tail_mat)[4] = bone.disp_tail_mat(); /* TODO: This should be moved to depsgraph or armature refresh - * and not be tight to the draw pass creation. + * and not be tied to the draw pass creation. * This would refresh armature without invalidating the draw cache */ if (bone.is_posebone()) { bPoseChannel *pchan = bone.as_posebone(); @@ -1014,6 +964,38 @@ static void draw_bone_update_disp_matrix_default(UnifiedBonePtr bone) translate_m4(disp_tail_mat, 0.0f, 1.0f, 0.0f); } +static void draw_bone_update_disp_matrix_custom_shape(UnifiedBonePtr bone) +{ + float bone_scale[3]; + float(*bone_mat)[4]; + float(*disp_mat)[4]; + float(*disp_tail_mat)[4]; + float rot_mat[3][3]; + + /* Custom bone shapes are only supported in pose mode for now. */ + bPoseChannel *pchan = bone.as_posebone(); + + /* TODO: This should be moved to depsgraph or armature refresh + * and not be tied to the draw pass creation. + * This would refresh armature without invalidating the draw cache. */ + mul_v3_v3fl(bone_scale, pchan->custom_scale_xyz, PCHAN_CUSTOM_BONE_LENGTH(pchan)); + bone_mat = pchan->custom_tx ? pchan->custom_tx->pose_mat : pchan->pose_mat; + disp_mat = bone.disp_mat(); + disp_tail_mat = pchan->disp_tail_mat; + + eulO_to_mat3(rot_mat, pchan->custom_rotation_euler, ROT_MODE_XYZ); + + copy_m4_m4(disp_mat, bone_mat); + translate_m4(disp_mat, + pchan->custom_translation[0], + pchan->custom_translation[1], + pchan->custom_translation[2]); + mul_m4_m4m3(disp_mat, disp_mat, rot_mat); + rescale_m4(disp_mat, bone_scale); + copy_m4_m4(disp_tail_mat, disp_mat); + translate_m4(disp_tail_mat, 0.0f, 1.0f, 0.0f); +} + /* compute connected child pointer for B-Bone drawing */ static void edbo_compute_bbone_child(bArmature *arm) { @@ -1153,7 +1135,7 @@ static void draw_bone_update_disp_matrix_bbone(UnifiedBonePtr bone) short bbone_segments; /* TODO: This should be moved to depsgraph or armature refresh - * and not be tight to the draw pass creation. + * and not be tied to the draw pass creation. * This would refresh armature without invalidating the draw cache. */ if (bone.is_posebone()) { bPoseChannel *pchan = bone.as_posebone(); @@ -1334,6 +1316,285 @@ static void draw_points(const Armatures::DrawContext *ctx, } } +static void bone_draw_custom_shape(const Armatures::DrawContext *ctx, + const UnifiedBonePtr bone, + const eBone_Flag boneflag, + const int select_id) +{ + const float *col_solid = get_bone_solid_color(ctx, boneflag); + const float *col_wire = get_bone_wire_color(ctx, boneflag); + const float *col_hint = get_bone_hint_color(ctx, boneflag); + const float(*disp_mat)[4] = bone.disp_mat(); + + /* TODO(fclem): Code after this scope should be removed when we remove the legacy code. */ + auto sel_id = ctx->res->select_id(*ctx->ob_ref, select_id | BONESEL_BONE); + + /* Custom bone shapes are only supported in pose mode for now. */ + const bPoseChannel *pchan = bone.as_posebone(); + Object *custom_shape_ob = pchan->custom; + + if (custom_shape_ob->type == OB_EMPTY) { + if (custom_shape_ob->empty_drawtype != OB_EMPTY_IMAGE) { + drw_shgroup_bone_custom_empty( + ctx, disp_mat, col_wire, pchan->custom_shape_wire_width, sel_id, pchan->custom); + } + } + else if (boneflag & (BONE_DRAWWIRE | BONE_DRAW_LOCKED_WEIGHT)) { + drw_shgroup_bone_custom_wire( + ctx, disp_mat, col_wire, pchan->custom_shape_wire_width, sel_id, pchan->custom); + } + else { + drw_shgroup_bone_custom_solid(ctx, + disp_mat, + col_solid, + col_hint, + col_wire, + pchan->custom_shape_wire_width, + sel_id, + pchan->custom); + } +} + +static void bone_draw_octa(const Armatures::DrawContext *ctx, + const UnifiedBonePtr bone, + const eBone_Flag boneflag, + const int select_id) +{ + const float *col_solid = get_bone_solid_with_consts_color(ctx, bone, boneflag); + const float *col_wire = get_bone_wire_color(ctx, boneflag); + const float *col_hint = get_bone_hint_color(ctx, boneflag); + + auto sel_id = ctx->res->select_id(*ctx->ob_ref, select_id | BONESEL_BONE); + float4x4 bone_mat = ctx->ob->object_to_world() * float4x4(bone.disp_mat()); + + if (ctx->is_filled) { + ctx->bone_buf->octahedral_fill_buf.append({bone_mat, col_solid, col_hint}, sel_id); + } + if (col_wire[3] > 0.0f) { + ctx->bone_buf->octahedral_outline_buf.append({bone_mat, col_wire}, sel_id); + } + + draw_points(ctx, bone, boneflag, col_solid, select_id); +} + +static void bone_draw_line(const Armatures::DrawContext *ctx, + const UnifiedBonePtr bone, + const eBone_Flag boneflag, + const int select_id) +{ + const float *col_bone = get_bone_solid_with_consts_color(ctx, bone, boneflag); + const float *col_wire = get_bone_wire_color(ctx, boneflag); + const float no_display[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + const float *col_head = no_display; + const float *col_tail = col_bone; + + if (ctx->const_color != nullptr) { + col_wire = no_display; /* actually shrink the display. */ + col_bone = col_head = col_tail = ctx->const_color; + } + else { + const UniformData &theme = ctx->res->theme; + + if (bone.is_editbone() && bone.flag() & BONE_TIPSEL) { + col_tail = &theme.colors.vert_select.x; + } + + /* Draw root point if we are not connected to our parent. */ + if (!(bone.has_parent() && (boneflag & BONE_CONNECTED))) { + + if (bone.is_editbone()) { + col_head = (bone.flag() & BONE_ROOTSEL) ? &theme.colors.vert_select.x : col_bone; + } + else { + col_head = col_bone; + } + } + } + + if (select_id == -1) { + /* Not in bone selection mode (can still be object select mode), draw everything at once. + */ + drw_shgroup_bone_stick( + ctx, bone.disp_mat(), col_wire, col_bone, col_head, col_tail, select_id); + } + else { + /* In selection mode, draw bone, root and tip separately. */ + drw_shgroup_bone_stick(ctx, + bone.disp_mat(), + col_wire, + col_bone, + no_display, + no_display, + select_id | BONESEL_BONE); + + if (col_head[3] > 0.0f) { + drw_shgroup_bone_stick(ctx, + bone.disp_mat(), + col_wire, + no_display, + col_head, + no_display, + select_id | BONESEL_ROOT); + } + + drw_shgroup_bone_stick( + ctx, bone.disp_mat(), col_wire, no_display, no_display, col_tail, select_id | BONESEL_TIP); + } +} + +static void bone_draw_b_bone(const Armatures::DrawContext *ctx, + const UnifiedBonePtr bone, + const eBone_Flag boneflag, + const int select_id) +{ + const float *col_solid = get_bone_solid_with_consts_color(ctx, bone, boneflag); + const float *col_wire = get_bone_wire_color(ctx, boneflag); + const float *col_hint = get_bone_hint_color(ctx, boneflag); + + /* NOTE: Cannot reinterpret as float4x4 because of alignment requirement of float4x4. + * This would require a deeper refactor. */ + Span bbone_matrices; + if (bone.is_posebone()) { + bbone_matrices = {(Mat4 *)bone.as_posebone()->draw_data->bbone_matrix, + bone.as_posebone()->bone->segments}; + } + else { + bbone_matrices = {(Mat4 *)bone.as_editbone()->disp_bbone_mat, bone.as_editbone()->segments}; + } + + auto sel_id = ctx->res->select_id(*ctx->ob_ref, select_id | BONESEL_BONE); + + for (const Mat4 &in_bone_mat : bbone_matrices) { + float4x4 bone_mat = ctx->ob->object_to_world() * float4x4(in_bone_mat.mat); + + if (ctx->is_filled) { + ctx->bone_buf->bbones_fill_buf.append({bone_mat, col_solid, col_hint}, sel_id); + } + if (col_wire[3] > 0.0f) { + ctx->bone_buf->bbones_outline_buf.append({bone_mat, col_wire}, sel_id); + } + } + + if (ctx->draw_mode == ARM_DRAW_MODE_EDIT) { + draw_points(ctx, bone, boneflag, col_solid, select_id); + } +} + +static void bone_draw_envelope(const Armatures::DrawContext *ctx, + const UnifiedBonePtr bone, + const eBone_Flag boneflag, + const int select_id) +{ + const float *col_solid = get_bone_solid_with_consts_color(ctx, bone, boneflag); + const float *col_wire = get_bone_wire_color(ctx, boneflag); + const float *col_hint = get_bone_hint_color(ctx, boneflag); + + const float *rad_head, *rad_tail, *distance; + if (bone.is_editbone()) { + const EditBone *eBone = bone.as_editbone(); + rad_tail = &eBone->rad_tail; + distance = &eBone->dist; + rad_head = (eBone->parent && (boneflag & BONE_CONNECTED)) ? &eBone->parent->rad_tail : + &eBone->rad_head; + } + else { + const bPoseChannel *pchan = bone.as_posebone(); + rad_tail = &pchan->bone->rad_tail; + distance = &pchan->bone->dist; + rad_head = (pchan->parent && (boneflag & BONE_CONNECTED)) ? &pchan->parent->bone->rad_tail : + &pchan->bone->rad_head; + } + + if ((select_id == -1) && (boneflag & BONE_NO_DEFORM) == 0 && + ((boneflag & BONE_SELECTED) || + (bone.is_editbone() && (boneflag & (BONE_ROOTSEL | BONE_TIPSEL))))) + { + drw_shgroup_bone_envelope_distance(ctx, bone.disp_mat(), rad_head, rad_tail, distance); + } + + drw_shgroup_bone_envelope(ctx, + bone.disp_mat(), + col_solid, + col_hint, + col_wire, + rad_head, + rad_tail, + select_id | BONESEL_BONE); + + draw_points(ctx, bone, boneflag, col_solid, select_id); +} + +static void bone_draw_wire(const Armatures::DrawContext *ctx, + const UnifiedBonePtr bone, + const eBone_Flag boneflag, + const int select_id) +{ + using namespace blender::math; + + const float *col_wire = get_bone_wire_color(ctx, boneflag); + + auto sel_id = (ctx->bone_buf) ? ctx->res->select_id(*ctx->ob_ref, select_id | BONESEL_BONE) : + draw::select::SelectMap::select_invalid_id(); + + /* NOTE: Cannot reinterpret as float4x4 because of alignment requirement of float4x4. + * This would require a deeper refactor. */ + Span bbone_matrices; + if (bone.is_posebone()) { + bbone_matrices = {(Mat4 *)bone.as_posebone()->draw_data->bbone_matrix, + bone.as_posebone()->bone->segments}; + } + else { + bbone_matrices = {(Mat4 *)bone.as_editbone()->disp_bbone_mat, bone.as_editbone()->segments}; + } + + for (const Mat4 &in_bone_mat : bbone_matrices) { + float4x4 bmat = float4x4(in_bone_mat.mat); + float3 head = transform_point(ctx->ob->object_to_world(), bmat.location()); + float3 tail = transform_point(ctx->ob->object_to_world(), bmat.location() + bmat.y_axis()); + + ctx->bone_buf->wire_buf.append(head, tail, float4(col_wire), sel_id); + } + + if (bone.is_editbone()) { + const float *col_solid = get_bone_solid_with_consts_color(ctx, bone, boneflag); + draw_points(ctx, bone, boneflag, col_solid, select_id); + } +} + +static void bone_draw(const eArmature_Drawtype drawtype, + const bool use_custom_shape, + const Armatures::DrawContext *ctx, + const UnifiedBonePtr bone, + const eBone_Flag boneflag, + const int select_id) +{ + if (use_custom_shape) { + bone_draw_custom_shape(ctx, bone, boneflag, select_id); + return; + } + + switch (drawtype) { + case ARM_OCTA: + bone_draw_octa(ctx, bone, boneflag, select_id); + break; + case ARM_LINE: + bone_draw_line(ctx, bone, boneflag, select_id); + break; + case ARM_B_BONE: + bone_draw_b_bone(ctx, bone, boneflag, select_id); + break; + case ARM_ENVELOPE: + bone_draw_envelope(ctx, bone, boneflag, select_id); + break; + case ARM_WIRE: + bone_draw_wire(ctx, bone, boneflag, select_id); + break; + default: + BLI_assert_unreachable(); + break; + } +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -1401,6 +1662,34 @@ static void draw_bone_degrees_of_freedom(const Armatures::DrawContext *ctx, /** \name Draw Relationships * \{ */ +/** Should the relationship line between this bone and its parent be drawn? */ +static bool should_draw_relation_to_parent(const UnifiedBonePtr bone, const eBone_Flag boneflag) +{ + const bool has_parent = bone.has_parent(); + + if (bone.is_editbone() && has_parent) { + /* Always draw for unconnected bones, regardless of selection, + * since riggers will want to know about the links between bones + */ + return (boneflag & BONE_CONNECTED) == 0; + } + + if (bone.is_posebone() && has_parent) { + /* Only draw between unconnected bones. */ + if (boneflag & BONE_CONNECTED) { + return false; + } + + /* Only draw if bone or its parent is selected - reduces viewport + * complexity with complex rigs */ + const bPoseChannel *pchan = bone.as_posebone(); + return (boneflag & BONE_SELECTED) || + (pchan->parent->bone && (pchan->parent->bone->flag & BONE_SELECTED)); + } + + return false; +} + static void pchan_draw_ik_lines(const Armatures::DrawContext *ctx, const bPoseChannel *pchan, const bool only_temp) @@ -1495,14 +1784,13 @@ static void draw_bone_bone_relationship_line(const Armatures::DrawContext *ctx, } static void draw_bone_relations(const Armatures::DrawContext *ctx, - const ArmatureBoneDrawStrategy &draw_strategy, const UnifiedBonePtr bone, const eBone_Flag boneflag) { if (ctx->draw_mode == ARM_DRAW_MODE_EDIT) { const EditBone *ebone = bone.as_editbone(); if (ebone->parent) { - if (ctx->do_relations && draw_strategy.should_draw_relation_to_parent(bone, boneflag)) { + if (ctx->do_relations && should_draw_relation_to_parent(bone, boneflag)) { draw_bone_bone_relationship_line( ctx, ebone->head, ebone->parent->head, ebone->parent->tail); } @@ -1511,7 +1799,7 @@ static void draw_bone_relations(const Armatures::DrawContext *ctx, else { const bPoseChannel *pchan = bone.as_posebone(); if (pchan->parent) { - if (ctx->do_relations && draw_strategy.should_draw_relation_to_parent(bone, boneflag)) { + if (ctx->do_relations && should_draw_relation_to_parent(bone, boneflag)) { draw_bone_bone_relationship_line( ctx, pchan->pose_head, pchan->parent->pose_head, pchan->parent->pose_tail); } @@ -1566,452 +1854,25 @@ static void draw_bone_name(const Armatures::DrawContext *ctx, /** \} */ -/* -------------------------------------------------------------------- */ -/** \name Bone Drawing Strategies - * - * Bone drawing uses a strategy pattern for the different armature drawing modes. - * \{ */ - -/** - * Bone drawing strategy for unknown draw types. - * This doesn't do anything, except call the default matrix update function. - */ -class ArmatureBoneDrawStrategyEmpty : public ArmatureBoneDrawStrategy { - public: - void update_display_matrix(UnifiedBonePtr bone) const override - { - draw_bone_update_disp_matrix_default(bone); - } - - void draw_context_setup(Armatures::DrawContext * /*ctx*/, - const bool /*is_filled*/, - const bool /*do_envelope_dist*/) const override - { - } - - void draw_bone(const Armatures::DrawContext * /*ctx*/, - const UnifiedBonePtr /*bone*/, - const eBone_Flag /*boneflag*/, - const int /*select_id*/) const override - { - } -}; - -/** Bone drawing strategy for custom bone shapes. */ -class ArmatureBoneDrawStrategyCustomShape : public ArmatureBoneDrawStrategy { - public: - void update_display_matrix(UnifiedBonePtr bone) const override - { - float bone_scale[3]; - float(*bone_mat)[4]; - float(*disp_mat)[4]; - float(*disp_tail_mat)[4]; - float rot_mat[3][3]; - - /* Custom bone shapes are only supported in pose mode for now. */ - bPoseChannel *pchan = bone.as_posebone(); - - /* TODO: This should be moved to depsgraph or armature refresh - * and not be tight to the draw pass creation. - * This would refresh armature without invalidating the draw cache. */ - mul_v3_v3fl(bone_scale, pchan->custom_scale_xyz, PCHAN_CUSTOM_BONE_LENGTH(pchan)); - bone_mat = pchan->custom_tx ? pchan->custom_tx->pose_mat : pchan->pose_mat; - disp_mat = bone.disp_mat(); - disp_tail_mat = pchan->disp_tail_mat; - - eulO_to_mat3(rot_mat, pchan->custom_rotation_euler, ROT_MODE_XYZ); - - copy_m4_m4(disp_mat, bone_mat); - translate_m4(disp_mat, - pchan->custom_translation[0], - pchan->custom_translation[1], - pchan->custom_translation[2]); - mul_m4_m4m3(disp_mat, disp_mat, rot_mat); - rescale_m4(disp_mat, bone_scale); - copy_m4_m4(disp_tail_mat, disp_mat); - translate_m4(disp_tail_mat, 0.0f, 1.0f, 0.0f); - } - - void draw_context_setup(Armatures::DrawContext * /*ctx*/, - const bool /*is_filled*/, - const bool /*do_envelope_dist*/) const override - { - } - - void draw_bone(const Armatures::DrawContext *ctx, - const UnifiedBonePtr bone, - const eBone_Flag boneflag, - const int select_id) const override - { - const float *col_solid = get_bone_solid_color(ctx, boneflag); - const float *col_wire = get_bone_wire_color(ctx, boneflag); - const float *col_hint = get_bone_hint_color(ctx, boneflag); - const float(*disp_mat)[4] = bone.disp_mat(); - - /* TODO(fclem): Code after this scope should be removed when we remove the legacy code. */ - auto sel_id = ctx->res->select_id(*ctx->ob_ref, select_id | BONESEL_BONE); - - /* Custom bone shapes are only supported in pose mode for now. */ - const bPoseChannel *pchan = bone.as_posebone(); - Object *custom_shape_ob = pchan->custom; - - if (custom_shape_ob->type == OB_EMPTY) { - if (custom_shape_ob->empty_drawtype != OB_EMPTY_IMAGE) { - drw_shgroup_bone_custom_empty( - ctx, disp_mat, col_wire, pchan->custom_shape_wire_width, sel_id, pchan->custom); - } - } - else if (boneflag & (BONE_DRAWWIRE | BONE_DRAW_LOCKED_WEIGHT)) { - drw_shgroup_bone_custom_wire( - ctx, disp_mat, col_wire, pchan->custom_shape_wire_width, sel_id, pchan->custom); - } - else { - drw_shgroup_bone_custom_solid(ctx, - disp_mat, - col_solid, - col_hint, - col_wire, - pchan->custom_shape_wire_width, - sel_id, - pchan->custom); - } - } -}; - -/** Bone drawing strategy for ARM_OCTA. */ -class ArmatureBoneDrawStrategyOcta : public ArmatureBoneDrawStrategy { - public: - void update_display_matrix(UnifiedBonePtr bone) const override - { - draw_bone_update_disp_matrix_default(bone); - } - - void draw_context_setup(Armatures::DrawContext * /*ctx*/, - const bool /*is_filled*/, - const bool /*do_envelope_dist*/) const override - { - } - - void draw_bone(const Armatures::DrawContext *ctx, - const UnifiedBonePtr bone, - const eBone_Flag boneflag, - const int select_id) const override - { - const float *col_solid = get_bone_solid_with_consts_color(ctx, bone, boneflag); - const float *col_wire = get_bone_wire_color(ctx, boneflag); - const float *col_hint = get_bone_hint_color(ctx, boneflag); - - auto sel_id = ctx->res->select_id(*ctx->ob_ref, select_id | BONESEL_BONE); - float4x4 bone_mat = ctx->ob->object_to_world() * float4x4(bone.disp_mat()); - - if (ctx->is_filled) { - ctx->bone_buf->octahedral_fill_buf.append({bone_mat, col_solid, col_hint}, sel_id); - } - if (col_wire[3] > 0.0f) { - ctx->bone_buf->octahedral_outline_buf.append({bone_mat, col_wire}, sel_id); - } - - draw_points(ctx, bone, boneflag, col_solid, select_id); - } -}; - -/** Bone drawing strategy for ARM_LINE. */ -class ArmatureBoneDrawStrategyLine : public ArmatureBoneDrawStrategy { - public: - void update_display_matrix(UnifiedBonePtr bone) const override - { - draw_bone_update_disp_matrix_default(bone); - } - - void draw_context_setup(Armatures::DrawContext * /*ctx*/, - const bool /*is_filled*/, - const bool /*do_envelope_dist*/) const override - { - } - - void draw_bone(const Armatures::DrawContext *ctx, - const UnifiedBonePtr bone, - const eBone_Flag boneflag, - const int select_id) const override - { - const float *col_bone = get_bone_solid_with_consts_color(ctx, bone, boneflag); - const float *col_wire = get_bone_wire_color(ctx, boneflag); - const float no_display[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - const float *col_head = no_display; - const float *col_tail = col_bone; - - if (ctx->const_color != nullptr) { - col_wire = no_display; /* actually shrink the display. */ - col_bone = col_head = col_tail = ctx->const_color; - } - else { - const UniformData &theme = ctx->res->theme; - - if (bone.is_editbone() && bone.flag() & BONE_TIPSEL) { - col_tail = &theme.colors.vert_select.x; - } - - /* Draw root point if we are not connected to our parent. */ - if (!(bone.has_parent() && (boneflag & BONE_CONNECTED))) { - - if (bone.is_editbone()) { - col_head = (bone.flag() & BONE_ROOTSEL) ? &theme.colors.vert_select.x : col_bone; - } - else { - col_head = col_bone; - } - } - } - - if (select_id == -1) { - /* Not in bone selection mode (can still be object select mode), draw everything at once. */ - drw_shgroup_bone_stick( - ctx, bone.disp_mat(), col_wire, col_bone, col_head, col_tail, select_id); - } - else { - /* In selection mode, draw bone, root and tip separately. */ - drw_shgroup_bone_stick(ctx, - bone.disp_mat(), - col_wire, - col_bone, - no_display, - no_display, - select_id | BONESEL_BONE); - - if (col_head[3] > 0.0f) { - drw_shgroup_bone_stick(ctx, - bone.disp_mat(), - col_wire, - no_display, - col_head, - no_display, - select_id | BONESEL_ROOT); - } - - drw_shgroup_bone_stick(ctx, - bone.disp_mat(), - col_wire, - no_display, - no_display, - col_tail, - select_id | BONESEL_TIP); - } - } -}; - -/** Bone drawing strategy for ARM_B_BONE. */ -class ArmatureBoneDrawStrategyBBone : public ArmatureBoneDrawStrategy { - public: - void update_display_matrix(UnifiedBonePtr bone) const override - { - draw_bone_update_disp_matrix_bbone(bone); - } - - void draw_context_setup(Armatures::DrawContext * /*ctx*/, - const bool /*is_filled*/, - const bool /*do_envelope_dist*/) const override - { - } - - void draw_bone(const Armatures::DrawContext *ctx, - const UnifiedBonePtr bone, - const eBone_Flag boneflag, - const int select_id) const override - { - const float *col_solid = get_bone_solid_with_consts_color(ctx, bone, boneflag); - const float *col_wire = get_bone_wire_color(ctx, boneflag); - const float *col_hint = get_bone_hint_color(ctx, boneflag); - - /* NOTE: Cannot reinterpret as float4x4 because of alignment requirement of float4x4. - * This would require a deeper refactor. */ - Span bbone_matrices; - if (bone.is_posebone()) { - bbone_matrices = {(Mat4 *)bone.as_posebone()->draw_data->bbone_matrix, - bone.as_posebone()->bone->segments}; - } - else { - bbone_matrices = {(Mat4 *)bone.as_editbone()->disp_bbone_mat, bone.as_editbone()->segments}; - } - - auto sel_id = ctx->res->select_id(*ctx->ob_ref, select_id | BONESEL_BONE); - - for (const Mat4 &in_bone_mat : bbone_matrices) { - float4x4 bone_mat = ctx->ob->object_to_world() * float4x4(in_bone_mat.mat); - - if (ctx->is_filled) { - ctx->bone_buf->bbones_fill_buf.append({bone_mat, col_solid, col_hint}, sel_id); - } - if (col_wire[3] > 0.0f) { - ctx->bone_buf->bbones_outline_buf.append({bone_mat, col_wire}, sel_id); - } - } - - if (ctx->draw_mode == ARM_DRAW_MODE_EDIT) { - draw_points(ctx, bone, boneflag, col_solid, select_id); - } - } -}; - -/** Bone drawing strategy for ARM_ENVELOPE. */ -class ArmatureBoneDrawStrategyEnvelope : public ArmatureBoneDrawStrategy { - public: - void update_display_matrix(UnifiedBonePtr bone) const override - { - draw_bone_update_disp_matrix_default(bone); - } - - void draw_context_setup(Armatures::DrawContext * /*ctx*/, - const bool /*is_filled*/, - const bool /*do_envelope_dist*/) const override - { - } - - void draw_bone(const Armatures::DrawContext *ctx, - const UnifiedBonePtr bone, - const eBone_Flag boneflag, - const int select_id) const override - { - const float *col_solid = get_bone_solid_with_consts_color(ctx, bone, boneflag); - const float *col_wire = get_bone_wire_color(ctx, boneflag); - const float *col_hint = get_bone_hint_color(ctx, boneflag); - - const float *rad_head, *rad_tail, *distance; - if (bone.is_editbone()) { - const EditBone *eBone = bone.as_editbone(); - rad_tail = &eBone->rad_tail; - distance = &eBone->dist; - rad_head = (eBone->parent && (boneflag & BONE_CONNECTED)) ? &eBone->parent->rad_tail : - &eBone->rad_head; - } - else { - const bPoseChannel *pchan = bone.as_posebone(); - rad_tail = &pchan->bone->rad_tail; - distance = &pchan->bone->dist; - rad_head = (pchan->parent && (boneflag & BONE_CONNECTED)) ? &pchan->parent->bone->rad_tail : - &pchan->bone->rad_head; - } - - if ((select_id == -1) && (boneflag & BONE_NO_DEFORM) == 0 && - ((boneflag & BONE_SELECTED) || - (bone.is_editbone() && (boneflag & (BONE_ROOTSEL | BONE_TIPSEL))))) - { - drw_shgroup_bone_envelope_distance(ctx, bone.disp_mat(), rad_head, rad_tail, distance); - } - - drw_shgroup_bone_envelope(ctx, - bone.disp_mat(), - col_solid, - col_hint, - col_wire, - rad_head, - rad_tail, - select_id | BONESEL_BONE); - - draw_points(ctx, bone, boneflag, col_solid, select_id); - } -}; - -/** Bone drawing strategy for ARM_WIRE. */ -class ArmatureBoneDrawStrategyWire : public ArmatureBoneDrawStrategy { - public: - void update_display_matrix(UnifiedBonePtr bone) const override - { - draw_bone_update_disp_matrix_bbone(bone); - } - - void draw_context_setup(Armatures::DrawContext *ctx, - const bool /*is_filled*/, - const bool /*do_envelope_dist*/) const override - { - ctx->const_wire = 1.5f; - } - - void draw_bone(const Armatures::DrawContext *ctx, - const UnifiedBonePtr bone, - const eBone_Flag boneflag, - const int select_id) const override - { - using namespace blender::math; - - const float *col_wire = get_bone_wire_color(ctx, boneflag); - - auto sel_id = (ctx->bone_buf) ? ctx->res->select_id(*ctx->ob_ref, select_id | BONESEL_BONE) : - draw::select::SelectMap::select_invalid_id(); - - /* NOTE: Cannot reinterpret as float4x4 because of alignment requirement of float4x4. - * This would require a deeper refactor. */ - Span bbone_matrices; - if (bone.is_posebone()) { - bbone_matrices = {(Mat4 *)bone.as_posebone()->draw_data->bbone_matrix, - bone.as_posebone()->bone->segments}; - } - else { - bbone_matrices = {(Mat4 *)bone.as_editbone()->disp_bbone_mat, bone.as_editbone()->segments}; - } - - for (const Mat4 &in_bone_mat : bbone_matrices) { - float4x4 bmat = float4x4(in_bone_mat.mat); - float3 head = transform_point(ctx->ob->object_to_world(), bmat.location()); - float3 tail = transform_point(ctx->ob->object_to_world(), bmat.location() + bmat.y_axis()); - - ctx->bone_buf->wire_buf.append(head, tail, float4(col_wire), sel_id); - } - - if (bone.is_editbone()) { - const float *col_solid = get_bone_solid_with_consts_color(ctx, bone, boneflag); - draw_points(ctx, bone, boneflag, col_solid, select_id); - } - } -}; - -namespace { -/** - * Armature drawing strategies. - * - * Declared statically here because they cost almost no memory (no fields in any - * of the structs, so just the virtual function table), and this makes it very - * simple to just pass references to them around. - * - * See the functions below. - */ -ArmatureBoneDrawStrategyOcta strat_octa; -ArmatureBoneDrawStrategyLine strat_line; -ArmatureBoneDrawStrategyBBone strat_b_bone; -ArmatureBoneDrawStrategyEnvelope strat_envelope; -ArmatureBoneDrawStrategyWire strat_wire; -ArmatureBoneDrawStrategyEmpty strat_empty; -}; // namespace - -/** - * Return the armature bone drawing strategy for the given draw type. - * - * Note that this does not consider custom bone shapes, as those can be set per bone. - * For those occasions just instance a `ArmatureBoneDrawStrategyCustomShape` and use that. - */ -static ArmatureBoneDrawStrategy &strategy_for_armature_drawtype(const eArmature_Drawtype drawtype) -{ - switch (drawtype) { - case ARM_OCTA: - return strat_octa; - case ARM_LINE: - return strat_line; - case ARM_B_BONE: - return strat_b_bone; - case ARM_ENVELOPE: - return strat_envelope; - case ARM_WIRE: - return strat_wire; - } - BLI_assert_unreachable(); - return strat_empty; -} - -/** \} */ - /* -------------------------------------------------------------------- */ /** \name Main Draw Loops * \{ */ +static void bone_draw_update_display_matrix(const eArmature_Drawtype drawtype, + const bool use_custom_shape, + UnifiedBonePtr bone) +{ + if (use_custom_shape) { + draw_bone_update_disp_matrix_custom_shape(bone); + } + else if (ELEM(drawtype, ARM_B_BONE, ARM_WIRE)) { + draw_bone_update_disp_matrix_bbone(bone); + } + else { + draw_bone_update_disp_matrix_default(bone); + } +} + void Armatures::draw_armature_edit(Armatures::DrawContext *ctx) { Object *ob = ctx->ob; @@ -2029,9 +1890,7 @@ void Armatures::draw_armature_edit(Armatures::DrawContext *ctx) edbo_compute_bbone_child(&arm); - /* Determine drawing strategy. */ - const ArmatureBoneDrawStrategy &draw_strat = strategy_for_armature_drawtype( - eArmature_Drawtype(arm.drawtype)); + const eArmature_Drawtype arm_drawtype = eArmature_Drawtype(arm.drawtype); for (eBone = static_cast(arm.edbo->first), /* Note: Selection Next handles the object id merging later. */ @@ -2064,11 +1923,14 @@ void Armatures::draw_armature_edit(Armatures::DrawContext *ctx) } if (!is_select) { - draw_bone_relations(ctx, draw_strat, bone, boneflag); + draw_bone_relations(ctx, bone, boneflag); } - draw_strat.update_display_matrix(bone); - draw_strat.draw_bone(ctx, bone, boneflag, select_id); + const eArmature_Drawtype drawtype = eBone->drawtype == ARM_BONE_DEFAULT ? + arm_drawtype : + eArmature_Drawtype(eBone->drawtype); + bone_draw_update_display_matrix(drawtype, false, bone); + bone_draw(drawtype, false, ctx, bone, boneflag, select_id); if (!is_select) { if (show_text && (arm.flag & ARM_DRAWNAMES)) { @@ -2156,9 +2018,7 @@ void Armatures::draw_armature_pose(Armatures::DrawContext *ctx) } } - const ArmatureBoneDrawStrategy &draw_strat_normal = strategy_for_armature_drawtype( - eArmature_Drawtype(arm.drawtype)); - const ArmatureBoneDrawStrategyCustomShape draw_strat_custom; + const eArmature_Drawtype arm_drawtype = eArmature_Drawtype(arm.drawtype); for (bPoseChannel *pchan = static_cast(ob->pose->chanbase.first); pchan; pchan = pchan->next, index += 0x10000) @@ -2196,14 +2056,15 @@ void Armatures::draw_armature_pose(Armatures::DrawContext *ctx) } const bool use_custom_shape = (pchan->custom) && !(arm.flag & ARM_NO_CUSTOM); - const ArmatureBoneDrawStrategy &draw_strat = use_custom_shape ? draw_strat_custom : - draw_strat_normal; if (!is_pose_select) { - draw_bone_relations(ctx, draw_strat, bone_ptr, boneflag); + draw_bone_relations(ctx, bone_ptr, boneflag); } - draw_strat.update_display_matrix(bone_ptr); - draw_strat.draw_bone(ctx, bone_ptr, boneflag, select_id); + const eArmature_Drawtype drawtype = bone->drawtype == ARM_BONE_DEFAULT ? + arm_drawtype : + eArmature_Drawtype(bone->drawtype); + bone_draw_update_display_matrix(drawtype, use_custom_shape, bone_ptr); + bone_draw(drawtype, use_custom_shape, ctx, bone_ptr, boneflag, select_id); /* Below this point nothing is used for selection queries. */ if (is_pose_select) { diff --git a/source/blender/editors/armature/armature_add.cc b/source/blender/editors/armature/armature_add.cc index 8e2a76b5435..cbe6e750271 100644 --- a/source/blender/editors/armature/armature_add.cc +++ b/source/blender/editors/armature/armature_add.cc @@ -70,6 +70,7 @@ EditBone *ED_armature_ebone_add(bArmature *arm, const char *name) BLI_addtail(arm->edbo, bone); bone->flag |= BONE_TIPSEL; + bone->drawtype = ARM_BONE_DEFAULT; bone->weight = 1.0f; bone->dist = 0.25f; bone->xwidth = 0.1f; @@ -1668,6 +1669,7 @@ static wmOperatorStatus armature_extrude_exec(bContext *C, wmOperator *op) } newbone->color = ebone->color; + newbone->drawtype = ebone->drawtype; newbone->weight = ebone->weight; newbone->dist = ebone->dist; diff --git a/source/blender/editors/armature/armature_utils.cc b/source/blender/editors/armature/armature_utils.cc index 015ff7c4ef3..e6b3873312b 100644 --- a/source/blender/editors/armature/armature_utils.cc +++ b/source/blender/editors/armature/armature_utils.cc @@ -459,6 +459,7 @@ static EditBone *make_boneList_recursive(ListBase *edbo, STRNCPY(eBone->name, curBone->name); eBone->flag = curBone->flag; eBone->inherit_scale_mode = curBone->inherit_scale_mode; + eBone->drawtype = curBone->drawtype; /* fix selection flags */ if (eBone->flag & BONE_SELECTED) { @@ -690,6 +691,7 @@ void ED_armature_from_edit(Main *bmain, bArmature *arm) newBone->flag = eBone->flag; newBone->inherit_scale_mode = eBone->inherit_scale_mode; + newBone->drawtype = eBone->drawtype; if (eBone == arm->act_edbone) { /* Don't change active selection, this messes up separate which uses diff --git a/source/blender/makesdna/DNA_armature_defaults.h b/source/blender/makesdna/DNA_armature_defaults.h index 6b1009e0a5c..996190f5bee 100644 --- a/source/blender/makesdna/DNA_armature_defaults.h +++ b/source/blender/makesdna/DNA_armature_defaults.h @@ -10,10 +10,6 @@ /* clang-format off */ -/* -------------------------------------------------------------------- */ -/** \name bArmature Struct - * \{ */ - #define _DNA_DEFAULT_bArmature \ { \ .deformflag = ARM_DEF_VGROUP | ARM_DEF_ENVELOPE, \ @@ -22,6 +18,9 @@ .drawtype = ARM_OCTA, \ } -/** \} */ +#define _DNA_DEFAULT_Bone \ + { \ + .drawtype = ARM_BONE_DEFAULT, \ + } /* clang-format on */ diff --git a/source/blender/makesdna/DNA_armature_types.h b/source/blender/makesdna/DNA_armature_types.h index 5dadee46592..61815d89523 100644 --- a/source/blender/makesdna/DNA_armature_types.h +++ b/source/blender/makesdna/DNA_armature_types.h @@ -77,8 +77,8 @@ typedef struct Bone { float bone_mat[3][3]; int flag; - - char _pad1[4]; + int8_t drawtype; /* eArmature_Drawtype */ + char _pad1[3]; BoneColor color; /* MUST be named the same as in bPoseChannel and EditBone structs. */ char inherit_scale_mode; @@ -188,7 +188,7 @@ typedef struct bArmature { char _pad0[3]; int flag; - int drawtype; + int drawtype; /* eArmature_Drawtype */ short deformflag; short pathflag; @@ -374,6 +374,7 @@ typedef enum eArmature_Flag { /* armature->drawtype */ typedef enum eArmature_Drawtype { + ARM_BONE_DEFAULT = -1, /* Use draw type from Armature (only used on Bones). */ ARM_OCTA = 0, ARM_LINE = 1, ARM_B_BONE = 2, diff --git a/source/blender/makesrna/intern/rna_armature.cc b/source/blender/makesrna/intern/rna_armature.cc index bdd28e41b74..7b07513d2f5 100644 --- a/source/blender/makesrna/intern/rna_armature.cc +++ b/source/blender/makesrna/intern/rna_armature.cc @@ -1363,6 +1363,32 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) {0, nullptr, 0, nullptr, nullptr}, }; + static const EnumPropertyItem prop_drawtype_items[] = { + {ARM_BONE_DEFAULT, + "USE_ARMATURE_SETTING", + 0, + "Use Armature Setting", + "Use display mode from armature (default)"}, + {ARM_OCTA, "OCTAHEDRAL", 0, "Octahedral", "Display bones as octahedral shape"}, + {ARM_LINE, "STICK", 0, "Stick", "Display bones as simple 2D lines with dots"}, + {ARM_B_BONE, + "BBONE", + 0, + "B-Bone", + "Display bones as boxes, showing subdivision and B-Splines"}, + {ARM_ENVELOPE, + "ENVELOPE", + 0, + "Envelope", + "Display bones as extruded spheres, showing deformation influence volume"}, + {ARM_WIRE, + "WIRE", + 0, + "Wire", + "Display bones as thin wires, showing subdivision and B-Splines"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + PropertyRNA *prop; /* strings */ @@ -1387,6 +1413,13 @@ static void rna_def_bone_common(StructRNA *srna, int editbone) RNA_def_property_pointer_funcs(prop, "rna_EditBone_color_get", nullptr, nullptr, nullptr); } + prop = RNA_def_property(srna, "display_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, nullptr, "drawtype"); + RNA_def_property_enum_items(prop, prop_drawtype_items); + RNA_def_property_ui_text(prop, "Display Type", ""); + RNA_def_property_update(prop, 0, "rna_Armature_redraw_data"); + RNA_def_property_flag(prop, PROP_LIB_EXCEPTION); + /* flags */ prop = RNA_def_property(srna, "use_connect", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, nullptr, "flag", BONE_CONNECTED); @@ -2144,6 +2177,7 @@ static void rna_def_armature(BlenderRNA *brna) "Display bones as thin wires, showing subdivision and B-Splines"}, {0, nullptr, 0, nullptr, nullptr}, }; + static const EnumPropertyItem prop_pose_position_items[] = { {0, "POSE", 0, "Pose Position", "Show armature in posed state"}, {ARM_RESTPOS,