Texture Paint: round corners of dilated UV seams
This fixes the issue of long spikes on sharp corners by rounding them, guaranteeing that the bleed will never exceed the bleed distance set by the user in any part of the mesh. See: D4437
This commit is contained in:

committed by
Campbell Barton

parent
871248a77d
commit
10d9a2494f
@@ -231,6 +231,12 @@ typedef struct ProjStrokeHandle {
|
|||||||
Brush *brush;
|
Brush *brush;
|
||||||
} ProjStrokeHandle;
|
} ProjStrokeHandle;
|
||||||
|
|
||||||
|
typedef struct LoopSeamData {
|
||||||
|
float seam_uvs[2][2];
|
||||||
|
float seam_puvs[2][2];
|
||||||
|
float corner_dist_sq[2];
|
||||||
|
} LoopSeamData;
|
||||||
|
|
||||||
/* Main projection painting struct passed to all projection painting functions */
|
/* Main projection painting struct passed to all projection painting functions */
|
||||||
typedef struct ProjPaintState {
|
typedef struct ProjPaintState {
|
||||||
View3D *v3d;
|
View3D *v3d;
|
||||||
@@ -333,6 +339,7 @@ typedef struct ProjPaintState {
|
|||||||
bool is_maskbrush;
|
bool is_maskbrush;
|
||||||
#ifndef PROJ_DEBUG_NOSEAMBLEED
|
#ifndef PROJ_DEBUG_NOSEAMBLEED
|
||||||
float seam_bleed_px;
|
float seam_bleed_px;
|
||||||
|
float seam_bleed_px_sq;
|
||||||
#endif
|
#endif
|
||||||
/* clone vars */
|
/* clone vars */
|
||||||
float cloneOffset[2];
|
float cloneOffset[2];
|
||||||
@@ -391,7 +398,7 @@ typedef struct ProjPaintState {
|
|||||||
* helps as an extra validation step for seam detection. */
|
* helps as an extra validation step for seam detection. */
|
||||||
char *faceWindingFlags;
|
char *faceWindingFlags;
|
||||||
/** expanded UVs for faces to use as seams. */
|
/** expanded UVs for faces to use as seams. */
|
||||||
float (*loopSeamUVs)[2][2];
|
LoopSeamData (*loopSeamData);
|
||||||
/** Only needed for when seam_bleed_px is enabled, use to find UV seams. */
|
/** Only needed for when seam_bleed_px is enabled, use to find UV seams. */
|
||||||
LinkNode **vertFaces;
|
LinkNode **vertFaces;
|
||||||
/** Seams per vert, to find adjacent seams. */
|
/** Seams per vert, to find adjacent seams. */
|
||||||
@@ -1258,7 +1265,7 @@ static float compute_seam_normal(VertSeam *seam, VertSeam *adj, float r_no[2])
|
|||||||
* since the outset coords are a margin that keep an even distance from the original UV's,
|
* since the outset coords are a margin that keep an even distance from the original UV's,
|
||||||
* note that the image aspect is taken into account */
|
* note that the image aspect is taken into account */
|
||||||
static void uv_image_outset(
|
static void uv_image_outset(
|
||||||
const ProjPaintState *ps, float (*orig_uv)[2],
|
const ProjPaintState *ps, float (*orig_uv)[2], float (*puv)[2],
|
||||||
uint tri_index, const int ibuf_x, const int ibuf_y)
|
uint tri_index, const int ibuf_x, const int ibuf_y)
|
||||||
{
|
{
|
||||||
int fidx[2];
|
int fidx[2];
|
||||||
@@ -1266,23 +1273,13 @@ static void uv_image_outset(
|
|||||||
uint vert[2];
|
uint vert[2];
|
||||||
const MLoopTri *ltri = &ps->mlooptri_eval[tri_index];
|
const MLoopTri *ltri = &ps->mlooptri_eval[tri_index];
|
||||||
|
|
||||||
/* pixelspace uv's */
|
|
||||||
float puv[3][2];
|
|
||||||
float ibuf_inv[2];
|
float ibuf_inv[2];
|
||||||
|
|
||||||
ibuf_inv[0] = 1.0f / (float)ibuf_x;
|
ibuf_inv[0] = 1.0f / (float)ibuf_x;
|
||||||
ibuf_inv[1] = 1.0f / (float)ibuf_y;
|
ibuf_inv[1] = 1.0f / (float)ibuf_y;
|
||||||
|
|
||||||
puv[0][0] = orig_uv[0][0] * ibuf_x;
|
|
||||||
puv[0][1] = orig_uv[0][1] * ibuf_y;
|
|
||||||
|
|
||||||
puv[1][0] = orig_uv[1][0] * ibuf_x;
|
|
||||||
puv[1][1] = orig_uv[1][1] * ibuf_y;
|
|
||||||
|
|
||||||
puv[2][0] = orig_uv[2][0] * ibuf_x;
|
|
||||||
puv[2][1] = orig_uv[2][1] * ibuf_y;
|
|
||||||
|
|
||||||
for (fidx[0] = 0; fidx[0] < 3; fidx[0]++) {
|
for (fidx[0] = 0; fidx[0] < 3; fidx[0]++) {
|
||||||
|
LoopSeamData *seam_data;
|
||||||
float (*seam_uvs)[2];
|
float (*seam_uvs)[2];
|
||||||
float ang[2];
|
float ang[2];
|
||||||
|
|
||||||
@@ -1292,7 +1289,8 @@ static void uv_image_outset(
|
|||||||
|
|
||||||
loop_index = ltri->tri[fidx[0]];
|
loop_index = ltri->tri[fidx[0]];
|
||||||
|
|
||||||
seam_uvs = ps->loopSeamUVs[loop_index];
|
seam_data = &ps->loopSeamData[loop_index];
|
||||||
|
seam_uvs = seam_data->seam_uvs;
|
||||||
|
|
||||||
if (seam_uvs[0][0] != FLT_MAX) {
|
if (seam_uvs[0][0] != FLT_MAX) {
|
||||||
continue;
|
continue;
|
||||||
@@ -1308,18 +1306,27 @@ static void uv_image_outset(
|
|||||||
VertSeam *adj = find_adjacent_seam(ps, loop_index, vert[i], &seam);
|
VertSeam *adj = find_adjacent_seam(ps, loop_index, vert[i], &seam);
|
||||||
float no[2];
|
float no[2];
|
||||||
float len_fact;
|
float len_fact;
|
||||||
|
float tri_ang;
|
||||||
|
|
||||||
ang[i] = compute_seam_normal(seam, adj, no);
|
ang[i] = compute_seam_normal(seam, adj, no);
|
||||||
|
tri_ang = ang[i] - M_PI_2;
|
||||||
|
|
||||||
len_fact = cosf(ang[i] - M_PI_2);
|
if (tri_ang > 0.0f) {
|
||||||
|
const float dist = ps->seam_bleed_px * tanf(tri_ang);
|
||||||
|
seam_data->corner_dist_sq[i] = SQUARE(dist);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
seam_data->corner_dist_sq[i] = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
len_fact = cosf(tri_ang);
|
||||||
len_fact = UNLIKELY(len_fact < FLT_EPSILON) ? FLT_MAX : (1.0f / len_fact);
|
len_fact = UNLIKELY(len_fact < FLT_EPSILON) ? FLT_MAX : (1.0f / len_fact);
|
||||||
len_fact = MIN2(len_fact, 5.0f);
|
|
||||||
|
|
||||||
mul_v2_fl(no, ps->seam_bleed_px * len_fact);
|
mul_v2_fl(no, ps->seam_bleed_px * len_fact);
|
||||||
|
|
||||||
add_v2_v2v2(seam_uvs[i], puv[fidx[i]], no);
|
add_v2_v2v2(seam_data->seam_puvs[i], puv[fidx[i]], no);
|
||||||
|
|
||||||
mul_v2_v2(seam_uvs[i], ibuf_inv);
|
mul_v2_v2v2(seam_uvs[i], seam_data->seam_puvs[i], ibuf_inv);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle convergent normals (can self-intersect). */
|
/* Handle convergent normals (can self-intersect). */
|
||||||
@@ -3021,11 +3028,23 @@ static void project_paint_face_init(
|
|||||||
float seam_subsection[4][2];
|
float seam_subsection[4][2];
|
||||||
float fac1, fac2;
|
float fac1, fac2;
|
||||||
|
|
||||||
|
/* Pixelspace UVs. */
|
||||||
|
float lt_puv[3][2];
|
||||||
|
|
||||||
|
lt_puv[0][0] = lt_uv_pxoffset[0][0] * ibuf->x;
|
||||||
|
lt_puv[0][1] = lt_uv_pxoffset[0][1] * ibuf->y;
|
||||||
|
|
||||||
|
lt_puv[1][0] = lt_uv_pxoffset[1][0] * ibuf->x;
|
||||||
|
lt_puv[1][1] = lt_uv_pxoffset[1][1] * ibuf->y;
|
||||||
|
|
||||||
|
lt_puv[2][0] = lt_uv_pxoffset[2][0] * ibuf->x;
|
||||||
|
lt_puv[2][1] = lt_uv_pxoffset[2][1] * ibuf->y;
|
||||||
|
|
||||||
if ((ps->faceSeamFlags[tri_index] & PROJ_FACE_SEAM0) ||
|
if ((ps->faceSeamFlags[tri_index] & PROJ_FACE_SEAM0) ||
|
||||||
(ps->faceSeamFlags[tri_index] & PROJ_FACE_SEAM1) ||
|
(ps->faceSeamFlags[tri_index] & PROJ_FACE_SEAM1) ||
|
||||||
(ps->faceSeamFlags[tri_index] & PROJ_FACE_SEAM2))
|
(ps->faceSeamFlags[tri_index] & PROJ_FACE_SEAM2))
|
||||||
{
|
{
|
||||||
uv_image_outset(ps, lt_uv_pxoffset, tri_index, ibuf->x, ibuf->y);
|
uv_image_outset(ps, lt_uv_pxoffset, lt_puv, tri_index, ibuf->x, ibuf->y);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ps->loopSeamUVs cant be modified when threading, now this is done we can unlock. */
|
/* ps->loopSeamUVs cant be modified when threading, now this is done we can unlock. */
|
||||||
@@ -3056,6 +3075,8 @@ static void project_paint_face_init(
|
|||||||
/* Avoid div by zero. */
|
/* Avoid div by zero. */
|
||||||
if (len_squared_v2v2(vCoSS[fidx1], vCoSS[fidx2]) > FLT_EPSILON) {
|
if (len_squared_v2v2(vCoSS[fidx1], vCoSS[fidx2]) > FLT_EPSILON) {
|
||||||
uint loop_idx = ps->mlooptri_eval[tri_index].tri[fidx1];
|
uint loop_idx = ps->mlooptri_eval[tri_index].tri[fidx1];
|
||||||
|
LoopSeamData *seam_data = &ps->loopSeamData[loop_idx];
|
||||||
|
float (*seam_uvs)[2] = seam_data->seam_uvs;
|
||||||
|
|
||||||
if (is_ortho) {
|
if (is_ortho) {
|
||||||
fac1 = line_point_factor_v2(bucket_clip_edges[0], vCoSS[fidx1], vCoSS[fidx2]);
|
fac1 = line_point_factor_v2(bucket_clip_edges[0], vCoSS[fidx1], vCoSS[fidx2]);
|
||||||
@@ -3069,8 +3090,8 @@ static void project_paint_face_init(
|
|||||||
interp_v2_v2v2(seam_subsection[0], lt_uv_pxoffset[fidx1], lt_uv_pxoffset[fidx2], fac1);
|
interp_v2_v2v2(seam_subsection[0], lt_uv_pxoffset[fidx1], lt_uv_pxoffset[fidx2], fac1);
|
||||||
interp_v2_v2v2(seam_subsection[1], lt_uv_pxoffset[fidx1], lt_uv_pxoffset[fidx2], fac2);
|
interp_v2_v2v2(seam_subsection[1], lt_uv_pxoffset[fidx1], lt_uv_pxoffset[fidx2], fac2);
|
||||||
|
|
||||||
interp_v2_v2v2(seam_subsection[2], ps->loopSeamUVs[loop_idx][0], ps->loopSeamUVs[loop_idx][1], fac2);
|
interp_v2_v2v2(seam_subsection[2], seam_uvs[0], seam_uvs[1], fac2);
|
||||||
interp_v2_v2v2(seam_subsection[3], ps->loopSeamUVs[loop_idx][0], ps->loopSeamUVs[loop_idx][1], fac1);
|
interp_v2_v2v2(seam_subsection[3], seam_uvs[0], seam_uvs[1], fac1);
|
||||||
|
|
||||||
/* if the bucket_clip_edges values Z values was kept we could avoid this
|
/* if the bucket_clip_edges values Z values was kept we could avoid this
|
||||||
* Inset needs to be added so occlusion tests wont hit adjacent faces */
|
* Inset needs to be added so occlusion tests wont hit adjacent faces */
|
||||||
@@ -3089,19 +3110,35 @@ static void project_paint_face_init(
|
|||||||
|
|
||||||
has_x_isect = 0;
|
has_x_isect = 0;
|
||||||
for (x = bounds_px.xmin; x < bounds_px.xmax; x++) {
|
for (x = bounds_px.xmin; x < bounds_px.xmax; x++) {
|
||||||
|
float puv[2] = {(float)x, (float)y};
|
||||||
bool in_bounds;
|
bool in_bounds;
|
||||||
//uv[0] = (((float)x) + 0.5f) / (float)ibuf->x;
|
//uv[0] = (((float)x) + 0.5f) / (float)ibuf->x;
|
||||||
/* use offset uvs instead */
|
/* use offset uvs instead */
|
||||||
uv[0] = (float)x / ibuf_xf;
|
uv[0] = (float)x / ibuf_xf;
|
||||||
|
|
||||||
/* test we're inside uvspace bucket and triangle bounds */
|
/* test we're inside uvspace bucket and triangle bounds */
|
||||||
if (equals_v2v2(ps->loopSeamUVs[loop_idx][0], ps->loopSeamUVs[loop_idx][1])) {
|
if (equals_v2v2(seam_uvs[0], seam_uvs[1])) {
|
||||||
in_bounds = isect_point_tri_v2(uv, UNPACK3(seam_subsection));
|
in_bounds = isect_point_tri_v2(uv, UNPACK3(seam_subsection));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
in_bounds = isect_point_quad_v2(uv, UNPACK4(seam_subsection));
|
in_bounds = isect_point_quad_v2(uv, UNPACK4(seam_subsection));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (in_bounds) {
|
||||||
|
if ((seam_data->corner_dist_sq[0] > 0.0f) &&
|
||||||
|
(len_squared_v2v2(puv, seam_data->seam_puvs[0]) < seam_data->corner_dist_sq[0]) &&
|
||||||
|
(len_squared_v2v2(puv, lt_puv[fidx1]) > ps->seam_bleed_px_sq))
|
||||||
|
{
|
||||||
|
in_bounds = false;
|
||||||
|
}
|
||||||
|
else if ((seam_data->corner_dist_sq[1] > 0.0f) &&
|
||||||
|
(len_squared_v2v2(puv, seam_data->seam_puvs[1]) < seam_data->corner_dist_sq[1]) &&
|
||||||
|
(len_squared_v2v2(puv, lt_puv[fidx2]) > ps->seam_bleed_px_sq))
|
||||||
|
{
|
||||||
|
in_bounds = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (in_bounds) {
|
if (in_bounds) {
|
||||||
float pixel_on_edge[4];
|
float pixel_on_edge[4];
|
||||||
float fac;
|
float fac;
|
||||||
@@ -3394,9 +3431,9 @@ static void project_paint_delayed_face_init(ProjPaintState *ps, const MLoopTri *
|
|||||||
#ifndef PROJ_DEBUG_NOSEAMBLEED
|
#ifndef PROJ_DEBUG_NOSEAMBLEED
|
||||||
if (ps->seam_bleed_px > 0.0f) {
|
if (ps->seam_bleed_px > 0.0f) {
|
||||||
/* set as uninitialized */
|
/* set as uninitialized */
|
||||||
**ps->loopSeamUVs[lt->tri[0]] = FLT_MAX;
|
ps->loopSeamData[lt->tri[0]].seam_uvs[0][0] = FLT_MAX;
|
||||||
**ps->loopSeamUVs[lt->tri[1]] = FLT_MAX;
|
ps->loopSeamData[lt->tri[1]].seam_uvs[0][0] = FLT_MAX;
|
||||||
**ps->loopSeamUVs[lt->tri[2]] = FLT_MAX;
|
ps->loopSeamData[lt->tri[2]].seam_uvs[0][0] = FLT_MAX;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -3633,7 +3670,7 @@ static void proj_paint_state_seam_bleed_init(ProjPaintState *ps)
|
|||||||
ps->vertFaces = MEM_callocN(sizeof(LinkNode *) * ps->totvert_eval, "paint-vertFaces");
|
ps->vertFaces = MEM_callocN(sizeof(LinkNode *) * ps->totvert_eval, "paint-vertFaces");
|
||||||
ps->faceSeamFlags = MEM_callocN(sizeof(ushort) * ps->totlooptri_eval, "paint-faceSeamFlags");
|
ps->faceSeamFlags = MEM_callocN(sizeof(ushort) * ps->totlooptri_eval, "paint-faceSeamFlags");
|
||||||
ps->faceWindingFlags = MEM_callocN(sizeof(char) * ps->totlooptri_eval, "paint-faceWindindFlags");
|
ps->faceWindingFlags = MEM_callocN(sizeof(char) * ps->totlooptri_eval, "paint-faceWindindFlags");
|
||||||
ps->loopSeamUVs = MEM_mallocN(sizeof(float[2][2]) * ps->totloop_eval, "paint-loopSeamUVs");
|
ps->loopSeamData = MEM_mallocN(sizeof(LoopSeamData) * ps->totloop_eval, "paint-loopSeamUVs");
|
||||||
ps->vertSeams = MEM_callocN(sizeof(ListBase) * ps->totvert_eval, "paint-vertSeams");
|
ps->vertSeams = MEM_callocN(sizeof(ListBase) * ps->totvert_eval, "paint-vertSeams");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4303,7 +4340,7 @@ static void project_paint_end(ProjPaintState *ps)
|
|||||||
MEM_freeN(ps->vertFaces);
|
MEM_freeN(ps->vertFaces);
|
||||||
MEM_freeN(ps->faceSeamFlags);
|
MEM_freeN(ps->faceSeamFlags);
|
||||||
MEM_freeN(ps->faceWindingFlags);
|
MEM_freeN(ps->faceWindingFlags);
|
||||||
MEM_freeN(ps->loopSeamUVs);
|
MEM_freeN(ps->loopSeamData);
|
||||||
MEM_freeN(ps->vertSeams);
|
MEM_freeN(ps->vertSeams);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -5468,6 +5505,7 @@ static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps, int
|
|||||||
#ifndef PROJ_DEBUG_NOSEAMBLEED
|
#ifndef PROJ_DEBUG_NOSEAMBLEED
|
||||||
/* pixel num to bleed */
|
/* pixel num to bleed */
|
||||||
ps->seam_bleed_px = settings->imapaint.seam_bleed;
|
ps->seam_bleed_px = settings->imapaint.seam_bleed;
|
||||||
|
ps->seam_bleed_px_sq = SQUARE(settings->imapaint.seam_bleed);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (ps->do_mask_normal) {
|
if (ps->do_mask_normal) {
|
||||||
|
Reference in New Issue
Block a user