UV: add geometry driven uv relax brush
Differential Revision: https://developer.blender.org/D15530
This commit is contained in:
parent
29c1d736c4
commit
74ea0bee9c
|
@ -133,6 +133,7 @@ void EDBM_update(struct Mesh *me, const struct EDBMUpdate_Params *params);
|
|||
void EDBM_update_extern(struct Mesh *me, bool do_tessellation, bool is_destructive);
|
||||
|
||||
/**
|
||||
*
|
||||
* A specialized vert map used by stitch operator.
|
||||
*/
|
||||
struct UvElementMap *BM_uv_element_map_create(struct BMesh *bm,
|
||||
|
@ -141,11 +142,13 @@ struct UvElementMap *BM_uv_element_map_create(struct BMesh *bm,
|
|||
bool use_winding,
|
||||
bool do_islands);
|
||||
void BM_uv_element_map_free(struct UvElementMap *element_map);
|
||||
struct UvElement *BM_uv_element_get(struct UvElementMap *map,
|
||||
struct BMFace *efa,
|
||||
struct BMLoop *l);
|
||||
struct UvElement *BM_uv_element_get(const struct UvElementMap *map,
|
||||
const struct BMFace *efa,
|
||||
const struct BMLoop *l);
|
||||
struct UvElement *BM_uv_element_get_head(struct UvElementMap *map, struct UvElement *child);
|
||||
|
||||
struct UvElement **BM_uv_element_map_ensure_head_table(struct UvElementMap *element_map);
|
||||
|
||||
/**
|
||||
* Can we edit UV's for this mesh?
|
||||
*/
|
||||
|
|
|
@ -593,10 +593,10 @@ UvMapVert *BM_uv_vert_map_at_index(UvVertMap *vmap, uint v)
|
|||
return vmap->vert[v];
|
||||
}
|
||||
|
||||
static void bm_uv_ensure_head_table(UvElementMap *element_map)
|
||||
struct UvElement **BM_uv_element_map_ensure_head_table(struct UvElementMap *element_map)
|
||||
{
|
||||
if (element_map->head_table) {
|
||||
return;
|
||||
return element_map->head_table;
|
||||
}
|
||||
|
||||
/* For each UvElement, locate the "separate" UvElement that precedes it in the linked list. */
|
||||
|
@ -616,6 +616,7 @@ static void bm_uv_ensure_head_table(UvElementMap *element_map)
|
|||
}
|
||||
}
|
||||
}
|
||||
return element_map->head_table;
|
||||
}
|
||||
|
||||
#define INVALID_ISLAND ((unsigned int)-1)
|
||||
|
@ -645,7 +646,7 @@ static int bm_uv_edge_select_build_islands(UvElementMap *element_map,
|
|||
bool uv_selected,
|
||||
int cd_loop_uv_offset)
|
||||
{
|
||||
bm_uv_ensure_head_table(element_map);
|
||||
BM_uv_element_map_ensure_head_table(element_map);
|
||||
|
||||
int total_uvs = element_map->total_uvs;
|
||||
|
||||
|
@ -1070,7 +1071,7 @@ void BM_uv_element_map_free(UvElementMap *element_map)
|
|||
}
|
||||
}
|
||||
|
||||
UvElement *BM_uv_element_get(UvElementMap *element_map, BMFace *efa, BMLoop *l)
|
||||
UvElement *BM_uv_element_get(const UvElementMap *element_map, const BMFace *efa, const BMLoop *l)
|
||||
{
|
||||
UvElement *element = element_map->vertex[BM_elem_index_get(l->v)];
|
||||
while (element) {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_math_base_safe.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_brush_types.h"
|
||||
|
@ -43,6 +43,11 @@
|
|||
|
||||
#include "UI_view2d.h"
|
||||
|
||||
/* When set, the UV element is on the boundary of the graph.
|
||||
* i.e. Instead of a 2-dimensional laplace operator, use a 1-dimensional version.
|
||||
* Visually, UV elements on the graph boundary appear as borders of the UV Island. */
|
||||
#define MARK_BOUNDARY 1
|
||||
|
||||
typedef struct UvAdjacencyElement {
|
||||
/* pointer to original uvelement */
|
||||
UvElement *element;
|
||||
|
@ -230,6 +235,13 @@ static void HC_relaxation_iteration_uv(BMEditMesh *em,
|
|||
MEM_SAFE_FREE(tmp_uvdata);
|
||||
}
|
||||
|
||||
/* Legacy version which only does laplacian relaxation.
|
||||
* Probably a little faster as it caches UvEdges.
|
||||
* Mostly preserved for comparison with `HC_relaxation_iteration_uv`.
|
||||
* Once the HC method has been merged into `relaxation_iteration_uv`,
|
||||
* all the `HC_*` and `laplacian_*` specific functions can probably be removed.
|
||||
*/
|
||||
|
||||
static void laplacian_relaxation_iteration_uv(BMEditMesh *em,
|
||||
UvSculptData *sculptdata,
|
||||
const float mouse_coord[2],
|
||||
|
@ -306,6 +318,151 @@ static void laplacian_relaxation_iteration_uv(BMEditMesh *em,
|
|||
MEM_SAFE_FREE(tmp_uvdata);
|
||||
}
|
||||
|
||||
static void add_weighted_edge(float (*delta_buf)[3],
|
||||
const UvElement *storage,
|
||||
const UvElement *ele_next,
|
||||
const UvElement *ele_prev,
|
||||
const MLoopUV *luv_next,
|
||||
const MLoopUV *luv_prev,
|
||||
const float weight)
|
||||
{
|
||||
float delta[2];
|
||||
sub_v2_v2v2(delta, luv_next->uv, luv_prev->uv);
|
||||
|
||||
bool code1 = (ele_prev->flag & MARK_BOUNDARY);
|
||||
bool code2 = (ele_next->flag & MARK_BOUNDARY);
|
||||
if (code1 || (code1 == code2)) {
|
||||
int index_next = ele_next - storage;
|
||||
delta_buf[index_next][0] -= delta[0] * weight;
|
||||
delta_buf[index_next][1] -= delta[1] * weight;
|
||||
delta_buf[index_next][2] += fabsf(weight);
|
||||
}
|
||||
if (code2 || (code1 == code2)) {
|
||||
int index_prev = ele_prev - storage;
|
||||
delta_buf[index_prev][0] += delta[0] * weight;
|
||||
delta_buf[index_prev][1] += delta[1] * weight;
|
||||
delta_buf[index_prev][2] += fabsf(weight);
|
||||
}
|
||||
}
|
||||
|
||||
static float tri_weight_v3(int method, const float *v1, const float *v2, const float *v3)
|
||||
{
|
||||
switch (method) {
|
||||
case UV_SCULPT_TOOL_RELAX_LAPLACIAN:
|
||||
case UV_SCULPT_TOOL_RELAX_HC:
|
||||
return 1.0f;
|
||||
case UV_SCULPT_TOOL_RELAX_COTAN:
|
||||
return cotangent_tri_weight_v3(v1, v2, v3);
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
static void relaxation_iteration_uv(BMEditMesh *em,
|
||||
UvSculptData *sculptdata,
|
||||
const float mouse_coord[2],
|
||||
const float alpha,
|
||||
const float radius_squared,
|
||||
const float aspect_ratio,
|
||||
const int method)
|
||||
{
|
||||
if (method == UV_SCULPT_TOOL_RELAX_HC) {
|
||||
HC_relaxation_iteration_uv(em, sculptdata, mouse_coord, alpha, radius_squared, aspect_ratio);
|
||||
return;
|
||||
}
|
||||
if (method == UV_SCULPT_TOOL_RELAX_LAPLACIAN) {
|
||||
laplacian_relaxation_iteration_uv(
|
||||
em, sculptdata, mouse_coord, alpha, radius_squared, aspect_ratio);
|
||||
return;
|
||||
}
|
||||
|
||||
struct UvElement **head_table = BM_uv_element_map_ensure_head_table(sculptdata->elementMap);
|
||||
|
||||
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
|
||||
BLI_assert(cd_loop_uv_offset >= 0);
|
||||
|
||||
const int total_uvs = sculptdata->elementMap->total_uvs;
|
||||
float(*delta_buf)[3] = (float(*)[3])MEM_callocN(total_uvs * sizeof(float[3]), __func__);
|
||||
|
||||
const UvElement *storage = sculptdata->elementMap->storage;
|
||||
for (int j = 0; j < total_uvs; j++) {
|
||||
const UvElement *ele_curr = storage + j;
|
||||
const BMFace *efa = ele_curr->l->f;
|
||||
const UvElement *ele_next = BM_uv_element_get(sculptdata->elementMap, efa, ele_curr->l->next);
|
||||
const UvElement *ele_prev = BM_uv_element_get(sculptdata->elementMap, efa, ele_curr->l->prev);
|
||||
|
||||
const float *v_curr_co = ele_curr->l->v->co;
|
||||
const float *v_prev_co = ele_prev->l->v->co;
|
||||
const float *v_next_co = ele_next->l->v->co;
|
||||
|
||||
const MLoopUV *luv_curr = BM_ELEM_CD_GET_VOID_P(ele_curr->l, cd_loop_uv_offset);
|
||||
const MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(ele_next->l, cd_loop_uv_offset);
|
||||
const MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(ele_prev->l, cd_loop_uv_offset);
|
||||
|
||||
const UvElement *head_curr = head_table[ele_curr - sculptdata->elementMap->storage];
|
||||
const UvElement *head_next = head_table[ele_next - sculptdata->elementMap->storage];
|
||||
const UvElement *head_prev = head_table[ele_prev - sculptdata->elementMap->storage];
|
||||
|
||||
/* If the mesh is triangulated with no boundaries, only one edge is required. */
|
||||
const float weight_curr = tri_weight_v3(method, v_curr_co, v_prev_co, v_next_co);
|
||||
add_weighted_edge(delta_buf, storage, head_next, head_prev, luv_next, luv_prev, weight_curr);
|
||||
|
||||
/* Triangulated with a boundary? We need the incoming edges to solve the boundary. */
|
||||
const float weight_prev = tri_weight_v3(method, v_prev_co, v_curr_co, v_next_co);
|
||||
add_weighted_edge(delta_buf, storage, head_next, head_curr, luv_next, luv_curr, weight_prev);
|
||||
|
||||
if (method == UV_SCULPT_TOOL_RELAX_LAPLACIAN) {
|
||||
/* Laplacian method has zero weights on virtual edges. */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Meshes with quads (or other n-gons) need "virtual" edges too. */
|
||||
const float weight_next = tri_weight_v3(method, v_next_co, v_curr_co, v_prev_co);
|
||||
add_weighted_edge(delta_buf, storage, head_prev, head_curr, luv_prev, luv_curr, weight_next);
|
||||
}
|
||||
|
||||
Brush *brush = BKE_paint_brush(sculptdata->uvsculpt);
|
||||
for (int i = 0; i < sculptdata->totalUniqueUvs; i++) {
|
||||
UvAdjacencyElement *adj_el = &sculptdata->uv[i];
|
||||
if (adj_el->is_locked) {
|
||||
continue; /* Locked UVs can't move. */
|
||||
}
|
||||
|
||||
/* Is UV within brush's influence? */
|
||||
float diff[2];
|
||||
sub_v2_v2v2(diff, adj_el->uv, mouse_coord);
|
||||
diff[1] /= aspect_ratio;
|
||||
const float dist_squared = len_squared_v2(diff);
|
||||
if (dist_squared > radius_squared) {
|
||||
continue;
|
||||
}
|
||||
const float strength = alpha * BKE_brush_curve_strength_clamped(
|
||||
brush, sqrtf(dist_squared), sqrtf(radius_squared));
|
||||
|
||||
const float *delta_sum = delta_buf[adj_el->element - storage];
|
||||
MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(adj_el->element->l, cd_loop_uv_offset);
|
||||
BLI_assert(adj_el->uv == luv->uv); /* Only true for head. */
|
||||
adj_el->uv[0] = luv->uv[0] + strength * safe_divide(delta_sum[0], delta_sum[2]);
|
||||
adj_el->uv[1] = luv->uv[1] + strength * safe_divide(delta_sum[1], delta_sum[2]);
|
||||
|
||||
apply_sculpt_data_constraints(sculptdata, adj_el->uv);
|
||||
|
||||
/* Copy UV co-ordinates to all UvElements. */
|
||||
UvElement *tail = adj_el->element;
|
||||
while (tail) {
|
||||
MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(tail->l, cd_loop_uv_offset);
|
||||
copy_v2_v2(luv->uv, adj_el->uv);
|
||||
tail = tail->next;
|
||||
if (tail && tail->separate) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MEM_SAFE_FREE(delta_buf);
|
||||
}
|
||||
|
||||
static void uv_sculpt_stroke_apply(bContext *C,
|
||||
wmOperator *op,
|
||||
const wmEvent *event,
|
||||
|
@ -383,16 +540,11 @@ static void uv_sculpt_stroke_apply(bContext *C,
|
|||
}
|
||||
|
||||
/*
|
||||
* Smooth Tool
|
||||
* Relax Tool
|
||||
*/
|
||||
else if (tool == UV_SCULPT_TOOL_RELAX) {
|
||||
uint method = toolsettings->uv_relax_method;
|
||||
if (method == UV_SCULPT_TOOL_RELAX_HC) {
|
||||
HC_relaxation_iteration_uv(em, sculptdata, co, alpha, radius, aspectRatio);
|
||||
}
|
||||
else {
|
||||
laplacian_relaxation_iteration_uv(em, sculptdata, co, alpha, radius, aspectRatio);
|
||||
}
|
||||
relaxation_iteration_uv(
|
||||
em, sculptdata, co, alpha, radius, aspectRatio, toolsettings->uv_relax_method);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -476,6 +628,17 @@ static bool uv_edge_compare(const void *a, const void *b)
|
|||
return true;
|
||||
}
|
||||
|
||||
static void set_element_flag(UvElement *element, const int flag)
|
||||
{
|
||||
while (element) {
|
||||
element->flag |= flag;
|
||||
element = element->next;
|
||||
if (!element || element->separate) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
|
@ -666,6 +829,8 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
|
|||
data->uv[data->uvedges[i].uv1].is_locked = true;
|
||||
data->uv[data->uvedges[i].uv2].is_locked = true;
|
||||
}
|
||||
set_element_flag(data->uv[data->uvedges[i].uv1].element, MARK_BOUNDARY);
|
||||
set_element_flag(data->uv[data->uvedges[i].uv2].element, MARK_BOUNDARY);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -820,6 +820,7 @@ typedef struct RenderProfile {
|
|||
/** #ToolSettings.uv_relax_method */
|
||||
#define UV_SCULPT_TOOL_RELAX_LAPLACIAN 1
|
||||
#define UV_SCULPT_TOOL_RELAX_HC 2
|
||||
#define UV_SCULPT_TOOL_RELAX_COTAN 3
|
||||
|
||||
/* Stereo Flags */
|
||||
#define STEREO_RIGHT_NAME "right"
|
||||
|
|
|
@ -88,6 +88,11 @@ static const EnumPropertyItem uv_sculpt_relaxation_items[] = {
|
|||
"Laplacian",
|
||||
"Use Laplacian method for relaxation"},
|
||||
{UV_SCULPT_TOOL_RELAX_HC, "HC", 0, "HC", "Use HC method for relaxation"},
|
||||
{UV_SCULPT_TOOL_RELAX_COTAN,
|
||||
"COTAN",
|
||||
0,
|
||||
"Geometry",
|
||||
"Use Geometry (cotangent) relaxation, making UV's follow the underlying 3D geometry"},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit da8bdd7244c7b6c2eadf4c949ff391d0cc430275
|
||||
Subproject commit 2a541f164a222ef7bcd036d37687738acee8d946
|
Loading…
Reference in New Issue