From 7fb8e475d9eca93bff87f011b29ac8aa7bc0ebca Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 7 Jul 2022 14:57:43 +1000 Subject: Fix T99270: bones using empties as custom shapes can't be selected Regression in [0] which didn't account for the bounds of empty objects. Add support support calculating bounds from empty draw-type to use in pose-bone culling. [0]: 3267c91b4d5caab7da8aef071a446dd2e86f86a9 --- source/blender/blenkernel/BKE_armature.h | 4 ++ source/blender/blenkernel/BKE_object.h | 4 ++ source/blender/blenkernel/intern/armature.c | 27 +++++++-- source/blender/blenkernel/intern/object.cc | 64 ++++++++++++++++++++++ .../draw/engines/overlay/overlay_armature.c | 2 +- 5 files changed, 95 insertions(+), 6 deletions(-) diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h index 888de38dcc7..98f9f8e3588 100644 --- a/source/blender/blenkernel/BKE_armature.h +++ b/source/blender/blenkernel/BKE_armature.h @@ -158,9 +158,13 @@ struct BoundBox *BKE_armature_boundbox_get(struct Object *ob); * or the custom object's bounds (if the bone uses a custom object). * Visual elements such as the envelopes radius & bendy-bone spline segments are *not* included, * making this not so useful for viewport culling. + * + * \param use_empty_drawtype: When enabled, the draw type of empty custom-objects is tagen into + * account when calculating the bounds. */ void BKE_pchan_minmax(const struct Object *ob, const struct bPoseChannel *pchan, + const bool use_empty_drawtype, float r_min[3], float r_max[3]); /** diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index faf878dfc2a..6ed05bc267a 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -369,6 +369,9 @@ void BKE_object_empty_draw_type_set(struct Object *ob, int value); void BKE_object_boundbox_calc_from_mesh(struct Object *ob, const struct Mesh *me_eval); bool BKE_object_boundbox_calc_from_evaluated_geometry(struct Object *ob); +/** + * Calculate visual bounds from an empty objects draw-type. + */ void BKE_object_minmax(struct Object *ob, float r_min[3], float r_max[3], bool use_hidden); bool BKE_object_minmax_dupli(struct Depsgraph *depsgraph, struct Scene *scene, @@ -376,6 +379,7 @@ bool BKE_object_minmax_dupli(struct Depsgraph *depsgraph, float r_min[3], float r_max[3], bool use_hidden); +bool BKE_object_minmax_empty_drawtype(const struct Object *ob, float r_min[3], float r_max[3]); /** * Sometimes min-max isn't enough, we need to loop over each point. diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index dfe3d9cc310..622ecde6a91 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -2661,13 +2661,30 @@ BoundBox *BKE_armature_boundbox_get(Object *ob) return ob->runtime.bb; } -void BKE_pchan_minmax(const Object *ob, const bPoseChannel *pchan, float r_min[3], float r_max[3]) +void BKE_pchan_minmax(const Object *ob, + const bPoseChannel *pchan, + const bool use_empty_drawtype, + float r_min[3], + float r_max[3]) { const bArmature *arm = ob->data; const bPoseChannel *pchan_tx = (pchan->custom && pchan->custom_tx) ? pchan->custom_tx : pchan; - const BoundBox *bb_custom = ((pchan->custom) && !(arm->flag & ARM_NO_CUSTOM)) ? - BKE_object_boundbox_get(pchan->custom) : - NULL; + const BoundBox *bb_custom = NULL; + BoundBox bb_custom_buf; + + if ((pchan->custom) && !(arm->flag & ARM_NO_CUSTOM)) { + float min[3], max[3]; + if (use_empty_drawtype && (pchan->custom->type == OB_EMPTY) && + BKE_object_minmax_empty_drawtype(pchan->custom, min, max)) { + memset(&bb_custom_buf, 0x0, sizeof(bb_custom_buf)); + BKE_boundbox_init_from_minmax(&bb_custom_buf, min, max); + bb_custom = &bb_custom_buf; + } + else { + bb_custom = BKE_object_boundbox_get(pchan->custom); + } + } + if (bb_custom) { float mat[4][4], smat[4][4], rmat[4][4], tmp[4][4]; scale_m4_fl(smat, PCHAN_CUSTOM_BONE_LENGTH(pchan)); @@ -2704,7 +2721,7 @@ bool BKE_pose_minmax(Object *ob, float r_min[3], float r_max[3], bool use_hidden if (pchan->bone && (!((use_hidden == false) && (PBONE_VISIBLE(arm, pchan->bone) == false)) && !((use_select == true) && ((pchan->bone->flag & BONE_SELECTED) == 0)))) { - BKE_pchan_minmax(ob, pchan, r_min, r_max); + BKE_pchan_minmax(ob, pchan, false, r_min, r_max); changed = true; } } diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index 0bc092bec4f..d3301f46559 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -3990,6 +3990,70 @@ bool BKE_object_empty_image_data_is_visible_in_view3d(const Object *ob, const Re return true; } +bool BKE_object_minmax_empty_drawtype(const struct Object *ob, float r_min[3], float r_max[3]) +{ + BLI_assert(ob->type == OB_EMPTY); + float3 min(0), max(0); + + bool ok = false; + const float radius = ob->empty_drawsize; + + switch (ob->empty_drawtype) { + case OB_ARROWS: { + max = float3(radius); + ok = true; + break; + } + case OB_PLAINAXES: + case OB_CUBE: + case OB_EMPTY_SPHERE: { + min = float3(-radius); + max = float3(radius); + ok = true; + break; + } + case OB_CIRCLE: { + max[0] = max[2] = radius; + min[0] = min[2] = -radius; + ok = true; + break; + } + case OB_SINGLE_ARROW: { + max[2] = radius; + ok = true; + break; + } + case OB_EMPTY_CONE: { + min = float3(-radius, 0.0f, -radius); + max = float3(radius, radius * 2.0f, radius); + ok = true; + break; + } + case OB_EMPTY_IMAGE: { + const float *ofs = ob->ima_ofs; + /* NOTE: this is the best approximation that can be calculated without loading the image. */ + min[0] = ofs[0] * radius; + min[1] = ofs[1] * radius; + max[0] = radius + (ofs[0] * radius); + max[1] = radius + (ofs[1] * radius); + /* Since the image aspect can shrink the bounds towards the object origin, + * adjust the min/max to account for that. */ + for (int i = 0; i < 2; i++) { + CLAMP_MAX(min[i], 0.0f); + CLAMP_MIN(max[i], 0.0f); + } + ok = true; + break; + } + } + + if (ok) { + copy_v3_v3(r_min, min); + copy_v3_v3(r_max, max); + } + return ok; +} + bool BKE_object_minmax_dupli(Depsgraph *depsgraph, Scene *scene, Object *ob, diff --git a/source/blender/draw/engines/overlay/overlay_armature.c b/source/blender/draw/engines/overlay/overlay_armature.c index ea0c2f287a6..e38695c76ab 100644 --- a/source/blender/draw/engines/overlay/overlay_armature.c +++ b/source/blender/draw/engines/overlay/overlay_armature.c @@ -2102,7 +2102,7 @@ static void pchan_culling_calc_bsphere(const Object *ob, { float min[3], max[3]; INIT_MINMAX(min, max); - BKE_pchan_minmax(ob, pchan, min, max); + BKE_pchan_minmax(ob, pchan, true, min, max); mid_v3_v3v3(r_bsphere->center, min, max); r_bsphere->radius = len_v3v3(min, r_bsphere->center); } -- cgit v1.2.3