Fix: errors in the Edge Slide computed direction

The Edge Slide code was practically redone to correct errors and to
enable this operator to become a generic transform operator.

The errors are;
1. Unpredictable slide direction when the vertex "slides" over multiple
    edges.
2. Incorrect direction when the sliding edge has disconnected faces.

The fixes were:
- When the sliding direction splits into multiple directions, the
  chosen target is now the intersection of the destination edge lines
  (instead of the average point or a randon point)
- If the edge faces disconnect, the vertex slides in different
  directions on each face.

Other changes:
- Use C++ for Array e Vector types.
- Create methods to access or edit struct parameters.
- Use `TransData` to access the initial position of the vertex.
- Acess slide vertice by reference.
This commit is contained in:
Germano Cavalcante
2024-02-22 01:31:30 -03:00
parent 1dd57369f2
commit 7f1b70ac50
3 changed files with 446 additions and 726 deletions

View File

@@ -48,11 +48,15 @@ struct TransConvertTypeInfo {
* Structure used for Edge Slide operation.
*/
struct TransDataEdgeSlideVert {
struct BMVert *v;
blender::float3 v_co_orig;
blender::float3 dir_side[2];
float edge_len;
int loop_nr;
TransData *td;
blender::float3 dir_side[2]; /* Directional vectors on the sides.*/
float edge_len; /* Distance between vectors. */
int loop_nr; /* Number that identifies the group of connected edges. */
const float *v_co_orig() const
{
return this->td->iloc;
}
};
/**
@@ -273,10 +277,8 @@ void transform_convert_mesh_crazyspace_free(TransMeshDataCrazySpace *r_crazyspac
blender::Array<TransDataVertSlideVert> transform_mesh_vert_slide_data_create(
const TransDataContainer *tc, blender::Vector<blender::float3> &r_loc_dst_buffer);
TransDataEdgeSlideVert *transform_mesh_edge_slide_data_create(const TransDataContainer *tc,
const bool use_double_side,
int *r_group_len,
int *r_sv_len);
blender::Array<TransDataEdgeSlideVert> transform_mesh_edge_slide_data_create(
const TransDataContainer *tc, int *r_group_len);
/* `transform_convert_mesh_edge.cc` */

View File

@@ -2231,25 +2231,16 @@ Array<TransDataVertSlideVert> transform_mesh_vert_slide_data_create(
/** \name API for Edge Slide
* \{ */
static BMEdge *get_other_edge(BMVert *v, BMEdge *e)
static bool mesh_vert_is_inner(BMVert *v)
{
BMIter iter;
BMEdge *e_iter;
BM_ITER_ELEM (e_iter, &iter, v, BM_EDGES_OF_VERT) {
if (BM_elem_flag_test(e_iter, BM_ELEM_SELECT) && e_iter != e) {
return e_iter;
}
}
return nullptr;
return BM_vert_is_edge_pair(v) && !BM_vert_is_boundary(v);
}
/**
* Find the closest point on the ngon on the opposite side.
* used to set the edge slide distance for ngons.
*/
static bool bm_loop_calc_opposite_co(BMLoop *l_tmp, const float plane_no[3], float r_co[3])
static bool bm_loop_calc_opposite_co(const BMLoop *l_tmp, const float plane_no[3], float r_co[3])
{
/* skip adjacent edges */
BMLoop *l_first = l_tmp->next;
@@ -2281,616 +2272,321 @@ static bool bm_loop_calc_opposite_co(BMLoop *l_tmp, const float plane_no[3], flo
return found;
}
/**
* Given 2 edges and a loop, step over the loops
* and calculate a direction to slide along.
*
* \param r_slide_vec: the direction to slide,
* the length of the vector defines the slide distance.
*/
static BMLoop *get_next_loop(
BMVert *v, BMLoop *l, BMEdge *e_prev, BMEdge *e_next, float r_slide_vec[3])
static float3 isect_face_dst(const BMLoop *l)
{
BMLoop *l_first;
float vec_accum[3] = {0.0f, 0.0f, 0.0f};
float vec_accum_len = 0.0f;
int i = 0;
BLI_assert(BM_edge_share_vert(e_prev, e_next) == v);
BLI_assert(BM_vert_in_edge(l->e, v));
l_first = l;
do {
l = BM_loop_other_edge_loop(l, v);
if (l->e == e_next) {
if (i) {
normalize_v3_length(vec_accum, vec_accum_len / float(i));
}
else {
/* When there is no edge to slide along,
* we must slide along the vector defined by the face we're attach to */
BMLoop *l_tmp = BM_face_vert_share_loop(l_first->f, v);
BLI_assert(ELEM(l_tmp->e, e_prev, e_next) && ELEM(l_tmp->prev->e, e_prev, e_next));
if (l_tmp->f->len == 4) {
/* we could use code below, but in this case
* sliding diagonally across the quad works well */
sub_v3_v3v3(vec_accum, l_tmp->next->next->v->co, v->co);
}
else {
float tdir[3];
BM_loop_calc_face_direction(l_tmp, tdir);
cross_v3_v3v3(vec_accum, l_tmp->f->no, tdir);
#if 0
/* Rough guess, we can do better! */
normalize_v3_length(vec_accum,
(BM_edge_calc_length(e_prev) + BM_edge_calc_length(e_next)) / 2.0f);
#else
/* be clever, check the opposite ngon edge to slide into.
* this gives best results */
{
float tvec[3];
float dist;
if (bm_loop_calc_opposite_co(l_tmp, tdir, tvec)) {
dist = len_v3v3(l_tmp->v->co, tvec);
}
else {
dist = (BM_edge_calc_length(e_prev) + BM_edge_calc_length(e_next)) / 2.0f;
}
normalize_v3_length(vec_accum, dist);
}
#endif
}
}
copy_v3_v3(r_slide_vec, vec_accum);
return l;
}
/* accumulate the normalized edge vector,
* normalize so some edges don't skew the result */
float tvec[3];
sub_v3_v3v3(tvec, BM_edge_other_vert(l->e, v)->co, v->co);
vec_accum_len += normalize_v3(tvec);
add_v3_v3(vec_accum, tvec);
i += 1;
if (BM_loop_other_edge_loop(l, v)->e == e_next) {
if (i) {
normalize_v3_length(vec_accum, vec_accum_len / float(i));
}
copy_v3_v3(r_slide_vec, vec_accum);
return BM_loop_other_edge_loop(l, v);
}
} while ((l != l->radial_next) && ((l = l->radial_next) != l_first));
if (i) {
normalize_v3_length(vec_accum, vec_accum_len / float(i));
BMFace *f = l->f;
BMLoop *l_next = l->next;
if (f->len == 4) {
/* we could use code below, but in this case
* sliding diagonally across the quad works well */
return l_next->next->v->co;
}
copy_v3_v3(r_slide_vec, vec_accum);
float3 plane_no;
BM_loop_calc_face_direction(l, plane_no);
return nullptr;
float3 isect_co;
if (!bm_loop_calc_opposite_co(l, plane_no, isect_co)) {
/* Rare case. */
mid_v3_v3v3(isect_co, l->prev->v->co, l_next->v->co);
}
return isect_co;
}
static TransDataEdgeSlideVert *createEdgeSlideVerts_double_side(const TransDataContainer *tc,
int *r_group_len,
int *r_sv_len)
Array<TransDataEdgeSlideVert> transform_mesh_edge_slide_data_create(const TransDataContainer *tc,
int *r_group_len)
{
BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
BMesh *bm = em->bm;
BMIter iter;
BMEdge *e;
BMVert *v;
TransDataEdgeSlideVert *sv_array;
int sv_tot;
int *sv_table; /* BMVert -> sv_array index */
int numsel, i, loop_nr;
int td_selected_len = 0;
/* Ensure valid selection. */
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
BMIter iter2;
numsel = 0;
BM_ITER_ELEM (e, &iter2, v, BM_EDGES_OF_VERT) {
if (BM_elem_flag_test(e, BM_ELEM_SELECT)) {
/* BMESH_TODO: this is probably very evil,
* set `v->e` to a selected edge. */
v->e = e;
numsel++;
}
}
if (numsel == 0 || numsel > 2) {
/* Invalid edge selection. */
return nullptr;
}
}
}
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
if (BM_elem_flag_test(e, BM_ELEM_SELECT)) {
/* NOTE: any edge with loops can work, but we won't get predictable results, so bail out. */
if (!BM_edge_is_manifold(e) && !BM_edge_is_boundary(e)) {
/* can edges with at least once face user */
return nullptr;
}
}
}
sv_table = static_cast<int *>(MEM_mallocN(sizeof(*sv_table) * bm->totvert, __func__));
#define INDEX_UNSET -1
#define INDEX_INVALID -2
{
int j = 0;
BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
BM_elem_flag_enable(v, BM_ELEM_TAG);
sv_table[i] = INDEX_UNSET;
j += 1;
}
else {
BM_elem_flag_disable(v, BM_ELEM_TAG);
sv_table[i] = INDEX_INVALID;
}
BM_elem_index_set(v, i); /* set_inline */
}
bm->elem_index_dirty &= ~BM_VERT;
if (!j) {
MEM_freeN(sv_table);
return nullptr;
}
sv_tot = j;
}
sv_array = static_cast<TransDataEdgeSlideVert *>(
MEM_callocN(sizeof(TransDataEdgeSlideVert) * sv_tot, "sv_array"));
loop_nr = 0;
STACK_DECLARE(sv_array);
STACK_INIT(sv_array, sv_tot);
while (true) {
float vec_a[3], vec_b[3];
BMLoop *l_a, *l_b;
BMLoop *l_a_prev, *l_b_prev;
BMVert *v_first;
/* If this succeeds call get_next_loop()
* which calculates the direction to slide based on clever checks.
*
* otherwise we simply use 'e_dir' as an edge-rail.
* (which is better when the attached edge is a boundary, see: #40422)
*/
#define EDGESLIDE_VERT_IS_INNER(v, e_dir) \
\
((BM_edge_is_boundary(e_dir) == false) && (BM_vert_edge_count_nonwire(v) == 2))
v = nullptr;
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
break;
}
}
if (!v) {
BMIter iter;
BMVert *v;
TransData *td = tc->data;
for (int i = 0; i < tc->data_len; i++, td++) {
if (!(td->flag & TD_SELECTED)) {
/* The selected ones are sorted at the beginning. */
break;
}
v = static_cast<BMVert *>(td->extra);
int numsel = BM_iter_elem_count_flag(BM_EDGES_OF_VERT, v, BM_ELEM_SELECT, true);
if (numsel == 0 || numsel > 2) {
/* Invalid edge selection. */
return {};
}
td_selected_len++;
}
if (!v->e) {
BMEdge *e;
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
if (!BM_elem_flag_test(e, BM_ELEM_SELECT)) {
continue;
}
if (!BM_edge_is_manifold(e) && !BM_edge_is_boundary(e)) {
/* Can edges with at least once face user. */
return {};
}
}
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
BM_elem_index_set(v, -1);
}
bm->elem_index_dirty |= BM_VERT;
/* Alloc and initialize the #TransDataEdgeSlideVert. */
Array<TransDataEdgeSlideVert> r_sv(td_selected_len);
TransDataEdgeSlideVert *sv = &r_sv[0];
int sv_index = 0;
td = tc->data;
for (int i = 0; i < tc->data_len; i++, td++) {
if (!(td->flag & TD_SELECTED)) {
continue;
}
sv->td = td;
sv->loop_nr = -1;
sv->dir_side[1] = float3(0);
/* Identify the #TransDataEdgeSlideVert by the vertex index. */
v = static_cast<BMVert *>(td->extra);
BM_elem_index_set(v, sv_index);
sv_index++;
sv++;
}
/* Map indicating the indexes of #TransData connected by edge. */
Array<int2> td_connected(tc->data_len, int2(-1, -1));
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
if (!BM_elem_flag_test(e, BM_ELEM_SELECT)) {
continue;
}
int td_index_1 = BM_elem_index_get(e->v1);
int td_index_2 = BM_elem_index_get(e->v2);
int slot_1 = int(td_connected[td_index_1][0] != -1);
int slot_2 = int(td_connected[td_index_2][0] != -1);
td_connected[td_index_1][slot_1] = td_index_2;
td_connected[td_index_2][slot_2] = td_index_1;
}
/* Compute the sliding groups. */
int loop_nr = 0;
for (int i : r_sv.index_range()) {
TransDataEdgeSlideVert *sv = &r_sv[i];
if (sv->loop_nr != -1) {
/* This vertex has already been computed. */
continue;
}
v_first = v;
/* Walk along the edge loop. */
e = v->e;
/* First, rewind. */
do {
e = get_other_edge(v, e);
if (!e) {
e = v->e;
break;
}
if (!BM_elem_flag_test(BM_edge_other_vert(e, v), BM_ELEM_TAG)) {
break;
}
v = BM_edge_other_vert(e, v);
} while (e != v_first->e);
BM_elem_flag_disable(v, BM_ELEM_TAG);
l_a = e->l;
l_b = e->l->radial_next;
/* regarding e_next, use get_next_loop()'s improved interpolation where possible */
{
BMEdge *e_next = get_other_edge(v, e);
if (e_next) {
get_next_loop(v, l_a, e, e_next, vec_a);
}
else {
BMLoop *l_tmp = BM_loop_other_edge_loop(l_a, v);
if (EDGESLIDE_VERT_IS_INNER(v, l_tmp->e)) {
get_next_loop(v, l_a, e, l_tmp->e, vec_a);
}
else {
sub_v3_v3v3(vec_a, BM_edge_other_vert(l_tmp->e, v)->co, v->co);
}
}
/* Start from a vertex connected to just a single edge or any if it doesn't exist. */
int i_curr = i;
int i_prev = td_connected[i][1];
while (!ELEM(i_prev, -1, i)) {
int tmp = td_connected[i_prev][0] != i_curr ? td_connected[i_prev][0] :
td_connected[i_prev][1];
i_curr = i_prev;
i_prev = tmp;
}
/* Equivalent to `!BM_edge_is_boundary(e)`. */
if (l_b != l_a) {
BMEdge *e_next = get_other_edge(v, e);
if (e_next) {
get_next_loop(v, l_b, e, e_next, vec_b);
}
else {
BMLoop *l_tmp = BM_loop_other_edge_loop(l_b, v);
if (EDGESLIDE_VERT_IS_INNER(v, l_tmp->e)) {
get_next_loop(v, l_b, e, l_tmp->e, vec_b);
}
else {
sub_v3_v3v3(vec_b, BM_edge_other_vert(l_tmp->e, v)->co, v->co);
}
}
}
else {
l_b = nullptr;
}
l_a_prev = nullptr;
l_b_prev = nullptr;
#define SV_FROM_VERT(v) \
\
((sv_table[BM_elem_index_get(v)] == INDEX_UNSET) ? \
((void)(sv_table[BM_elem_index_get(v)] = STACK_SIZE(sv_array)), \
\
STACK_PUSH_RET_PTR(sv_array)) : \
\
(&sv_array[sv_table[BM_elem_index_get(v)]]))
/* Iterate over the loop. */
v_first = v;
do {
bool l_a_ok_prev;
bool l_b_ok_prev;
/**
* We need at least 3 points to calculate the intersection of
* `prev`-`curr` and `next`-`curr` destinations.
* `next_next` is only required to identify the edge in `next.e`.
*
* | | | |
* | prev.e | curr.e | next.e |
* prev.v ---- curr.v ---- next.v ---- next_next.v
*/
struct {
int i; /* The #TransDataEdgeSlideVert index. */
TransDataEdgeSlideVert *sv;
BMVert *v_prev;
BMEdge *e_prev;
BMVert *v;
BMEdge *e;
struct {
BMFace *f;
BMVert *v_dst;
float3 dst;
} fdata[2];
bool vert_is_edge_pair;
} prev = {}, curr = {}, next = {}, next_next = {};
/* XXX, 'sv' will initialize multiple times, this is suspicious. see #34024. */
BLI_assert(v != nullptr);
BLI_assert(sv_table[BM_elem_index_get(v)] != INDEX_INVALID);
sv = SV_FROM_VERT(v);
sv->v = v;
copy_v3_v3(sv->v_co_orig, v->co);
sv->loop_nr = loop_nr;
next.i = td_connected[i_curr][0] != i_prev ? td_connected[i_curr][0] : td_connected[i_curr][1];
next.sv = &r_sv[next.i];
next.v = static_cast<BMVert *>(next.sv->td->extra);
next.vert_is_edge_pair = mesh_vert_is_inner(next.v);
if (l_a || l_a_prev) {
copy_v3_v3(sv->dir_side[0], vec_a);
}
curr.i = i_curr;
curr.sv = &r_sv[curr.i];
curr.v = static_cast<BMVert *>(curr.sv->td->extra);
curr.vert_is_edge_pair = mesh_vert_is_inner(curr.v);
curr.e = BM_edge_exists(curr.v, next.v);
if (l_b || l_b_prev) {
copy_v3_v3(sv->dir_side[1], vec_b);
}
/* Do not compute `prev` for now. Let the loop calculate `curr` twice. */
prev.i = -1;
v_prev = v;
v = BM_edge_other_vert(e, v);
e_prev = e;
e = get_other_edge(v, e);
if (!e) {
BLI_assert(v != nullptr);
BLI_assert(sv_table[BM_elem_index_get(v)] != INDEX_INVALID);
sv = SV_FROM_VERT(v);
sv->v = v;
copy_v3_v3(sv->v_co_orig, v->co);
sv->loop_nr = loop_nr;
if (l_a) {
BMLoop *l_tmp = BM_loop_other_edge_loop(l_a, v);
BMVert *v_other = BM_edge_other_vert(l_tmp->e, v);
if (EDGESLIDE_VERT_IS_INNER(v, l_tmp->e)) {
get_next_loop(v, l_a, e_prev, l_tmp->e, sv->dir_side[0]);
}
else {
sub_v3_v3v3(sv->dir_side[0], v_other->co, v->co);
}
while (curr.i != -1) {
if (next.i != -1) {
next_next.i = td_connected[next.i][0] != curr.i ? td_connected[next.i][0] :
td_connected[next.i][1];
if (next_next.i != -1) {
next_next.sv = &r_sv[next_next.i];
next_next.v = static_cast<BMVert *>(next_next.sv->td->extra);
next_next.vert_is_edge_pair = mesh_vert_is_inner(next_next.v);
next.e = BM_edge_exists(next.v, next_next.v);
}
if (l_b) {
BMLoop *l_tmp = BM_loop_other_edge_loop(l_b, v);
BMVert *v_other = BM_edge_other_vert(l_tmp->e, v);
if (EDGESLIDE_VERT_IS_INNER(v, l_tmp->e)) {
get_next_loop(v, l_b, e_prev, l_tmp->e, sv->dir_side[1]);
BMLoop *l;
BM_ITER_ELEM (l, &iter, curr.e, BM_LOOPS_OF_EDGE) {
BMFace *f_curr = l->f;
BMVert *v1_dst, *v2_dst;
BMEdge *l_edge_next;
BMLoop *l1_slide, *l1, *l2;
if (l->v == curr.v) {
l1 = l;
l1_slide = l->prev;
l2 = l->next;
l_edge_next = l2->e;
v1_dst = l1_slide->v;
v2_dst = l2->next->v;
}
else {
sub_v3_v3v3(sv->dir_side[1], v_other->co, v->co);
l1_slide = l1 = l->next;
l2 = l;
l_edge_next = l2->prev->e;
v1_dst = l1_slide->next->v;
v2_dst = l2->prev->v;
}
float3 dst = v1_dst->co;
/* Sometimes the sliding direction may fork (`isect_curr_dirs` is `true`).
* In this case, the resulting direction is the intersection of the destinations. */
bool isect_curr_dirs = false;
/* Identify the slot to slide according to the directions already computed in `curr`. */
int best_slide = -1;
if (f_curr == curr.fdata[0].f || v1_dst == curr.fdata[0].v_dst) {
best_slide = 0;
}
else if (f_curr == curr.fdata[1].f || v1_dst == curr.fdata[1].v_dst) {
best_slide = 1;
}
else if (ELEM(nullptr, curr.fdata[0].f, curr.fdata[1].f)) {
best_slide = int(curr.fdata[0].f != nullptr);
curr.fdata[best_slide].f = f_curr;
if (curr.vert_is_edge_pair) {
curr.fdata[best_slide].dst = isect_face_dst(l1);
}
else {
curr.fdata[best_slide].v_dst = v1_dst;
curr.fdata[best_slide].dst = v1_dst->co;
}
}
else {
isect_curr_dirs = true;
/* Find the best direction among those already computed.
* Prioritizing in order:
* - Edge that share faces between them.
* - Boundary edge that points to the closest direction.
* - Any edge that points to the closest direction. */
BMLoop *l_other = l1_slide->radial_next;
while (l_other != l1_slide) {
if (l_other->f == curr.fdata[0].f) {
best_slide = 0;
break;
}
if (l_other->f == curr.fdata[1].f) {
best_slide = 1;
break;
}
l_other = (l_other->v == curr.v ? l_other->prev : l_other->next)->radial_next;
}
if (best_slide == -1) {
BMEdge *e0 = curr.fdata[0].v_dst ? BM_edge_exists(curr.v, curr.fdata[0].v_dst) :
nullptr;
BMEdge *e1 = curr.fdata[1].v_dst ? BM_edge_exists(curr.v, curr.fdata[1].v_dst) :
nullptr;
const bool is_boundary_0 = e0 && BM_edge_is_boundary(e0);
const bool is_boundary_1 = e1 && BM_edge_is_boundary(e1);
if (is_boundary_0 && !is_boundary_1) {
best_slide = 0;
}
else if (is_boundary_1 && !is_boundary_0) {
best_slide = 1;
}
else {
/* Find the closest direction. */
float3 src = curr.v->co;
float3 dir_curr = dst - src;
float3 dir0 = math::normalize(curr.fdata[0].dst - src);
float3 dir1 = math::normalize(curr.fdata[1].dst - src);
float dot0 = math::dot(dir_curr, dir0);
float dot1 = math::dot(dir_curr, dir1);
best_slide = int(dot0 < dot1);
}
}
}
/* Compute `next`. */
next.fdata[best_slide].f = f_curr;
if (l_edge_next == next.e || next.vert_is_edge_pair) {
/* Case where the vertex slides over the face. */
next.fdata[best_slide].v_dst = nullptr;
next.fdata[best_slide].dst = isect_face_dst(l2);
}
else {
/* Case where the vertex slides over an edge. */
next.fdata[best_slide].v_dst = v2_dst;
next.fdata[best_slide].dst = v2_dst->co;
}
if (isect_curr_dirs) {
/* The `best_slide` can only have one direction. */
float3 &dst0 = prev.fdata[best_slide].dst;
float3 &dst1 = curr.fdata[best_slide].dst;
float3 &dst2 = dst;
float3 &dst3 = next.fdata[best_slide].dst;
float3 isect0, isect1;
if (isect_line_line_epsilon_v3(dst0, dst1, dst2, dst3, isect0, isect1, FLT_EPSILON)) {
curr.fdata[best_slide].dst = math::midpoint(isect0, isect1);
}
}
}
}
BM_elem_flag_disable(v, BM_ELEM_TAG);
BM_elem_flag_disable(v_prev, BM_ELEM_TAG);
/* The data in `curr` is computed. Use to compute the #TransDataEdgeSlideVert. */
float3 iloc = curr.sv->td->iloc;
if (curr.fdata[0].f) {
curr.sv->dir_side[0] = curr.fdata[0].dst - iloc;
}
if (curr.fdata[1].f) {
curr.sv->dir_side[1] = curr.fdata[1].dst - iloc;
}
curr.sv->edge_len = math::distance(curr.sv->dir_side[0], curr.sv->dir_side[1]);
curr.sv->loop_nr = loop_nr;
if (i_prev != -1 && prev.i == i_prev) {
/* Cycle returned to the beginning.
* The data with index `i_curr` was computed twice to make sure the directions are correct
* the second time. */
break;
}
l_a_ok_prev = (l_a != nullptr);
l_b_ok_prev = (l_b != nullptr);
l_a_prev = l_a;
l_b_prev = l_b;
if (l_a) {
l_a = get_next_loop(v, l_a, e_prev, e, vec_a);
}
else {
zero_v3(vec_a);
}
if (l_b) {
l_b = get_next_loop(v, l_b, e_prev, e, vec_b);
}
else {
zero_v3(vec_b);
}
if (l_a && l_b) {
/* pass */
}
else {
if (l_a || l_b) {
/* find the opposite loop if it was missing previously */
if (l_a == nullptr && l_b && (l_b->radial_next != l_b)) {
l_a = l_b->radial_next;
}
else if (l_b == nullptr && l_a && (l_a->radial_next != l_a)) {
l_b = l_a->radial_next;
}
}
else if (e->l != nullptr) {
/* if there are non-contiguous faces, we can still recover
* the loops of the new edges faces */
/* NOTE:, the behavior in this case means edges may move in opposite directions,
* this could be made to work more usefully. */
if (l_a_ok_prev) {
l_a = e->l;
l_b = (l_a->radial_next != l_a) ? l_a->radial_next : nullptr;
}
else if (l_b_ok_prev) {
l_b = e->l;
l_a = (l_b->radial_next != l_b) ? l_b->radial_next : nullptr;
}
}
if (!l_a_ok_prev && l_a) {
get_next_loop(v, l_a, e, e_prev, vec_a);
}
if (!l_b_ok_prev && l_b) {
get_next_loop(v, l_b, e, e_prev, vec_b);
}
}
BM_elem_flag_disable(v, BM_ELEM_TAG);
BM_elem_flag_disable(v_prev, BM_ELEM_TAG);
} while ((e != v_first->e) && (l_a || l_b));
#undef SV_FROM_VERT
#undef INDEX_UNSET
#undef INDEX_INVALID
/* Move forward. */
prev = curr;
curr = next;
next = next_next;
}
loop_nr++;
#undef EDGESLIDE_VERT_IS_INNER
}
// EDBM_flag_disable_all(em, BM_ELEM_SELECT);
BLI_assert(STACK_SIZE(sv_array) == uint(sv_tot));
MEM_freeN(sv_table);
*r_group_len = loop_nr;
*r_sv_len = sv_tot;
return sv_array;
}
/**
* A simple version of #createEdgeSlideVerts_double_side
* Which assumes the longest unselected.
*/
static TransDataEdgeSlideVert *createEdgeSlideVerts_single_side(const TransDataContainer *tc,
int *r_group_len,
int *r_sv_len)
{
BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
BMesh *bm = em->bm;
BMIter iter;
BMEdge *e;
TransDataEdgeSlideVert *sv_array;
int sv_tot;
int *sv_table; /* BMVert -> sv_array index */
int loop_nr;
/* ensure valid selection */
{
int i = 0, j = 0;
BMVert *v;
BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
float len_sq_max = -1.0f;
BMIter iter2;
BM_ITER_ELEM (e, &iter2, v, BM_EDGES_OF_VERT) {
if (!BM_elem_flag_test(e, BM_ELEM_SELECT)) {
float len_sq = BM_edge_calc_length_squared(e);
if (len_sq > len_sq_max) {
len_sq_max = len_sq;
v->e = e;
}
}
}
if (len_sq_max != -1.0f) {
j++;
}
}
BM_elem_index_set(v, i); /* set_inline */
}
bm->elem_index_dirty &= ~BM_VERT;
if (!j) {
return nullptr;
}
sv_tot = j;
}
BLI_assert(sv_tot != 0);
/* over alloc */
sv_array = static_cast<TransDataEdgeSlideVert *>(
MEM_callocN(sizeof(TransDataEdgeSlideVert) * bm->totvertsel, "sv_array"));
/* Same loop for all loops, weak but we don't connect loops in this case. */
loop_nr = 1;
sv_table = static_cast<int *>(MEM_mallocN(sizeof(*sv_table) * bm->totvert, __func__));
{
int i = 0, j = 0;
BMVert *v;
BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
sv_table[i] = -1;
if ((v->e != nullptr) && BM_elem_flag_test(v, BM_ELEM_SELECT)) {
if (BM_elem_flag_test(v->e, BM_ELEM_SELECT) == 0) {
TransDataEdgeSlideVert *sv;
sv = &sv_array[j];
sv->v = v;
copy_v3_v3(sv->v_co_orig, v->co);
BMVert *v_other = BM_edge_other_vert(v->e, v);
sub_v3_v3v3(sv->dir_side[0], v_other->co, v->co);
sv->loop_nr = 0;
sv_table[i] = j;
j += 1;
}
}
}
}
/* check for wire vertices,
* interpolate the directions of wire verts between non-wire verts */
if (sv_tot != bm->totvert) {
const int sv_tot_nowire = sv_tot;
TransDataEdgeSlideVert *sv_iter = sv_array;
for (int i = 0; i < sv_tot_nowire; i++, sv_iter++) {
BMIter eiter;
BM_ITER_ELEM (e, &eiter, sv_iter->v, BM_EDGES_OF_VERT) {
/* walk over wire */
TransDataEdgeSlideVert *sv_end = nullptr;
BMEdge *e_step = e;
BMVert *v = sv_iter->v;
int j;
j = sv_tot;
while (true) {
BMVert *v_other = BM_edge_other_vert(e_step, v);
int endpoint = ((sv_table[BM_elem_index_get(v_other)] != -1) +
(BM_vert_is_edge_pair(v_other) == false));
if ((BM_elem_flag_test(e_step, BM_ELEM_SELECT) &&
BM_elem_flag_test(v_other, BM_ELEM_SELECT)) &&
(endpoint == 0))
{
/* scan down the list */
TransDataEdgeSlideVert *sv;
BLI_assert(sv_table[BM_elem_index_get(v_other)] == -1);
sv_table[BM_elem_index_get(v_other)] = j;
sv = &sv_array[j];
sv->v = v_other;
copy_v3_v3(sv->v_co_orig, v_other->co);
copy_v3_v3(sv->dir_side[0], sv_iter->dir_side[0]);
j++;
/* advance! */
v = v_other;
e_step = BM_DISK_EDGE_NEXT(e_step, v_other);
}
else {
if ((endpoint == 2) && (sv_tot != j)) {
BLI_assert(BM_elem_index_get(v_other) != -1);
sv_end = &sv_array[sv_table[BM_elem_index_get(v_other)]];
}
break;
}
}
if (sv_end) {
int sv_tot_prev = sv_tot;
const float *co_src = sv_iter->v->co;
const float *co_dst = sv_end->v->co;
const float *dir_src = sv_iter->dir_side[0];
const float *dir_dst = sv_end->dir_side[0];
sv_tot = j;
while (j-- != sv_tot_prev) {
float factor;
factor = line_point_factor_v3(sv_array[j].v->co, co_src, co_dst);
interp_v3_v3v3(sv_array[j].dir_side[0], dir_src, dir_dst, factor);
}
}
}
}
}
// EDBM_flag_disable_all(em, BM_ELEM_SELECT);
MEM_freeN(sv_table);
*r_group_len = loop_nr;
*r_sv_len = sv_tot;
return sv_array;
}
TransDataEdgeSlideVert *transform_mesh_edge_slide_data_create(const TransDataContainer *tc,
const bool use_double_side,
int *r_group_len,
int *r_sv_len)
{
TransDataEdgeSlideVert *sv_array =
use_double_side ? createEdgeSlideVerts_double_side(tc, r_group_len, r_sv_len) :
createEdgeSlideVerts_single_side(tc, r_group_len, r_sv_len);
if (sv_array) {
TransDataEdgeSlideVert *sv = sv_array;
for (int i = 0; i < *r_sv_len; i++, sv++) {
/* Set length */
sv->edge_len = len_v3v3(sv->dir_side[0], sv->dir_side[1]);
}
}
return sv_array;
return r_sv;
}
/** \} */

View File

@@ -9,14 +9,11 @@
#include "BLI_math_matrix.h"
#include "BLI_string.h"
#include "BKE_editmesh.hh"
#include "BKE_editmesh_bvh.h"
#include "BKE_unit.hh"
#include "GPU_immediate.h"
#include "GPU_matrix.h"
#include "ED_mesh.hh"
#include "ED_screen.hh"
#include "ED_transform_snap_object_context.hh"
@@ -25,6 +22,7 @@
#include "RNA_access.hh"
#include "UI_interface.hh"
#include "UI_view2d.hh"
#include "BLT_translation.hh"
@@ -41,11 +39,44 @@ using namespace blender;
* \{ */
struct EdgeSlideData {
TransDataEdgeSlideVert *sv;
int totsv;
Array<TransDataEdgeSlideVert> sv;
int mval_start[2], mval_end[2];
int curr_sv_index;
private:
float4x4 proj_mat;
float2 win_half;
public:
void update_proj_mat(TransInfo *t, const TransDataContainer *tc)
{
ARegion *region = t->region;
this->win_half = {region->winx / 2.0f, region->winy / 2.0f};
if (t->spacetype == SPACE_VIEW3D) {
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
this->proj_mat = ED_view3d_ob_project_mat_get(rv3d, tc->obedit);
for (int i = 0; i < 4; i++) {
this->proj_mat[i][0] *= this->win_half[0];
this->proj_mat[i][1] *= this->win_half[1];
}
}
else {
const View2D *v2d = static_cast<View2D *>(t->view);
UI_view2d_view_to_region_m4(v2d, this->proj_mat.ptr());
this->proj_mat.location()[0] -= this->win_half[0];
this->proj_mat.location()[1] -= this->win_half[1];
}
}
void project(const TransDataEdgeSlideVert *sv, float2 &r_sco_a, float2 &r_sco_b)
{
float3 iloc = sv->v_co_orig();
r_sco_a = math::project_point(this->proj_mat, iloc + sv->dir_side[0]).xy() + this->win_half;
r_sco_b = math::project_point(this->proj_mat, iloc + sv->dir_side[1]).xy() + this->win_half;
}
};
struct EdgeSlideParams {
@@ -124,37 +155,6 @@ static void interp_line_v3_v3v3v3(
}
}
static blender::float4x4 edge_slide_projmat_get(TransInfo *t, TransDataContainer *tc)
{
RegionView3D *rv3d = nullptr;
if (t->spacetype == SPACE_VIEW3D) {
/* Background mode support. */
rv3d = static_cast<RegionView3D *>(t->region ? t->region->regiondata : nullptr);
}
if (!rv3d) {
/* Ok, let's try to survive this. */
return blender::float4x4::identity();
}
return ED_view3d_ob_project_mat_get(rv3d, tc->obedit);
}
static void edge_slide_pair_project(TransDataEdgeSlideVert *sv,
ARegion *region,
const float projectMat[4][4],
float r_sco_a[3],
float r_sco_b[3])
{
BMVert *v = sv->v;
add_v3_v3v3(r_sco_b, v->co, sv->dir_side[1]);
ED_view3d_project_float_v3_m4(region, r_sco_b, r_sco_b, projectMat);
add_v3_v3v3(r_sco_a, v->co, sv->dir_side[0]);
ED_view3d_project_float_v3_m4(region, r_sco_a, r_sco_a, projectMat);
}
static void edge_slide_data_init_mval(MouseInput *mi, EdgeSlideData *sld, float *mval_dir)
{
/* Possible all of the edge loops are pointing directly at the view. */
@@ -184,10 +184,11 @@ static bool is_vert_slide_visible(TransInfo *t,
TransDataEdgeSlideVert *sv,
const float4 &plane_near)
{
const float3 &v_co_orig = sv->v_co_orig();
float3 points[3] = {
sv->v_co_orig,
sv->v_co_orig + sv->dir_side[0] * 0.9f,
sv->v_co_orig + sv->dir_side[1] * 0.9f,
v_co_orig,
v_co_orig + sv->dir_side[0] * 0.9f,
v_co_orig + sv->dir_side[1] * 0.9f,
};
float3 hit_loc;
@@ -239,7 +240,6 @@ static bool is_vert_slide_visible(TransInfo *t,
* Calculate screen-space `mval_start` / `mval_end`, optionally slide direction.
*/
static void calcEdgeSlide_mval_range(TransInfo *t,
TransDataContainer *tc,
EdgeSlideData *sld,
const int loop_nr,
const blender::float2 &mval,
@@ -248,9 +248,9 @@ static void calcEdgeSlide_mval_range(TransInfo *t,
/* only for use_calc_direction */
float(*loop_dir)[3] = nullptr, *loop_maxdist = nullptr;
float mval_dir[3], dist_best_sq;
float mval_dir[3], dist_best_sq = FLT_MAX;
/* Use for visibility checks. */
/* use for visibility checks */
SnapObjectContext *snap_context = nullptr;
bool use_occlude_geometry = false;
float4 plane_near;
@@ -262,21 +262,18 @@ static void calcEdgeSlide_mval_range(TransInfo *t,
snap_context = ED_transform_snap_object_context_create(t->scene, 0);
}
const blender::float4x4 projection = edge_slide_projmat_get(t, tc);
/* find mouse vectors, the global one, and one per loop in case we have
* multiple loops selected, in case they are oriented different */
zero_v3(mval_dir);
dist_best_sq = -1.0f;
if (use_calc_direction) {
loop_dir = static_cast<float(*)[3]>(MEM_callocN(sizeof(float[3]) * loop_nr, "sv loop_dir"));
loop_maxdist = static_cast<float *>(MEM_mallocN(sizeof(float) * loop_nr, "sv loop_maxdist"));
copy_vn_fl(loop_maxdist, loop_nr, -1.0f);
copy_vn_fl(loop_maxdist, loop_nr, FLT_MAX);
}
TransDataEdgeSlideVert *sv = &sld->sv[0];
for (int i = 0; i < sld->totsv; i++, sv++) {
for (int i : sld->sv.index_range()) {
TransDataEdgeSlideVert *sv = &sld->sv[i];
bool is_visible = !use_occlude_geometry ||
is_vert_slide_visible(t, snap_context, sv, plane_near);
@@ -285,20 +282,16 @@ static void calcEdgeSlide_mval_range(TransInfo *t,
continue;
}
/* Search cross edges for visible edge to the mouse cursor,
* then use the shared vertex to calculate screen vector. */
/* screen-space coords */
float sco_a[3], sco_b[3];
float dist_sq;
int l_nr;
edge_slide_pair_project(sv, t->region, projection.ptr(), sco_a, sco_b);
float2 sco_a, sco_b;
sld->project(sv, sco_a, sco_b);
/* global direction */
dist_sq = dist_squared_to_line_segment_v2(mval, sco_b, sco_a);
float dist_sq = dist_squared_to_line_segment_v2(mval, sco_b, sco_a);
if (is_visible) {
if ((dist_best_sq == -1.0f) ||
/* intentionally use 2d size on 3d vector */
(dist_sq < dist_best_sq && (len_squared_v2v2(sco_b, sco_a) > 0.1f)))
{
if (dist_sq < dist_best_sq && (len_squared_v2v2(sco_b, sco_a) > 0.1f)) {
dist_best_sq = dist_sq;
sub_v3_v3v3(mval_dir, sco_b, sco_a);
sld->curr_sv_index = i;
@@ -307,8 +300,8 @@ static void calcEdgeSlide_mval_range(TransInfo *t,
if (use_calc_direction) {
/* per loop direction */
l_nr = sv->loop_nr;
if (loop_maxdist[l_nr] == -1.0f || dist_sq < loop_maxdist[l_nr]) {
int l_nr = sv->loop_nr;
if (dist_sq < loop_maxdist[l_nr]) {
loop_maxdist[l_nr] = dist_sq;
sub_v3_v3v3(loop_dir[l_nr], sco_b, sco_a);
}
@@ -316,13 +309,11 @@ static void calcEdgeSlide_mval_range(TransInfo *t,
}
if (use_calc_direction) {
int i;
sv = &sld->sv[0];
for (i = 0; i < sld->totsv; i++, sv++) {
for (TransDataEdgeSlideVert &sv : sld->sv) {
/* switch a/b if loop direction is different from global direction */
int l_nr = sv->loop_nr;
int l_nr = sv.loop_nr;
if (dot_v3v3(loop_dir[l_nr], mval_dir) < 0.0f) {
swap_v3_v3(sv->dir_side[0], sv->dir_side[1]);
swap_v3_v3(sv.dir_side[0], sv.dir_side[1]);
}
}
@@ -341,18 +332,48 @@ static EdgeSlideData *createEdgeSlideVerts(TransInfo *t,
TransDataContainer *tc,
const bool use_double_side)
{
int loop_nr;
EdgeSlideData *sld = static_cast<EdgeSlideData *>(MEM_callocN(sizeof(*sld), "sld"));
sld->curr_sv_index = 0;
int group_len;
EdgeSlideData *sld = MEM_new<EdgeSlideData>("sld");
sld->sv = transform_mesh_edge_slide_data_create(tc, &group_len);
sld->sv = transform_mesh_edge_slide_data_create(tc, use_double_side, &loop_nr, &sld->totsv);
if (sld->sv == nullptr) {
MEM_freeN(sld);
if (sld->sv.is_empty()) {
MEM_delete(sld);
return nullptr;
}
calcEdgeSlide_mval_range(t, tc, sld, loop_nr, t->mval, use_double_side);
if (!use_double_side) {
/* Single Side Case.
* Used by #MESH_OT_offset_edge_loops_slide.
* It only slides to the side with the longest length. */
struct TMP {
float2 accum;
int count;
} zero{};
Array<TMP> array_len(group_len, zero);
for (TransDataEdgeSlideVert &sv : sld->sv) {
array_len[sv.loop_nr].accum += float2(math::length(sv.dir_side[0]),
math::length(sv.dir_side[1]));
array_len[sv.loop_nr].count++;
}
for (TMP &accum : array_len) {
accum.accum /= accum.count;
}
for (TransDataEdgeSlideVert &sv : sld->sv) {
if (array_len[sv.loop_nr].accum[1] > array_len[sv.loop_nr].accum[0]) {
sv.dir_side[0] = sv.dir_side[1];
}
sv.dir_side[1] = float3(0);
sv.edge_len = math::length(sv.dir_side[0]);
}
}
sld->curr_sv_index = 0;
sld->update_proj_mat(t, tc);
calcEdgeSlide_mval_range(t, sld, group_len, t->mval, use_double_side);
return sld;
}
@@ -367,8 +388,7 @@ static void freeEdgeSlideVerts(TransInfo * /*t*/,
return;
}
MEM_freeN(sld->sv);
MEM_freeN(sld);
MEM_delete(sld);
custom_data->data = nullptr;
}
@@ -427,47 +447,51 @@ static void drawEdgeSlide(TransInfo *t)
GPU_blend(GPU_BLEND_ALPHA);
GPU_matrix_push();
GPU_matrix_mul(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->object_to_world().ptr());
if (t->spacetype == SPACE_VIEW3D) {
GPU_matrix_push();
GPU_matrix_mul(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->object_to_world().ptr());
}
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index];
const float3 &curr_sv_co_orig = curr_sv->v_co_orig();
if (slp->use_even == true) {
/* Even mode */
float co_a[3], co_b[3], co_mark[3];
TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index];
const float fac = (slp->perc + 1.0f) / 2.0f;
const float ctrl_size = UI_GetThemeValuef(TH_FACEDOT_SIZE) + 1.5f;
const float guide_size = ctrl_size - 0.5f;
const int alpha_shade = -30;
add_v3_v3v3(co_a, curr_sv->v_co_orig, curr_sv->dir_side[0]);
add_v3_v3v3(co_b, curr_sv->v_co_orig, curr_sv->dir_side[1]);
add_v3_v3v3(co_a, curr_sv_co_orig, curr_sv->dir_side[0]);
add_v3_v3v3(co_b, curr_sv_co_orig, curr_sv->dir_side[1]);
GPU_line_width(line_size);
immUniformThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade);
immBeginAtMost(GPU_PRIM_LINES, 4);
if (!is_zero_v3(curr_sv->dir_side[0])) {
if (!math::is_zero(curr_sv->dir_side[0])) {
immVertex3fv(pos, co_a);
immVertex3fv(pos, curr_sv->v_co_orig);
immVertex3fv(pos, curr_sv_co_orig);
}
if (!is_zero_v3(curr_sv->dir_side[1])) {
if (!math::is_zero(curr_sv->dir_side[1])) {
immVertex3fv(pos, co_b);
immVertex3fv(pos, curr_sv->v_co_orig);
immVertex3fv(pos, curr_sv_co_orig);
}
immEnd();
{
float *co_test = nullptr;
if (slp->flipped) {
if (!is_zero_v3(curr_sv->dir_side[1])) {
if (!math::is_zero(curr_sv->dir_side[1])) {
co_test = co_b;
}
}
else {
if (!is_zero_v3(curr_sv->dir_side[0])) {
if (!math::is_zero(curr_sv->dir_side[0])) {
co_test = co_a;
}
}
@@ -484,36 +508,35 @@ static void drawEdgeSlide(TransInfo *t)
immUniformThemeColorShadeAlpha(TH_SELECT, 255, alpha_shade);
GPU_point_size(guide_size);
immBegin(GPU_PRIM_POINTS, 1);
interp_line_v3_v3v3v3(co_mark, co_b, curr_sv->v_co_orig, co_a, fac);
interp_line_v3_v3v3v3(co_mark, co_b, curr_sv_co_orig, co_a, fac);
immVertex3fv(pos, co_mark);
immEnd();
}
else if (is_clamp == false) {
const int side_index = slp->curr_side_unclamp;
TransDataEdgeSlideVert *sv;
int i;
const int alpha_shade = -160;
GPU_line_width(line_size);
immUniformThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade);
immBegin(GPU_PRIM_LINES, sld->totsv * 2);
immBegin(GPU_PRIM_LINES, sld->sv.size() * 2);
/* TODO(@ideasman42): Loop over all verts. */
sv = sld->sv;
for (i = 0; i < sld->totsv; i++, sv++) {
for (TransDataEdgeSlideVert &sv : sld->sv) {
float a[3], b[3];
if (!is_zero_v3(sv->dir_side[side_index])) {
copy_v3_v3(a, sv->dir_side[side_index]);
if (!is_zero_v3(sv.dir_side[side_index])) {
copy_v3_v3(a, sv.dir_side[side_index]);
}
else {
copy_v3_v3(a, sv->dir_side[!side_index]);
copy_v3_v3(a, sv.dir_side[!side_index]);
}
mul_v3_fl(a, 100.0f);
negate_v3_v3(b, a);
add_v3_v3(a, sv->v_co_orig);
add_v3_v3(b, sv->v_co_orig);
const float3 &sv_co_orig = sv.v_co_orig();
add_v3_v3(a, sv_co_orig);
add_v3_v3(b, sv_co_orig);
immVertex3fv(pos, a);
immVertex3fv(pos, b);
@@ -522,27 +545,27 @@ static void drawEdgeSlide(TransInfo *t)
}
else {
/* Common case. */
TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index];
const int alpha_shade = -160;
float co_dir[3];
add_v3_v3v3(co_dir, curr_sv->v_co_orig, curr_sv->dir_side[slp->curr_side_unclamp]);
add_v3_v3v3(co_dir, curr_sv_co_orig, curr_sv->dir_side[slp->curr_side_unclamp]);
GPU_line_width(line_size);
immUniformThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade);
immBeginAtMost(GPU_PRIM_LINES, 2);
immVertex3fv(pos, curr_sv->v_co_orig);
immVertex3fv(pos, curr_sv_co_orig);
immVertex3fv(pos, co_dir);
immEnd();
}
immUnbindProgram();
GPU_matrix_pop();
if (t->spacetype == SPACE_VIEW3D) {
GPU_matrix_pop();
GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
}
GPU_blend(GPU_BLEND_NONE);
GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
}
static void edge_slide_snap_apply(TransInfo *t, float *value)
@@ -551,11 +574,11 @@ static void edge_slide_snap_apply(TransInfo *t, float *value)
EdgeSlideParams *slp = static_cast<EdgeSlideParams *>(t->custom.mode.data);
EdgeSlideData *sld_active = static_cast<EdgeSlideData *>(tc->custom.mode.data);
TransDataEdgeSlideVert *sv = &sld_active->sv[sld_active->curr_sv_index];
float snap_point[3], co_orig[3], co_dest[2][3], dvec[3];
float3 co_orig, co_dest[2], dvec, snap_point;
co_orig = sv->v_co_orig();
co_dest[0] = co_orig + sv->dir_side[0];
co_dest[1] = co_orig + sv->dir_side[1];
copy_v3_v3(co_orig, sv->v_co_orig);
add_v3_v3v3(co_dest[0], co_orig, sv->dir_side[0]);
add_v3_v3v3(co_dest[1], co_orig, sv->dir_side[1]);
if (tc->use_local_mat) {
mul_m4_v3(tc->mat, co_orig);
mul_m4_v3(tc->mat, co_dest[0]);
@@ -575,7 +598,10 @@ static void edge_slide_snap_apply(TransInfo *t, float *value)
side_index = perc < 0.0f;
}
else {
side_index = slp->curr_side_unclamp;
/* Use the side indicated in `EdgeSlideParams::curr_side_unclamp` as long as that side is not
* zero length. */
side_index = int(slp->curr_side_unclamp ==
!math::is_zero(sv->dir_side[slp->curr_side_unclamp]));
}
}
else {
@@ -628,7 +654,7 @@ static void edge_slide_snap_apply(TransInfo *t, float *value)
*value = perc;
}
static void edge_slide_apply_elem(const TransDataEdgeSlideVert *sv,
static void edge_slide_apply_elem(const TransDataEdgeSlideVert &sv,
const float fac,
const float curr_length_fac,
const int curr_side_unclamp,
@@ -637,21 +663,21 @@ static void edge_slide_apply_elem(const TransDataEdgeSlideVert *sv,
const bool use_flip,
float r_co[3])
{
copy_v3_v3(r_co, sv->v_co_orig);
copy_v3_v3(r_co, sv.v_co_orig());
if (use_even == false) {
if (use_clamp) {
const int side_index = (fac < 0.0f);
const float fac_final = fabsf(fac);
madd_v3_v3fl(r_co, sv->dir_side[side_index], fac_final);
madd_v3_v3fl(r_co, sv.dir_side[side_index], fac_final);
}
else {
int side_index = curr_side_unclamp;
if (is_zero_v3(sv->dir_side[side_index])) {
side_index = !side_index;
if (is_zero_v3(sv.dir_side[side_index])) {
side_index = int(!side_index);
}
const float fac_final = (side_index == (fac < 0.0f) ? fabsf(fac) : -fabsf(fac));
madd_v3_v3fl(r_co, sv->dir_side[side_index], fac_final);
madd_v3_v3fl(r_co, sv.dir_side[side_index], fac_final);
}
}
else {
@@ -665,18 +691,18 @@ static void edge_slide_apply_elem(const TransDataEdgeSlideVert *sv,
* is the same as the distance between the original vert locations,
* same goes for the lines below.
*/
if (sv->edge_len > FLT_EPSILON) {
if (sv.edge_len > FLT_EPSILON) {
float co_a[3], co_b[3];
const float fac_final = min_ff(sv->edge_len, curr_length_fac) / sv->edge_len;
const float fac_final = min_ff(sv.edge_len, curr_length_fac) / sv.edge_len;
add_v3_v3v3(co_a, sv->v_co_orig, sv->dir_side[0]);
add_v3_v3v3(co_b, sv->v_co_orig, sv->dir_side[1]);
add_v3_v3v3(co_a, r_co, sv.dir_side[0]);
add_v3_v3v3(co_b, r_co, sv.dir_side[1]);
if (use_flip) {
interp_line_v3_v3v3v3(r_co, co_b, sv->v_co_orig, co_a, fac_final);
interp_line_v3_v3v3v3(r_co, co_b, r_co, co_a, fac_final);
}
else {
interp_line_v3_v3v3v3(r_co, co_a, sv->v_co_orig, co_b, fac_final);
interp_line_v3_v3v3v3(r_co, co_a, r_co, co_b, fac_final);
}
}
}
@@ -710,10 +736,9 @@ static void doEdgeSlide(TransInfo *t, float perc)
continue;
}
TransDataEdgeSlideVert *sv = sld->sv;
for (int i = 0; i < sld->totsv; i++, sv++) {
for (TransDataEdgeSlideVert &sv : sld->sv) {
edge_slide_apply_elem(
sv, perc, curr_length_fac, curr_side_unclamp, use_clamp, use_even, use_flip, sv->v->co);
sv, perc, curr_length_fac, curr_side_unclamp, use_clamp, use_even, use_flip, sv.td->loc);
}
}
}
@@ -780,14 +805,14 @@ static void edge_slide_transform_matrix_fn(TransInfo *t, float mat_xform[4][4])
EdgeSlideParams *slp = static_cast<EdgeSlideParams *>(t->custom.mode.data);
TransDataContainer *tc = edge_slide_container_first_ok(t);
EdgeSlideData *sld_active = static_cast<EdgeSlideData *>(tc->custom.mode.data);
TransDataEdgeSlideVert *sv_active = &sld_active->sv[sld_active->curr_sv_index];
TransDataEdgeSlideVert &sv_active = sld_active->sv[sld_active->curr_sv_index];
copy_v3_v3(orig_co, sv_active->v_co_orig);
copy_v3_v3(orig_co, sv_active.v_co_orig());
const float fac = t->values_final[0];
float curr_length_fac = 0.0f;
if (slp->use_even) {
curr_length_fac = sv_active->edge_len * (((slp->flipped ? fac : -fac) + 1.0f) / 2.0f);
curr_length_fac = sv_active.edge_len * (((slp->flipped ? fac : -fac) + 1.0f) / 2.0f);
}
edge_slide_apply_elem(sv_active,
@@ -885,18 +910,15 @@ static void initEdgeSlide(TransInfo *t, wmOperator *op)
void transform_mode_edge_slide_reproject_input(TransInfo *t)
{
ARegion *region = t->region;
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
EdgeSlideData *sld = static_cast<EdgeSlideData *>(tc->custom.mode.data);
if (sld) {
const blender::float4x4 projection = edge_slide_projmat_get(t, tc);
sld->update_proj_mat(t, tc);
TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index];
float mval_dir[3], sco_a[3], sco_b[3];
edge_slide_pair_project(curr_sv, region, projection.ptr(), sco_a, sco_b);
sub_v3_v3v3(mval_dir, sco_b, sco_a);
float2 sco_a, sco_b;
sld->project(curr_sv, sco_a, sco_b);
float2 mval_dir = sco_b - sco_a;
edge_slide_data_init_mval(&t->mouse, sld, mval_dir);
}
}