Currently the `MLoopUV` struct stores UV coordinates and flags related to editing UV maps in the UV editor. This patch changes the coordinates to use the generic 2D vector type, and moves the flags into three separate boolean attributes. This follows the design in T95965, with the ultimate intention of simplifying code and improving performance. Importantly, the change allows exporters and renderers to use UVs "touched" by geometry nodes, which only creates generic attributes. It also allows geometry nodes to create "proper" UV maps from scratch, though only with the Store Named Attribute node for now. The new design considers any 2D vector attribute on the corner domain to be a UV map. In the future, they might be distinguished from regular 2D vectors with attribute metadata, which may be helpful because they are often interpolated differently. Most of the code changes deal with passing around UV BMesh custom data offsets and tracking the boolean "sublayers". The boolean layers are use the following prefixes for attribute names: vert selection: `.vs.`, edge selection: `.es.`, pinning: `.pn.`. Currently these are short to avoid using up the maximum length of attribute names. To accommodate for these 4 extra characters, the name length limit is enlarged to 68 bytes, while the maximum user settable name length is still 64 bytes. Unfortunately Python/RNA API access to the UV flag data becomes slower. Accessing the boolean layers directly is be better for performance in general. Like the other mesh SoA refactors, backward and forward compatibility aren't affected, and won't be changed until 4.0. We pay for that by making mesh reading and writing more expensive with conversions. Resolves T85962 Differential Revision: https://developer.blender.org/D14365
610 lines
20 KiB
C
610 lines
20 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup bmesh
|
|
*
|
|
* Find a path between 2 elements in UV space.
|
|
*/
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_heap_simple.h"
|
|
#include "BLI_linklist.h"
|
|
#include "BLI_math.h"
|
|
|
|
#include "DNA_meshdata_types.h"
|
|
|
|
#include "bmesh.h"
|
|
#include "bmesh_path_uv.h" /* own include */
|
|
#include "intern/bmesh_query.h"
|
|
#include "intern/bmesh_query_uv.h"
|
|
|
|
#define COST_INIT_MAX FLT_MAX
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Generic Helpers
|
|
* \{ */
|
|
|
|
/**
|
|
* Use skip options when we want to start measuring from a boundary.
|
|
*
|
|
* See #step_cost_3_v3_ex in bmesh_path.c which follows the same logic.
|
|
*/
|
|
static float step_cost_3_v2_ex(
|
|
const float v1[2], const float v2[2], const float v3[2], bool skip_12, bool skip_23)
|
|
{
|
|
float d1[2], d2[2];
|
|
|
|
/* The cost is based on the simple sum of the length of the two edges. */
|
|
sub_v2_v2v2(d1, v2, v1);
|
|
sub_v2_v2v2(d2, v3, v2);
|
|
const float cost_12 = normalize_v2(d1);
|
|
const float cost_23 = normalize_v2(d2);
|
|
const float cost = ((skip_12 ? 0.0f : cost_12) + (skip_23 ? 0.0f : cost_23));
|
|
|
|
/* But is biased to give higher values to sharp turns, so that it will take paths with
|
|
* fewer "turns" when selecting between equal-weighted paths between the two edges. */
|
|
return cost * (1.0f + 0.5f * (2.0f - sqrtf(fabsf(dot_v2v2(d1, d2)))));
|
|
}
|
|
|
|
static float step_cost_3_v2(const float v1[2], const float v2[2], const float v3[2])
|
|
{
|
|
return step_cost_3_v2_ex(v1, v2, v3, false, false);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name BM_mesh_calc_path_uv_vert
|
|
* \{ */
|
|
|
|
static void verttag_add_adjacent_uv(HeapSimple *heap,
|
|
BMLoop *l_a,
|
|
BMLoop **loops_prev,
|
|
float *cost,
|
|
const struct BMCalcPathUVParams *params)
|
|
{
|
|
BLI_assert(params->aspect_y != 0.0f);
|
|
const int cd_loop_uv_offset = params->cd_loop_uv_offset;
|
|
const int l_a_index = BM_elem_index_get(l_a);
|
|
const float *luv_a = BM_ELEM_CD_GET_FLOAT_P(l_a, cd_loop_uv_offset);
|
|
const float uv_a[2] = {luv_a[0], luv_a[1] / params->aspect_y};
|
|
|
|
{
|
|
BMIter liter;
|
|
BMLoop *l;
|
|
/* Loop over faces of face, but do so by first looping over loops. */
|
|
BM_ITER_ELEM (l, &liter, l_a->v, BM_LOOPS_OF_VERT) {
|
|
const float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
|
|
if (equals_v2v2(luv_a, luv)) {
|
|
/* 'l_a' is already tagged, tag all adjacent. */
|
|
BM_elem_flag_enable(l, BM_ELEM_TAG);
|
|
BMLoop *l_b = l->next;
|
|
do {
|
|
if (!BM_elem_flag_test(l_b, BM_ELEM_TAG)) {
|
|
const float *luv_b = BM_ELEM_CD_GET_FLOAT_P(l_b, cd_loop_uv_offset);
|
|
const float uv_b[2] = {luv_b[0], luv_b[1] / params->aspect_y};
|
|
/* We know 'l_b' is not visited, check it out! */
|
|
const int l_b_index = BM_elem_index_get(l_b);
|
|
const float cost_cut = params->use_topology_distance ? 1.0f : len_v2v2(uv_a, uv_b);
|
|
const float cost_new = cost[l_a_index] + cost_cut;
|
|
|
|
if (cost[l_b_index] > cost_new) {
|
|
cost[l_b_index] = cost_new;
|
|
loops_prev[l_b_index] = l_a;
|
|
BLI_heapsimple_insert(heap, cost_new, l_b);
|
|
}
|
|
}
|
|
/* This means we only step onto `l->prev` & `l->next`. */
|
|
if (params->use_step_face == false) {
|
|
if (l_b == l->next) {
|
|
l_b = l->prev->prev;
|
|
}
|
|
}
|
|
} while ((l_b = l_b->next) != l);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct LinkNode *BM_mesh_calc_path_uv_vert(BMesh *bm,
|
|
BMLoop *l_src,
|
|
BMLoop *l_dst,
|
|
const struct BMCalcPathUVParams *params,
|
|
bool (*filter_fn)(BMLoop *, void *),
|
|
void *user_data)
|
|
{
|
|
LinkNode *path = NULL;
|
|
/* BM_ELEM_TAG flag is used to store visited edges */
|
|
BMIter viter;
|
|
HeapSimple *heap;
|
|
float *cost;
|
|
BMLoop **loops_prev;
|
|
int i = 0, totloop;
|
|
BMFace *f;
|
|
|
|
/* NOTE: would pass BM_EDGE except we are looping over all faces anyway. */
|
|
// BM_mesh_elem_index_ensure(bm, BM_LOOP); // NOT NEEDED FOR FACETAG
|
|
|
|
BM_ITER_MESH (f, &viter, bm, BM_FACES_OF_MESH) {
|
|
BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
|
|
BMLoop *l_iter = l_first;
|
|
do {
|
|
BM_elem_flag_set(l_iter, BM_ELEM_TAG, !filter_fn(l_iter, user_data));
|
|
BM_elem_index_set(l_iter, i); /* set_inline */
|
|
i += 1;
|
|
} while ((l_iter = l_iter->next) != l_first);
|
|
}
|
|
bm->elem_index_dirty &= ~BM_LOOP;
|
|
|
|
/* Allocate. */
|
|
totloop = bm->totloop;
|
|
loops_prev = MEM_callocN(sizeof(*loops_prev) * totloop, __func__);
|
|
cost = MEM_mallocN(sizeof(*cost) * totloop, __func__);
|
|
|
|
copy_vn_fl(cost, totloop, COST_INIT_MAX);
|
|
|
|
/* Regular dijkstra shortest path, but over UV loops instead of vertices. */
|
|
heap = BLI_heapsimple_new();
|
|
BLI_heapsimple_insert(heap, 0.0f, l_src);
|
|
cost[BM_elem_index_get(l_src)] = 0.0f;
|
|
|
|
BMLoop *l = NULL;
|
|
while (!BLI_heapsimple_is_empty(heap)) {
|
|
l = BLI_heapsimple_pop_min(heap);
|
|
|
|
if ((l->v == l_dst->v) && BM_loop_uv_share_vert_check(l, l_dst, params->cd_loop_uv_offset)) {
|
|
break;
|
|
}
|
|
|
|
if (!BM_elem_flag_test(l, BM_ELEM_TAG)) {
|
|
/* Adjacent loops are tagged while stepping to avoid 2x loops. */
|
|
BM_elem_flag_enable(l, BM_ELEM_TAG);
|
|
verttag_add_adjacent_uv(heap, l, loops_prev, cost, params);
|
|
}
|
|
}
|
|
|
|
if ((l->v == l_dst->v) && BM_loop_uv_share_vert_check(l, l_dst, params->cd_loop_uv_offset)) {
|
|
do {
|
|
BLI_linklist_prepend(&path, l);
|
|
} while ((l = loops_prev[BM_elem_index_get(l)]));
|
|
}
|
|
|
|
MEM_freeN(loops_prev);
|
|
MEM_freeN(cost);
|
|
BLI_heapsimple_free(heap, NULL);
|
|
|
|
return path;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name BM_mesh_calc_path_uv_edge
|
|
* \{ */
|
|
|
|
static float edgetag_cut_cost_vert_uv(
|
|
BMLoop *l_e_a, BMLoop *l_e_b, BMLoop *l_v, const float aspect_y, const int cd_loop_uv_offset)
|
|
{
|
|
BMLoop *l_v1 = (l_v->v == l_e_a->v) ? l_e_a->next : l_e_a;
|
|
BMLoop *l_v2 = (l_v->v == l_e_b->v) ? l_e_b->next : l_e_b;
|
|
|
|
float *luv_v1 = BM_ELEM_CD_GET_FLOAT_P(l_v1, cd_loop_uv_offset);
|
|
float *luv_v2 = BM_ELEM_CD_GET_FLOAT_P(l_v2, cd_loop_uv_offset);
|
|
float *luv_v = BM_ELEM_CD_GET_FLOAT_P(l_v, cd_loop_uv_offset);
|
|
|
|
float uv_v1[2] = {luv_v1[0], luv_v1[1] / aspect_y};
|
|
float uv_v2[2] = {luv_v2[0], luv_v2[1] / aspect_y};
|
|
float uv_v[2] = {luv_v[0], luv_v[1] / aspect_y};
|
|
|
|
return step_cost_3_v2(uv_v1, uv_v, uv_v2);
|
|
}
|
|
|
|
static float edgetag_cut_cost_face_uv(
|
|
BMLoop *l_e_a, BMLoop *l_e_b, BMFace *f, const float aspect_v2[2], const int cd_loop_uv_offset)
|
|
{
|
|
float l_e_a_cent[2], l_e_b_cent[2], f_cent[2];
|
|
float *luv_e_a = BM_ELEM_CD_GET_FLOAT_P(l_e_a, cd_loop_uv_offset);
|
|
float *luv_e_b = BM_ELEM_CD_GET_FLOAT_P(l_e_b, cd_loop_uv_offset);
|
|
|
|
mid_v2_v2v2(l_e_a_cent, luv_e_a, luv_e_a);
|
|
mid_v2_v2v2(l_e_b_cent, luv_e_b, luv_e_b);
|
|
|
|
mul_v2_v2(l_e_a_cent, aspect_v2);
|
|
mul_v2_v2(l_e_b_cent, aspect_v2);
|
|
|
|
BM_face_uv_calc_center_median_weighted(f, aspect_v2, cd_loop_uv_offset, f_cent);
|
|
|
|
return step_cost_3_v2(l_e_a_cent, l_e_b_cent, f_cent);
|
|
}
|
|
|
|
static void edgetag_add_adjacent_uv(HeapSimple *heap,
|
|
BMLoop *l_a,
|
|
BMLoop **loops_prev,
|
|
float *cost,
|
|
const struct BMCalcPathUVParams *params)
|
|
{
|
|
BLI_assert(params->aspect_y != 0.0f);
|
|
const int cd_loop_uv_offset = params->cd_loop_uv_offset;
|
|
BMLoop *l_a_verts[2] = {l_a, l_a->next};
|
|
const int l_a_index = BM_elem_index_get(l_a);
|
|
|
|
if (params->use_step_face == false) {
|
|
for (int i = 0; i < ARRAY_SIZE(l_a_verts); i++) {
|
|
|
|
/* Skip current UV vert if it is part of the previous UV edge in the path. */
|
|
if (loops_prev[l_a_index]) {
|
|
BMLoop *l_prev = loops_prev[l_a_index];
|
|
if (l_a_verts[i]->v != l_prev->v) {
|
|
l_prev = (l_a_verts[i]->v == l_prev->next->v) ? l_prev->next : NULL;
|
|
}
|
|
if (l_prev && BM_loop_uv_share_vert_check(l_a_verts[i], l_prev, cd_loop_uv_offset)) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
BMEdge *e_b;
|
|
BMIter eiter;
|
|
BM_ITER_ELEM (e_b, &eiter, l_a_verts[i]->v, BM_EDGES_OF_VERT) {
|
|
BMLoop *l_first, *l_b;
|
|
l_first = l_b = e_b->l;
|
|
do {
|
|
if (!BM_elem_flag_test(l_b, BM_ELEM_TAG)) {
|
|
BMLoop *l_b_vert = (l_a_verts[i]->v == l_b->v) ? l_b : l_b->next;
|
|
if (BM_loop_uv_share_vert_check(l_a_verts[i], l_b_vert, cd_loop_uv_offset)) {
|
|
/* We know 'l_b' is not visited, check it out! */
|
|
const int l_b_index = BM_elem_index_get(l_b);
|
|
const float cost_cut = params->use_topology_distance ?
|
|
1.0f :
|
|
edgetag_cut_cost_vert_uv(l_a,
|
|
l_b,
|
|
l_a_verts[i],
|
|
params->aspect_y,
|
|
cd_loop_uv_offset);
|
|
const float cost_new = cost[l_a_index] + cost_cut;
|
|
|
|
if (cost[l_b_index] > cost_new) {
|
|
cost[l_b_index] = cost_new;
|
|
loops_prev[l_b_index] = l_a;
|
|
BLI_heapsimple_insert(heap, cost_new, l_b);
|
|
}
|
|
}
|
|
}
|
|
} while ((l_b = l_b->radial_next) != l_first);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
const float aspect_v2[2] = {1.0f, 1.0f / params->aspect_y};
|
|
BMLoop *l_first, *l_iter;
|
|
l_iter = l_first = l_a;
|
|
do {
|
|
/* Ensures connected UVs and that they lie on the same island. */
|
|
if (!BM_loop_uv_share_edge_check(l_a, l_iter, cd_loop_uv_offset)) {
|
|
continue;
|
|
}
|
|
|
|
BMLoop *l_cycle_iter, *l_cycle_end;
|
|
l_cycle_iter = l_iter->next;
|
|
l_cycle_end = l_iter;
|
|
do {
|
|
BMLoop *l_b = l_cycle_iter;
|
|
if (!BM_elem_flag_test(l_b, BM_ELEM_TAG)) {
|
|
/* We know 'l_b' is not visited, check it out! */
|
|
const int l_b_index = BM_elem_index_get(l_b);
|
|
const float cost_cut = params->use_topology_distance ?
|
|
1.0f :
|
|
edgetag_cut_cost_face_uv(l_a,
|
|
l_b,
|
|
l_iter->f,
|
|
aspect_v2,
|
|
params->cd_loop_uv_offset);
|
|
const float cost_new = cost[l_a_index] + cost_cut;
|
|
|
|
if (cost[l_b_index] > cost_new) {
|
|
cost[l_b_index] = cost_new;
|
|
loops_prev[l_b_index] = l_a;
|
|
BLI_heapsimple_insert(heap, cost_new, l_b);
|
|
}
|
|
}
|
|
} while ((l_cycle_iter = l_cycle_iter->next) != l_cycle_end);
|
|
} while ((l_iter = l_iter->radial_next) != l_first);
|
|
}
|
|
}
|
|
|
|
struct LinkNode *BM_mesh_calc_path_uv_edge(BMesh *bm,
|
|
BMLoop *l_src,
|
|
BMLoop *l_dst,
|
|
const struct BMCalcPathUVParams *params,
|
|
bool (*filter_fn)(BMLoop *, void *),
|
|
void *user_data)
|
|
{
|
|
LinkNode *path = NULL;
|
|
|
|
BMFace *f;
|
|
BMIter iter;
|
|
HeapSimple *heap;
|
|
float *cost;
|
|
BMLoop **loops_prev;
|
|
int i = 0, totloop;
|
|
|
|
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
|
|
BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
|
|
BMLoop *l_iter = l_first;
|
|
do {
|
|
BM_elem_flag_set(l_iter, BM_ELEM_TAG, !filter_fn(l_iter, user_data));
|
|
BM_elem_index_set(l_iter, i);
|
|
i += 1;
|
|
} while ((l_iter = l_iter->next) != l_first);
|
|
}
|
|
bm->elem_index_dirty &= ~BM_LOOP;
|
|
|
|
totloop = bm->totloop;
|
|
loops_prev = MEM_callocN(sizeof(*loops_prev) * totloop, __func__);
|
|
cost = MEM_mallocN(sizeof(*cost) * totloop, __func__);
|
|
|
|
copy_vn_fl(cost, totloop, COST_INIT_MAX);
|
|
|
|
/* Regular dijkstra shortest path, but over UV loops/edges instead of vertices. */
|
|
heap = BLI_heapsimple_new();
|
|
BLI_heapsimple_insert(heap, 0.0f, l_src);
|
|
cost[BM_elem_index_get(l_src)] = 0.0f;
|
|
|
|
BMLoop *l = NULL;
|
|
while (!BLI_heapsimple_is_empty(heap)) {
|
|
l = BLI_heapsimple_pop_min(heap);
|
|
|
|
if ((l->e == l_dst->e) && BM_loop_uv_share_edge_check(l, l_dst, params->cd_loop_uv_offset)) {
|
|
break;
|
|
}
|
|
|
|
if (!BM_elem_flag_test(l, BM_ELEM_TAG)) {
|
|
BM_elem_flag_enable(l, BM_ELEM_TAG);
|
|
edgetag_add_adjacent_uv(heap, l, loops_prev, cost, params);
|
|
}
|
|
}
|
|
|
|
if ((l->e == l_dst->e) && BM_loop_uv_share_edge_check(l, l_dst, params->cd_loop_uv_offset)) {
|
|
do {
|
|
BLI_linklist_prepend(&path, l);
|
|
} while ((l = loops_prev[BM_elem_index_get(l)]));
|
|
}
|
|
|
|
MEM_freeN(loops_prev);
|
|
MEM_freeN(cost);
|
|
BLI_heapsimple_free(heap, NULL);
|
|
|
|
return path;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name BM_mesh_calc_path_uv_face
|
|
* \{ */
|
|
|
|
static float facetag_cut_cost_edge_uv(BMFace *f_a,
|
|
BMFace *f_b,
|
|
BMLoop *l_edge,
|
|
const void *const f_endpoints[2],
|
|
const float aspect_v2[2],
|
|
const int cd_loop_uv_offset)
|
|
{
|
|
float f_a_cent[2];
|
|
float f_b_cent[2];
|
|
float e_cent[2];
|
|
|
|
BM_face_uv_calc_center_median_weighted(f_a, aspect_v2, cd_loop_uv_offset, f_a_cent);
|
|
BM_face_uv_calc_center_median_weighted(f_b, aspect_v2, cd_loop_uv_offset, f_b_cent);
|
|
|
|
const float *co_v1 = BM_ELEM_CD_GET_FLOAT_P(l_edge, cd_loop_uv_offset);
|
|
const float *co_v2 = BM_ELEM_CD_GET_FLOAT_P(l_edge->next, cd_loop_uv_offset);
|
|
|
|
#if 0
|
|
mid_v2_v2v2(e_cent, co_v1, co_v2);
|
|
#else
|
|
/* For triangle fans it gives better results to pick a point on the edge. */
|
|
{
|
|
float ix_e[2];
|
|
isect_line_line_v2_point(co_v1, co_v2, f_a_cent, f_b_cent, ix_e);
|
|
const float factor = line_point_factor_v2(ix_e, co_v1, co_v2);
|
|
if (factor < 0.0f) {
|
|
copy_v2_v2(e_cent, co_v1);
|
|
}
|
|
else if (factor > 1.0f) {
|
|
copy_v2_v2(e_cent, co_v2);
|
|
}
|
|
else {
|
|
copy_v2_v2(e_cent, ix_e);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Apply aspect before calculating cost. */
|
|
mul_v2_v2(f_a_cent, aspect_v2);
|
|
mul_v2_v2(f_b_cent, aspect_v2);
|
|
mul_v2_v2(e_cent, aspect_v2);
|
|
|
|
return step_cost_3_v2_ex(
|
|
f_a_cent, e_cent, f_b_cent, (f_a == f_endpoints[0]), (f_b == f_endpoints[1]));
|
|
}
|
|
|
|
static float facetag_cut_cost_vert_uv(BMFace *f_a,
|
|
BMFace *f_b,
|
|
BMLoop *l_vert,
|
|
const void *const f_endpoints[2],
|
|
const float aspect_v2[2],
|
|
const int cd_loop_uv_offset)
|
|
{
|
|
float f_a_cent[2];
|
|
float f_b_cent[2];
|
|
float v_cent[2];
|
|
|
|
BM_face_uv_calc_center_median_weighted(f_a, aspect_v2, cd_loop_uv_offset, f_a_cent);
|
|
BM_face_uv_calc_center_median_weighted(f_b, aspect_v2, cd_loop_uv_offset, f_b_cent);
|
|
|
|
copy_v2_v2(v_cent, BM_ELEM_CD_GET_FLOAT_P(l_vert, cd_loop_uv_offset));
|
|
|
|
mul_v2_v2(f_a_cent, aspect_v2);
|
|
mul_v2_v2(f_b_cent, aspect_v2);
|
|
mul_v2_v2(v_cent, aspect_v2);
|
|
|
|
return step_cost_3_v2_ex(
|
|
f_a_cent, v_cent, f_b_cent, (f_a == f_endpoints[0]), (f_b == f_endpoints[1]));
|
|
}
|
|
|
|
static void facetag_add_adjacent_uv(HeapSimple *heap,
|
|
BMFace *f_a,
|
|
BMFace **faces_prev,
|
|
float *cost,
|
|
const void *const f_endpoints[2],
|
|
const float aspect_v2[2],
|
|
const struct BMCalcPathUVParams *params)
|
|
{
|
|
const int cd_loop_uv_offset = params->cd_loop_uv_offset;
|
|
const int f_a_index = BM_elem_index_get(f_a);
|
|
|
|
/* Loop over faces of face, but do so by first looping over loops. */
|
|
{
|
|
BMIter liter;
|
|
BMLoop *l_a;
|
|
|
|
BM_ITER_ELEM (l_a, &liter, f_a, BM_LOOPS_OF_FACE) {
|
|
BMLoop *l_first, *l_iter;
|
|
|
|
/* Check there is an adjacent face to loop over. */
|
|
if (l_a != l_a->radial_next) {
|
|
l_iter = l_first = l_a->radial_next;
|
|
do {
|
|
BMFace *f_b = l_iter->f;
|
|
if (!BM_elem_flag_test(f_b, BM_ELEM_TAG)) {
|
|
if (BM_loop_uv_share_edge_check(l_a, l_iter, cd_loop_uv_offset)) {
|
|
/* We know 'f_b' is not visited, check it out! */
|
|
const int f_b_index = BM_elem_index_get(f_b);
|
|
const float cost_cut =
|
|
params->use_topology_distance ?
|
|
1.0f :
|
|
facetag_cut_cost_edge_uv(
|
|
f_a, f_b, l_iter, f_endpoints, aspect_v2, cd_loop_uv_offset);
|
|
const float cost_new = cost[f_a_index] + cost_cut;
|
|
|
|
if (cost[f_b_index] > cost_new) {
|
|
cost[f_b_index] = cost_new;
|
|
faces_prev[f_b_index] = f_a;
|
|
BLI_heapsimple_insert(heap, cost_new, f_b);
|
|
}
|
|
}
|
|
}
|
|
} while ((l_iter = l_iter->radial_next) != l_first);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (params->use_step_face) {
|
|
BMIter liter;
|
|
BMLoop *l_a;
|
|
|
|
BM_ITER_ELEM (l_a, &liter, f_a, BM_LOOPS_OF_FACE) {
|
|
BMIter litersub;
|
|
BMLoop *l_b;
|
|
BM_ITER_ELEM (l_b, &litersub, l_a->v, BM_LOOPS_OF_VERT) {
|
|
if ((l_a != l_b) && !BM_loop_share_edge_check(l_a, l_b)) {
|
|
BMFace *f_b = l_b->f;
|
|
if (!BM_elem_flag_test(f_b, BM_ELEM_TAG)) {
|
|
if (BM_loop_uv_share_vert_check(l_a, l_b, cd_loop_uv_offset)) {
|
|
/* We know 'f_b' is not visited, check it out! */
|
|
const int f_b_index = BM_elem_index_get(f_b);
|
|
const float cost_cut =
|
|
params->use_topology_distance ?
|
|
1.0f :
|
|
facetag_cut_cost_vert_uv(
|
|
f_a, f_b, l_a, f_endpoints, aspect_v2, cd_loop_uv_offset);
|
|
const float cost_new = cost[f_a_index] + cost_cut;
|
|
|
|
if (cost[f_b_index] > cost_new) {
|
|
cost[f_b_index] = cost_new;
|
|
faces_prev[f_b_index] = f_a;
|
|
BLI_heapsimple_insert(heap, cost_new, f_b);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct LinkNode *BM_mesh_calc_path_uv_face(BMesh *bm,
|
|
BMFace *f_src,
|
|
BMFace *f_dst,
|
|
const struct BMCalcPathUVParams *params,
|
|
bool (*filter_fn)(BMFace *, void *),
|
|
void *user_data)
|
|
{
|
|
const float aspect_v2[2] = {1.0f, 1.0f / params->aspect_y};
|
|
LinkNode *path = NULL;
|
|
/* BM_ELEM_TAG flag is used to store visited edges */
|
|
BMIter fiter;
|
|
HeapSimple *heap;
|
|
float *cost;
|
|
BMFace **faces_prev;
|
|
int i = 0, totface;
|
|
|
|
/* Start measuring face path at the face edges, ignoring their centers. */
|
|
const void *const f_endpoints[2] = {f_src, f_dst};
|
|
|
|
/* NOTE: would pass BM_EDGE except we are looping over all faces anyway. */
|
|
// BM_mesh_elem_index_ensure(bm, BM_LOOP); // NOT NEEDED FOR FACETAG
|
|
|
|
{
|
|
BMFace *f;
|
|
BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
|
|
BM_elem_flag_set(f, BM_ELEM_TAG, !filter_fn(f, user_data));
|
|
BM_elem_index_set(f, i); /* set_inline */
|
|
i += 1;
|
|
}
|
|
bm->elem_index_dirty &= ~BM_FACE;
|
|
}
|
|
|
|
/* Allocate. */
|
|
totface = bm->totface;
|
|
faces_prev = MEM_callocN(sizeof(*faces_prev) * totface, __func__);
|
|
cost = MEM_mallocN(sizeof(*cost) * totface, __func__);
|
|
|
|
copy_vn_fl(cost, totface, COST_INIT_MAX);
|
|
|
|
/* Regular dijkstra shortest path, but over UV faces instead of vertices. */
|
|
heap = BLI_heapsimple_new();
|
|
BLI_heapsimple_insert(heap, 0.0f, f_src);
|
|
cost[BM_elem_index_get(f_src)] = 0.0f;
|
|
|
|
BMFace *f = NULL;
|
|
while (!BLI_heapsimple_is_empty(heap)) {
|
|
f = BLI_heapsimple_pop_min(heap);
|
|
|
|
if (f == f_dst) {
|
|
break;
|
|
}
|
|
|
|
if (!BM_elem_flag_test(f, BM_ELEM_TAG)) {
|
|
/* Adjacent loops are tagged while stepping to avoid 2x loops. */
|
|
BM_elem_flag_enable(f, BM_ELEM_TAG);
|
|
facetag_add_adjacent_uv(heap, f, faces_prev, cost, f_endpoints, aspect_v2, params);
|
|
}
|
|
}
|
|
|
|
if (f == f_dst) {
|
|
do {
|
|
BLI_linklist_prepend(&path, f);
|
|
} while ((f = faces_prev[BM_elem_index_get(f)]));
|
|
}
|
|
|
|
MEM_freeN(faces_prev);
|
|
MEM_freeN(cost);
|
|
BLI_heapsimple_free(heap, NULL);
|
|
|
|
return path;
|
|
}
|
|
|
|
/** \} */
|