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:
@@ -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` */
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user