Fix T99684: Upgrade Averages Island Scale with options Scale UV and Shear

Differential Revision: https://developer.blender.org/D15421
This commit is contained in:
Chris Blackbourn 2022-07-14 14:40:07 +12:00
parent 09a74ff8b6
commit 931779197a
5 changed files with 123 additions and 16 deletions

View File

@ -238,6 +238,7 @@ bool invert_m3_ex(float m[3][3], float epsilon);
bool invert_m3_m3_ex(float m1[3][3], const float m2[3][3], float epsilon);
bool invert_m3(float R[3][3]);
bool invert_m2_m2(float R[2][2], const float A[2][2]);
bool invert_m3_m3(float R[3][3], const float A[3][3]);
bool invert_m4(float R[4][4]);
bool invert_m4_m4(float R[4][4], const float A[4][4]);

View File

@ -1116,6 +1116,22 @@ double determinant_m3_array_db(const double m[3][3])
m[2][0] * (m[0][1] * m[1][2] - m[0][2] * m[1][1]));
}
bool invert_m2_m2(float m1[2][2], const float m2[2][2])
{
adjoint_m2_m2(m1, m2);
float det = determinant_m2(m2[0][0], m2[1][0], m2[0][1], m2[1][1]);
bool success = (det != 0.0f);
if (success) {
m1[0][0] /= det;
m1[1][0] /= det;
m1[0][1] /= det;
m1[1][1] /= det;
}
return success;
}
bool invert_m3_ex(float m[3][3], const float epsilon)
{
float tmp[3][3];

View File

@ -1191,7 +1191,7 @@ void UV_OT_pack_islands(wmOperatorType *ot)
/** \name Average UV Islands Scale Operator
* \{ */
static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op))
static int average_islands_scale_exec(bContext *C, wmOperator *op)
{
const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
@ -1215,8 +1215,12 @@ static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_CANCELLED;
}
/* RNA props */
const bool scale_uv = RNA_boolean_get(op->ptr, "scale_uv");
const bool shear = RNA_boolean_get(op->ptr, "shear");
ParamHandle *handle = construct_param_handle_multi(scene, objects, objects_len, &options);
GEO_uv_parametrizer_average(handle, false);
GEO_uv_parametrizer_average(handle, false, scale_uv, shear);
GEO_uv_parametrizer_flush(handle);
GEO_uv_parametrizer_delete(handle);
@ -1247,6 +1251,10 @@ void UV_OT_average_islands_scale(wmOperatorType *ot)
/* api callbacks */
ot->exec = average_islands_scale_exec;
ot->poll = ED_operator_uvedit;
/* properties */
RNA_def_boolean(ot->srna, "scale_uv", false, "Non-Uniform", "Scale U and V independently");
RNA_def_boolean(ot->srna, "shear", false, "Shear", "Reduce shear within islands");
}
/** \} */
@ -1845,7 +1853,7 @@ static void uvedit_unwrap(const Scene *scene,
result_info ? &result_info->count_failed : NULL);
GEO_uv_parametrizer_lscm_end(handle);
GEO_uv_parametrizer_average(handle, true);
GEO_uv_parametrizer_average(handle, true, false, false);
GEO_uv_parametrizer_flush(handle);

View File

@ -103,7 +103,10 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle,
/** \name Average area for all charts
* \{ */
void GEO_uv_parametrizer_average(ParamHandle *handle, bool ignore_pinned);
void GEO_uv_parametrizer_average(ParamHandle *handle,
bool ignore_pinned,
bool scale_uv,
bool shear);
/** \} */

View File

@ -148,7 +148,8 @@ typedef struct PChart {
} lscm;
struct PChartPack {
float rescale, area;
float size[2] /* , trans[2] */;
float size[2];
float origin[2];
} pack;
} u;
@ -4243,7 +4244,10 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle,
}
}
void GEO_uv_parametrizer_average(ParamHandle *phandle, bool ignore_pinned)
void GEO_uv_parametrizer_average(ParamHandle *phandle,
bool ignore_pinned,
bool scale_uv,
bool shear)
{
PChart *chart;
int i;
@ -4263,6 +4267,83 @@ void GEO_uv_parametrizer_average(ParamHandle *phandle, bool ignore_pinned)
continue;
}
p_chart_uv_bbox(chart, minv, maxv);
mid_v2_v2v2(chart->u.pack.origin, minv, maxv);
if (scale_uv || shear) {
/* It's possible that for some "bad" inputs, the following iteration will converge slowly or
* perhaps even diverge. Rather than infinite loop, we only iterate a maximum of `max_iter`
* times. (Also useful when making changes to the calculation.) */
int max_iter = 10;
for (int j = 0; j < max_iter; j++) {
/* An island could contain millions of polygons. When summing many small values, we need to
* use double precision in the accumulator to maintain accuracy. Note that the individual
* calculations only need to be at single precision.*/
double scale_cou = 0;
double scale_cov = 0;
double scale_cross = 0;
double weight_sum = 0;
for (PFace *f = chart->faces; f; f = f->nextlink) {
float m[2][2], s[2][2];
PVert *va = f->edge->vert;
PVert *vb = f->edge->next->vert;
PVert *vc = f->edge->next->next->vert;
s[0][0] = va->uv[0] - vc->uv[0];
s[0][1] = va->uv[1] - vc->uv[1];
s[1][0] = vb->uv[0] - vc->uv[0];
s[1][1] = vb->uv[1] - vc->uv[1];
/* Find the "U" axis and "V" axis in triangle co-ordinates. Normally this would require
* SVD, but in 2D we can use a cheaper matrix inversion instead.*/
if (!invert_m2_m2(m, s)) {
continue;
}
float cou[3], cov[3]; /* i.e. Texture "U" and texture "V" in 3D co-ordinates.*/
for (int i = 0; i < 3; i++) {
cou[i] = m[0][0] * (va->co[i] - vc->co[i]) + m[0][1] * (vb->co[i] - vc->co[i]);
cov[i] = m[1][0] * (va->co[i] - vc->co[i]) + m[1][1] * (vb->co[i] - vc->co[i]);
}
const float weight = p_face_area(f);
scale_cou += len_v3(cou) * weight;
scale_cov += len_v3(cov) * weight;
if (shear) {
normalize_v3(cov);
normalize_v3(cou);
/* Why is scale_cross called `cross` when we call `dot`? The next line calculates:
* `scale_cross += length(cross(cross(cou, face_normal), cov))`
* By construction, both `cou` and `cov` are orthogonal to the face normal.
* By definition, the normal vector has unit length. */
scale_cross += dot_v3v3(cou, cov) * weight;
}
weight_sum += weight;
}
if (scale_cou * scale_cov < 1e-10f) {
break;
}
const float scale_factor_u = scale_uv ? sqrtf(scale_cou / scale_cov) : 1.0f;
/* Compute correction transform. */
float t[2][2];
t[0][0] = scale_factor_u;
t[1][0] = clamp_f((float)(scale_cross / weight_sum), -0.5f, 0.5f);
t[0][1] = 0;
t[1][1] = 1.0f / scale_factor_u;
/* Apply the correction. */
p_chart_uv_transform(chart, t);
/* How far from the identity transform are we? [[1,0],[0,1]] */
const float err = fabsf(t[0][0] - 1.0f) + fabsf(t[1][0]) + fabsf(t[0][1]) +
fabsf(t[1][1] - 1.0f);
const float tolerance = 1e-6f; /* Trade accuracy for performance. */
if (err < tolerance) {
/* Too slow? Use Richardson Extrapolation to accelerate the convergence.*/
break;
}
}
}
chart->u.pack.area = 0.0f; /* 3d area */
chart->u.pack.rescale = 0.0f; /* UV area, abusing rescale for tmp storage, oh well :/ */
@ -4292,18 +4373,16 @@ void GEO_uv_parametrizer_average(ParamHandle *phandle, bool ignore_pinned)
if (chart->u.pack.area != 0.0f && chart->u.pack.rescale != 0.0f) {
fac = chart->u.pack.area / chart->u.pack.rescale;
/* Get the island center */
p_chart_uv_bbox(chart, minv, maxv);
trans[0] = (minv[0] + maxv[0]) / -2.0f;
trans[1] = (minv[1] + maxv[1]) / -2.0f;
/* Move center to 0,0 */
p_chart_uv_translate(chart, trans);
/* Average scale. */
p_chart_uv_scale(chart, sqrtf(fac / tot_fac));
/* Move to original center */
trans[0] = -trans[0];
trans[1] = -trans[1];
/* Get the current island center. */
p_chart_uv_bbox(chart, minv, maxv);
/* Move to original center. */
mid_v2_v2v2(trans, minv, maxv);
negate_v2(trans);
add_v2_v2(trans, chart->u.pack.origin);
p_chart_uv_translate(chart, trans);
}
}