diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index 15ebfb54f41..6c5081d1ea9 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -143,6 +143,8 @@ bool BKE_boundbox_ray_hit_check( void BKE_boundbox_calc_center_aabb(const struct BoundBox *bb, float r_cent[3]); void BKE_boundbox_calc_size_aabb(const struct BoundBox *bb, float r_size[3]); void BKE_boundbox_minmax(const struct BoundBox *bb, float obmat[4][4], float r_min[3], float r_max[3]); +struct BoundBox *BKE_boundbox_ensure_minimum_dimensions( + struct BoundBox *bb, struct BoundBox *bb_temp, const float epsilon); struct BoundBox *BKE_object_boundbox_get(struct Object *ob); void BKE_object_dimensions_get(struct Object *ob, float vec[3]); diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index fa20607ce30..526a71b477d 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -2665,6 +2665,66 @@ void BKE_boundbox_minmax(const BoundBox *bb, float obmat[4][4], float r_min[3], } } +/** + * Returns a BBox which each dimensions are at least epsilon. + * \note In case a given dimension needs to be enlarged, its final value will be in [epsilon, 3 * epsilon] range. + * + * \param bb the input bbox to check. + * \param bb_temp the temp bbox to modify (\a bb content is never changed). + * \param epsilon the minimum dimension to ensure. + * \return either bb (if nothing needed to be changed) or bb_temp. + */ +BoundBox *BKE_boundbox_ensure_minimum_dimensions(BoundBox *bb, BoundBox *bb_temp, const float epsilon) +{ + if (fabsf(bb->vec[0][0] - bb->vec[4][0]) < epsilon) { + /* Flat along X axis... */ + *bb_temp = *bb; + bb = bb_temp; + bb->vec[0][0] -= epsilon; + bb->vec[1][0] -= epsilon; + bb->vec[2][0] -= epsilon; + bb->vec[3][0] -= epsilon; + bb->vec[4][0] += epsilon; + bb->vec[5][0] += epsilon; + bb->vec[6][0] += epsilon; + bb->vec[7][0] += epsilon; + } + + if (fabsf(bb->vec[0][1] - bb->vec[3][1]) < epsilon) { + /* Flat along Y axis... */ + if (bb != bb_temp) { + *bb_temp = *bb; + bb = bb_temp; + } + bb->vec[0][1] -= epsilon; + bb->vec[1][1] -= epsilon; + bb->vec[4][1] -= epsilon; + bb->vec[5][1] -= epsilon; + bb->vec[2][1] += epsilon; + bb->vec[3][1] += epsilon; + bb->vec[6][1] += epsilon; + bb->vec[7][1] += epsilon; + } + + if (fabsf(bb->vec[0][2] - bb->vec[1][2]) < epsilon) { + /* Flat along Z axis... */ + if (bb != bb_temp) { + *bb_temp = *bb; + bb = bb_temp; + } + bb->vec[0][2] -= epsilon; + bb->vec[3][2] -= epsilon; + bb->vec[4][2] -= epsilon; + bb->vec[7][2] -= epsilon; + bb->vec[1][2] += epsilon; + bb->vec[2][2] += epsilon; + bb->vec[5][2] += epsilon; + bb->vec[6][2] += epsilon; + } + + return bb; +} + BoundBox *BKE_object_boundbox_get(Object *ob) { BoundBox *bb = NULL; diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 867e862017f..f6040732983 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -1527,8 +1527,17 @@ static bool snapDerivedMesh(short snap_mode, ARegion *ar, Object *ob, DerivedMes if (do_bb) { BoundBox *bb = BKE_object_boundbox_get(ob); - if (!BKE_boundbox_ray_hit_check(bb, ray_start_local, ray_normal_local, &len_diff)) { - return retval; + + if (bb) { + BoundBox bb_temp; + + /* We cannot aford a bbox with some null dimension, which may happen in some cases... + * Threshold is rather high, but seems to be needed to get good behavior, see T46099. */ + bb = BKE_boundbox_ensure_minimum_dimensions(bb, &bb_temp, 1e-1f); + + if (!BKE_boundbox_ray_hit_check(bb, ray_start_local, ray_normal_local, &len_diff)) { + return retval; + } } } else if (do_ray_start_correction) { @@ -2151,8 +2160,17 @@ static bool peelDerivedMesh( * test against boundbox first * */ if (looptri_num > 16) { - struct BoundBox *bb = BKE_object_boundbox_get(ob); - test = BKE_boundbox_ray_hit_check(bb, ray_start_local, ray_normal_local, NULL); + BoundBox *bb = BKE_object_boundbox_get(ob); + + if (bb) { + BoundBox bb_temp; + + /* We cannot aford a bbox with some null dimension, which may happen in some cases... + * Threshold is rather high, but seems to be needed to get good behavior, see T46099. */ + bb = BKE_boundbox_ensure_minimum_dimensions(bb, &bb_temp, 1e-1f); + + test = BKE_boundbox_ray_hit_check(bb, ray_start_local, ray_normal_local, NULL); + } } if (test == true) {