UV: improve uv sculpt tools with boundary support and pin support

Fix boundary conditions for the Relax UV tool with the Laplacian method.

Add Pinned UV support to Relax UV tool (all modes) and Pinch UV tool.

Differential Revision: https://developer.blender.org/D15669
This commit is contained in:
Chris Blackbourn 2022-08-16 17:13:29 +12:00
parent 8841bd9660
commit 3c351da89f
1 changed files with 53 additions and 56 deletions

View File

@ -42,23 +42,22 @@
#include "UI_view2d.h"
#define MARK_BOUNDARY 1
typedef struct UvAdjacencyElement {
/* pointer to original uvelement */
UvElement *element;
/* uv pointer for convenience. Caution, this points to the original UVs! */
float *uv;
/* general use flag (Used to check if Element is boundary here) */
char flag;
/* Are we on locked in place? */
bool is_locked;
/* Are we on the boundary? */
bool is_boundary;
} UvAdjacencyElement;
typedef struct UvEdge {
uint uv1;
uint uv2;
/* general use flag
* (Used to check if edge is boundary here, and propagates to adjacency elements) */
char flag;
/* Are we in the interior? */
bool is_interior;
} UvEdge;
typedef struct UVInitialStrokeElement {
@ -170,17 +169,14 @@ static void HC_relaxation_iteration_uv(BMEditMesh *em,
}
for (i = 0; i < sculptdata->totalUniqueUvs; i++) {
float dist;
/* This is supposed to happen only if "Pin Edges" is on,
* since we have initialization on stroke start.
* If ever uv brushes get their own mode we should check for toolsettings option too. */
if (sculptdata->uv[i].flag & MARK_BOUNDARY) {
if (sculptdata->uv[i].is_locked) {
continue;
}
sub_v2_v2v2(diff, sculptdata->uv[i].uv, mouse_coord);
diff[1] /= aspectRatio;
if ((dist = dot_v2v2(diff, diff)) <= radius) {
float dist = dot_v2v2(diff, diff);
if (dist <= radius) {
UvElement *element;
float strength;
strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius_root);
@ -233,11 +229,16 @@ static void laplacian_relaxation_iteration_uv(BMEditMesh *em,
/* counting neighbors */
for (i = 0; i < sculptdata->totalUvEdges; i++) {
UvEdge *tmpedge = sculptdata->uvedges + i;
tmp_uvdata[tmpedge->uv1].ncounter++;
tmp_uvdata[tmpedge->uv2].ncounter++;
add_v2_v2(tmp_uvdata[tmpedge->uv2].sum_co, sculptdata->uv[tmpedge->uv1].uv);
add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_co, sculptdata->uv[tmpedge->uv2].uv);
bool code1 = sculptdata->uv[sculptdata->uvedges[i].uv1].is_boundary;
bool code2 = sculptdata->uv[sculptdata->uvedges[i].uv2].is_boundary;
if (code1 || (code1 == code2)) {
tmp_uvdata[tmpedge->uv2].ncounter++;
add_v2_v2(tmp_uvdata[tmpedge->uv2].sum_co, sculptdata->uv[tmpedge->uv1].uv);
}
if (code2 || (code1 == code2)) {
tmp_uvdata[tmpedge->uv1].ncounter++;
add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_co, sculptdata->uv[tmpedge->uv2].uv);
}
}
/* Original Laplacian algorithm included removal of normal component of translation.
@ -248,17 +249,14 @@ static void laplacian_relaxation_iteration_uv(BMEditMesh *em,
}
for (i = 0; i < sculptdata->totalUniqueUvs; i++) {
float dist;
/* This is supposed to happen only if "Pin Edges" is on,
* since we have initialization on stroke start.
* If ever uv brushes get their own mode we should check for toolsettings option too. */
if (sculptdata->uv[i].flag & MARK_BOUNDARY) {
if (sculptdata->uv[i].is_locked) {
continue;
}
sub_v2_v2v2(diff, sculptdata->uv[i].uv, mouse_coord);
diff[1] /= aspectRatio;
if ((dist = dot_v2v2(diff, diff)) <= radius) {
float dist = dot_v2v2(diff, diff);
if (dist <= radius) {
UvElement *element;
float strength;
strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius_root);
@ -327,17 +325,15 @@ static void uv_sculpt_stroke_apply(bContext *C,
int i;
alpha *= invert;
for (i = 0; i < sculptdata->totalUniqueUvs; i++) {
float dist, diff[2];
/* This is supposed to happen only if "Lock Borders" is on,
* since we have initialization on stroke start.
* If ever uv brushes get their own mode we should check for toolsettings option too. */
if (sculptdata->uv[i].flag & MARK_BOUNDARY) {
if (sculptdata->uv[i].is_locked) {
continue;
}
float diff[2];
sub_v2_v2v2(diff, sculptdata->uv[i].uv, co);
diff[1] /= aspectRatio;
if ((dist = dot_v2v2(diff, diff)) <= radius) {
float dist = dot_v2v2(diff, diff);
if (dist <= radius) {
UvElement *element;
float strength;
strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius_root);
@ -481,8 +477,6 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
bool do_island_optimization = !(ts->uv_sculpt_settings & UV_SCULPT_ALL_ISLANDS);
int island_index = 0;
/* Holds, for each UvElement in elementMap, an index of its unique UV. */
int *uniqueUv;
data->tool = (RNA_enum_get(op->ptr, "mode") == BRUSH_STROKE_SMOOTH) ?
UV_SCULPT_TOOL_RELAX :
ts->uvsculpt->paint.brush->uv_sculpt_tool;
@ -522,12 +516,13 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
}
/* Allocate the unique uv buffers */
data->uv = MEM_mallocN(sizeof(*data->uv) * unique_uvs, "uv_brush_unique_uvs");
uniqueUv = MEM_mallocN(sizeof(*uniqueUv) * data->elementMap->total_uvs,
"uv_brush_unique_uv_map");
data->uv = MEM_callocN(sizeof(*data->uv) * unique_uvs, "uv_brush_unique_uvs");
/* Holds, for each UvElement in elementMap, an index of its unique UV. */
int *uniqueUv = MEM_mallocN(sizeof(*uniqueUv) * data->elementMap->total_uvs,
"uv_brush_unique_uv_map");
edgeHash = BLI_ghash_new(uv_edge_hash, uv_edge_compare, "uv_brush_edge_hash");
/* we have at most totalUVs edges */
edges = MEM_mallocN(sizeof(*edges) * data->elementMap->total_uvs, "uv_brush_all_edges");
edges = MEM_callocN(sizeof(*edges) * data->elementMap->total_uvs, "uv_brush_all_edges");
if (!data->uv || !uniqueUv || !edgeHash || !edges) {
MEM_SAFE_FREE(edges);
MEM_SAFE_FREE(uniqueUv);
@ -559,8 +554,12 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
counter++;
data->uv[counter].element = element;
data->uv[counter].flag = 0;
data->uv[counter].uv = luv->uv;
if (data->tool != UV_SCULPT_TOOL_GRAB) {
if (luv->flag & MLOOPUV_PINNED) {
data->uv[counter].is_locked = true;
}
}
}
/* Pointer arithmetic to the rescue, as always :). */
uniqueUv[element - data->elementMap->storage] = counter;
@ -576,7 +575,6 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
data->elementMap, efa, l, island_index, do_island_optimization);
int offset2, itmp2 = uv_element_offset_from_face_get(
data->elementMap, efa, l->next, island_index, do_island_optimization);
char *flag;
/* Skip edge if not found(unlikely) or not on valid island */
if (itmp1 == -1 || itmp2 == -1) {
@ -586,7 +584,6 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
offset1 = uniqueUv[itmp1];
offset2 = uniqueUv[itmp2];
edges[counter].flag = 0;
/* Using an order policy, sort UV's according to address space.
* This avoids having two different UvEdges with the same UV's on different positions. */
if (offset1 < offset2) {
@ -597,15 +594,13 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
edges[counter].uv1 = offset2;
edges[counter].uv2 = offset1;
}
/* Hack! Set the value of the key to its flag.
* Now we can set the flag when an edge exists twice :) */
flag = BLI_ghash_lookup(edgeHash, &edges[counter]);
if (flag) {
*flag = 1;
UvEdge *prev_edge = BLI_ghash_lookup(edgeHash, &edges[counter]);
if (prev_edge) {
prev_edge->is_interior = true;
edges[counter].is_interior = true;
}
else {
/* Hack mentioned */
BLI_ghash_insert(edgeHash, &edges[counter], &edges[counter].flag);
BLI_ghash_insert(edgeHash, &edges[counter], &edges[counter]);
}
counter++;
}
@ -614,7 +609,7 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
MEM_SAFE_FREE(uniqueUv);
/* Allocate connectivity data, we allocate edges once */
data->uvedges = MEM_mallocN(sizeof(*data->uvedges) * BLI_ghash_len(edgeHash),
data->uvedges = MEM_callocN(sizeof(*data->uvedges) * BLI_ghash_len(edgeHash),
"uv_brush_edge_connectivity_data");
if (!data->uvedges) {
BLI_ghash_free(edgeHash, NULL, NULL);
@ -637,11 +632,13 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
MEM_SAFE_FREE(edges);
/* transfer boundary edge property to UV's */
if (ts->uv_sculpt_settings & UV_SCULPT_LOCK_BORDERS) {
for (int i = 0; i < data->totalUvEdges; i++) {
if (!data->uvedges[i].flag) {
data->uv[data->uvedges[i].uv1].flag |= MARK_BOUNDARY;
data->uv[data->uvedges[i].uv2].flag |= MARK_BOUNDARY;
for (int i = 0; i < data->totalUvEdges; i++) {
if (!data->uvedges[i].is_interior) {
data->uv[data->uvedges[i].uv1].is_boundary = true;
data->uv[data->uvedges[i].uv2].is_boundary = true;
if (ts->uv_sculpt_settings & UV_SCULPT_LOCK_BORDERS) {
data->uv[data->uvedges[i].uv1].is_locked = true;
data->uv[data->uvedges[i].uv2].is_locked = true;
}
}
}
@ -684,16 +681,16 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
copy_v2_v2(data->initial_stroke->init_coord, co);
counter = 0;
for (int i = 0; i < data->totalUniqueUvs; i++) {
float dist, diff[2];
if (data->uv[i].flag & MARK_BOUNDARY) {
if (data->uv[i].is_locked) {
continue;
}
float diff[2];
sub_v2_v2v2(diff, data->uv[i].uv, co);
diff[1] /= aspectRatio;
if ((dist = dot_v2v2(diff, diff)) <= radius) {
float dist = dot_v2v2(diff, diff);
if (dist <= radius) {
float strength;
strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius_root);