Fix #96535: Incorrect extrude direction with two faces of a cube
It was possible two quads at right-angles to each other would accumulate a "tangent" that was co-linear with the it's normal. Resolve this by calculating two tangents for each face, then using the accumulated tangent that's least co-linear with the accumulated normal.
This commit is contained in:
@@ -328,12 +328,37 @@ static int bm_vert_tri_find_unique_edge(BMVert *verts[3])
|
||||
void BM_vert_tri_calc_tangent_edge(BMVert *verts[3], float r_tangent[3])
|
||||
{
|
||||
const int index = bm_vert_tri_find_unique_edge(verts);
|
||||
const int index_next = (index + 1) % 3;
|
||||
|
||||
sub_v3_v3v3(r_tangent, verts[index]->co, verts[(index + 1) % 3]->co);
|
||||
|
||||
sub_v3_v3v3(r_tangent, verts[index]->co, verts[index_next]->co);
|
||||
normalize_v3(r_tangent);
|
||||
}
|
||||
|
||||
void BM_vert_tri_calc_tangent_pair_edge(BMVert *verts[3],
|
||||
float r_tangent_a[3],
|
||||
float r_tangent_b[3])
|
||||
{
|
||||
const int index = bm_vert_tri_find_unique_edge(verts);
|
||||
const int index_next = (index + 1) % 3;
|
||||
const int index_prev = (index_next + 1) % 3;
|
||||
|
||||
sub_v3_v3v3(r_tangent_a, verts[index]->co, verts[index_next]->co);
|
||||
normalize_v3(r_tangent_a);
|
||||
|
||||
/* Pick the adjacent loop that is least co-linear. */
|
||||
float vec_prev[3], vec_next[3];
|
||||
float tmp_prev[3], tmp_next[3];
|
||||
|
||||
sub_v3_v3v3(vec_prev, verts[index_prev]->co, verts[index]->co);
|
||||
sub_v3_v3v3(vec_next, verts[index_next]->co, verts[index_prev]->co);
|
||||
|
||||
cross_v3_v3v3(tmp_prev, r_tangent_a, vec_prev);
|
||||
cross_v3_v3v3(tmp_next, r_tangent_a, vec_next);
|
||||
|
||||
normalize_v3_v3(r_tangent_b,
|
||||
len_squared_v3(tmp_next) > len_squared_v3(tmp_prev) ? vec_next : vec_prev);
|
||||
}
|
||||
|
||||
void BM_vert_tri_calc_tangent_edge_pair(BMVert *verts[3], float r_tangent[3])
|
||||
{
|
||||
const int index = bm_vert_tri_find_unique_edge(verts);
|
||||
@@ -357,6 +382,72 @@ void BM_face_calc_tangent_edge(const BMFace *f, float r_tangent[3])
|
||||
normalize_v3(r_tangent);
|
||||
}
|
||||
|
||||
static void bm_face_calc_tangent_from_quad_edge_pair(const BMFace *f, float r_tangent[3])
|
||||
{
|
||||
BMVert *verts[4];
|
||||
float vec[3], vec_a[3], vec_b[3];
|
||||
|
||||
BM_face_as_array_vert_quad((BMFace *)f, verts);
|
||||
|
||||
sub_v3_v3v3(vec_a, verts[3]->co, verts[2]->co);
|
||||
sub_v3_v3v3(vec_b, verts[0]->co, verts[1]->co);
|
||||
add_v3_v3v3(r_tangent, vec_a, vec_b);
|
||||
|
||||
sub_v3_v3v3(vec_a, verts[0]->co, verts[3]->co);
|
||||
sub_v3_v3v3(vec_b, verts[1]->co, verts[2]->co);
|
||||
add_v3_v3v3(vec, vec_a, vec_b);
|
||||
/* use the longest edge length */
|
||||
if (len_squared_v3(r_tangent) < len_squared_v3(vec)) {
|
||||
copy_v3_v3(r_tangent, vec);
|
||||
}
|
||||
normalize_v3(r_tangent);
|
||||
}
|
||||
|
||||
static void bm_face_calc_tangent_pair_from_quad_edge_pair(const BMFace *f,
|
||||
float r_tangent_a[3],
|
||||
float r_tangent_b[3])
|
||||
{
|
||||
BLI_assert(f->len == 4);
|
||||
BMVert *verts[4];
|
||||
float vec_a[3], vec_b[3];
|
||||
|
||||
BM_face_as_array_vert_quad((BMFace *)f, verts);
|
||||
|
||||
sub_v3_v3v3(vec_a, verts[3]->co, verts[2]->co);
|
||||
sub_v3_v3v3(vec_b, verts[0]->co, verts[1]->co);
|
||||
add_v3_v3v3(r_tangent_a, vec_a, vec_b);
|
||||
|
||||
sub_v3_v3v3(vec_a, verts[0]->co, verts[3]->co);
|
||||
sub_v3_v3v3(vec_b, verts[1]->co, verts[2]->co);
|
||||
add_v3_v3v3(r_tangent_b, vec_a, vec_b);
|
||||
|
||||
/* `r_tangent_a` always gets the longest edge. */
|
||||
if (normalize_v3(r_tangent_a) < normalize_v3(r_tangent_b)) {
|
||||
swap_v3_v3(r_tangent_a, r_tangent_b);
|
||||
}
|
||||
}
|
||||
|
||||
void BM_face_calc_tangent_pair_edge(const BMFace *f, float r_tangent_a[3], float r_tangent_b[3])
|
||||
{
|
||||
const BMLoop *l_long = BM_face_find_longest_loop((BMFace *)f);
|
||||
|
||||
sub_v3_v3v3(r_tangent_a, l_long->v->co, l_long->next->v->co);
|
||||
normalize_v3(r_tangent_a);
|
||||
|
||||
/* Pick the adjacent loop that is least co-linear. */
|
||||
float vec_prev[3], vec_next[3];
|
||||
float tmp_prev[3], tmp_next[3];
|
||||
|
||||
sub_v3_v3v3(vec_prev, l_long->prev->v->co, l_long->v->co);
|
||||
sub_v3_v3v3(vec_next, l_long->next->v->co, l_long->next->next->v->co);
|
||||
|
||||
cross_v3_v3v3(tmp_prev, r_tangent_a, vec_prev);
|
||||
cross_v3_v3v3(tmp_next, r_tangent_a, vec_next);
|
||||
|
||||
normalize_v3_v3(r_tangent_b,
|
||||
len_squared_v3(tmp_next) > len_squared_v3(tmp_prev) ? vec_next : vec_prev);
|
||||
}
|
||||
|
||||
void BM_face_calc_tangent_edge_pair(const BMFace *f, float r_tangent[3])
|
||||
{
|
||||
if (f->len == 3) {
|
||||
@@ -368,23 +459,7 @@ void BM_face_calc_tangent_edge_pair(const BMFace *f, float r_tangent[3])
|
||||
}
|
||||
else if (f->len == 4) {
|
||||
/* Use longest edge pair */
|
||||
BMVert *verts[4];
|
||||
float vec[3], vec_a[3], vec_b[3];
|
||||
|
||||
BM_face_as_array_vert_quad((BMFace *)f, verts);
|
||||
|
||||
sub_v3_v3v3(vec_a, verts[3]->co, verts[2]->co);
|
||||
sub_v3_v3v3(vec_b, verts[0]->co, verts[1]->co);
|
||||
add_v3_v3v3(r_tangent, vec_a, vec_b);
|
||||
|
||||
sub_v3_v3v3(vec_a, verts[0]->co, verts[3]->co);
|
||||
sub_v3_v3v3(vec_b, verts[1]->co, verts[2]->co);
|
||||
add_v3_v3v3(vec, vec_a, vec_b);
|
||||
/* use the longest edge length */
|
||||
if (len_squared_v3(r_tangent) < len_squared_v3(vec)) {
|
||||
copy_v3_v3(r_tangent, vec);
|
||||
}
|
||||
normalize_v3(r_tangent);
|
||||
BM_face_calc_tangent_edge(f, r_tangent);
|
||||
}
|
||||
else {
|
||||
/* For ngons use two longest disconnected edges */
|
||||
@@ -488,7 +563,7 @@ void BM_face_calc_tangent_auto(const BMFace *f, float r_tangent[3])
|
||||
}
|
||||
else if (f->len == 4) {
|
||||
/* longest edge pair of a quad */
|
||||
BM_face_calc_tangent_edge_pair(f, r_tangent);
|
||||
bm_face_calc_tangent_from_quad_edge_pair(f, r_tangent);
|
||||
}
|
||||
else {
|
||||
/* longest edge of an ngon */
|
||||
@@ -496,6 +571,24 @@ void BM_face_calc_tangent_auto(const BMFace *f, float r_tangent[3])
|
||||
}
|
||||
}
|
||||
|
||||
void BM_face_calc_tangent_pair_auto(const BMFace *f, float r_tangent_a[3], float r_tangent_b[3])
|
||||
{
|
||||
if (f->len == 3) {
|
||||
/* most 'unique' edge of a triangle */
|
||||
BMVert *verts[3];
|
||||
BM_face_as_array_vert_tri((BMFace *)f, verts);
|
||||
BM_vert_tri_calc_tangent_pair_edge(verts, r_tangent_a, r_tangent_b);
|
||||
}
|
||||
else if (f->len == 4) {
|
||||
/* longest edge pair of a quad */
|
||||
bm_face_calc_tangent_pair_from_quad_edge_pair(f, r_tangent_a, r_tangent_b);
|
||||
}
|
||||
else {
|
||||
/* longest edge of an ngon */
|
||||
BM_face_calc_tangent_pair_edge(f, r_tangent_a, r_tangent_b);
|
||||
}
|
||||
}
|
||||
|
||||
void BM_face_calc_bounds_expand(const BMFace *f, float min[3], float max[3])
|
||||
{
|
||||
const BMLoop *l_iter, *l_first;
|
||||
|
||||
@@ -98,6 +98,8 @@ float BM_face_calc_perimeter_with_mat3(const BMFace *f,
|
||||
* Compute the tangent of the face, using the longest edge.
|
||||
*/
|
||||
void BM_face_calc_tangent_edge(const BMFace *f, float r_tangent[3]) ATTR_NONNULL();
|
||||
void BM_face_calc_tangent_pair_edge(const BMFace *f, float r_tangent_a[3], float r_tangent_b[3]);
|
||||
|
||||
/**
|
||||
* Compute the tangent of the face, using the two longest disconnected edges.
|
||||
*
|
||||
@@ -122,6 +124,14 @@ void BM_face_calc_tangent_vert_diagonal(const BMFace *f, float r_tangent[3]) ATT
|
||||
* \note Callers shouldn't depend on the *exact* method used here.
|
||||
*/
|
||||
void BM_face_calc_tangent_auto(const BMFace *f, float r_tangent[3]) ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* A version of BM_face_calc_tangent_auto that calculates two tangents.
|
||||
* Useful when one may not be usable.
|
||||
*/
|
||||
void BM_face_calc_tangent_pair_auto(const BMFace *f, float r_tangent_a[3], float r_tangent_b[3])
|
||||
ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* computes center of face in 3d. uses center of bounding box.
|
||||
*/
|
||||
@@ -280,6 +290,9 @@ void BM_face_as_array_loop_quad(BMFace *f, BMLoop *r_loops[4]) ATTR_NONNULL();
|
||||
* \param r_tangent: Calculated unit length tangent (return value).
|
||||
*/
|
||||
void BM_vert_tri_calc_tangent_edge(BMVert *verts[3], float r_tangent[3]);
|
||||
void BM_vert_tri_calc_tangent_pair_edge(BMVert *verts[3],
|
||||
float r_tangent_a[3],
|
||||
float r_tangent_b[3]);
|
||||
/**
|
||||
* Calculate a tangent from any 3 vertices,
|
||||
*
|
||||
|
||||
@@ -951,7 +951,6 @@ int getTransformOrientation_ex(const Scene *scene,
|
||||
if (ob->type == OB_MESH) {
|
||||
BMEditMesh *em = BKE_editmesh_from_object(ob);
|
||||
BMEditSelection ese;
|
||||
float vec[3] = {0, 0, 0};
|
||||
|
||||
/* Use last selected with active. */
|
||||
if (activeOnly && BM_select_history_active_get(em->bm, &ese)) {
|
||||
@@ -975,14 +974,42 @@ int getTransformOrientation_ex(const Scene *scene,
|
||||
BMFace *efa;
|
||||
BMIter iter;
|
||||
|
||||
float normal[3] = {0.0f};
|
||||
float plane_pair[2][3] = {{0.0f}};
|
||||
|
||||
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
|
||||
if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
|
||||
BM_face_calc_tangent_auto(efa, vec);
|
||||
add_v3_v3(r_normal, efa->no);
|
||||
add_v3_v3(r_plane, vec);
|
||||
float tangent_pair[2][3];
|
||||
BM_face_calc_tangent_pair_auto(efa, tangent_pair[0], tangent_pair[1]);
|
||||
add_v3_v3(normal, efa->no);
|
||||
add_v3_v3(plane_pair[0], tangent_pair[0]);
|
||||
add_v3_v3(plane_pair[1], tangent_pair[1]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Pick the best plane (least likely to be co-linear),
|
||||
* since this can result in failure to construct a usable matrix, see: #96535. */
|
||||
int plane_index;
|
||||
{
|
||||
float normal_unit[3];
|
||||
float plane_unit_pair[2][3], plane_ortho_pair[2][3];
|
||||
|
||||
normalize_v3_v3(normal_unit, normal);
|
||||
normalize_v3_v3(plane_unit_pair[0], plane_pair[0]);
|
||||
normalize_v3_v3(plane_unit_pair[1], plane_pair[1]);
|
||||
|
||||
cross_v3_v3v3(plane_ortho_pair[0], normal_unit, plane_unit_pair[0]);
|
||||
cross_v3_v3v3(plane_ortho_pair[1], normal_unit, plane_unit_pair[1]);
|
||||
|
||||
plane_index = (len_squared_v3(plane_ortho_pair[0]) >
|
||||
len_squared_v3(plane_ortho_pair[1])) ?
|
||||
0 :
|
||||
1;
|
||||
}
|
||||
|
||||
add_v3_v3(r_normal, normal);
|
||||
add_v3_v3(r_plane, plane_pair[plane_index]);
|
||||
|
||||
result = ORIENTATION_FACE;
|
||||
}
|
||||
else if (em->bm->totvertsel == 3) {
|
||||
|
||||
Reference in New Issue
Block a user