Fix UV unwrap not working with only one pinned vertex

Now the one vertex defines the position of the UV chart, while rotation and
shape is still determined automatically.

Initial patch by Willis (wlssirius).

Differential Revision: https://developer.blender.org/D8484
This commit is contained in:
Brecht Van Lommel 2020-08-10 18:32:07 +02:00
parent cbb1bb90fd
commit da95fa8344
Notes: blender-bot 2023-02-13 21:33:59 +01:00
Referenced by issue #79590, UV Island Won't Unwrap If Only One Vertex Is Pinned
1 changed files with 76 additions and 20 deletions

View File

@ -190,6 +190,9 @@ typedef struct PChart {
LinearSolver *context;
float *abf_alpha;
PVert *pin1, *pin2;
PVert *single_pin;
float single_pin_area;
float single_pin_uv[2];
} lscm;
struct PChartPack {
float rescale, area;
@ -467,6 +470,17 @@ static void p_chart_uv_bbox(PChart *chart, float minv[2], float maxv[2])
}
}
static float p_chart_uv_area(PChart *chart)
{
float area = 0.0f;
for (PFace *f = chart->faces; f; f = f->nextlink) {
area += fabsf(p_face_uv_area_signed(f));
}
return area;
}
static void p_chart_uv_scale(PChart *chart, float scale)
{
PVert *v;
@ -3176,7 +3190,7 @@ static void p_chart_lscm_begin(PChart *chart, PBool live, PBool abf)
}
}
if ((live && (!select || !deselect)) || (npins == 1)) {
if ((live && (!select || !deselect))) {
chart->u.lscm.context = NULL;
}
else {
@ -3185,6 +3199,16 @@ static void p_chart_lscm_begin(PChart *chart, PBool live, PBool abf)
p_chart_topological_sanity_check(chart);
#endif
if (npins == 1) {
chart->u.lscm.single_pin_area = p_chart_uv_area(chart);
for (v = chart->verts; v; v = v->nextlink) {
if (v->flag & PVERT_PIN) {
chart->u.lscm.single_pin = v;
break;
}
}
}
if (abf) {
if (!p_chart_abf_solve(chart)) {
param_warning("ABF solving failed: falling back to LSCM.\n");
@ -3192,12 +3216,12 @@ static void p_chart_lscm_begin(PChart *chart, PBool live, PBool abf)
}
if (npins <= 1) {
/* not enough pins, lets find some ourself */
/* No pins, let's find some ourself. */
PEdge *outer;
p_chart_boundaries(chart, NULL, &outer);
/* outer can be NULL with non-finite coords. */
/* Outer can be NULL with non-finite coords. */
if (!(outer && p_chart_symmetry_pins(chart, outer, &pin1, &pin2))) {
p_chart_extrema_verts(chart, &pin1, &pin2);
}
@ -3235,6 +3259,11 @@ static PBool p_chart_lscm_solve(PHandle *handle, PChart *chart)
}
}
if (chart->u.lscm.single_pin) {
/* If only one pin, save area and pin for transform later. */
copy_v2_v2(chart->u.lscm.single_pin_uv, chart->u.lscm.single_pin->uv);
}
if (chart->u.lscm.pin1) {
EIG_linear_solver_variable_lock(context, 2 * pin1->u.id);
EIG_linear_solver_variable_lock(context, 2 * pin1->u.id + 1);
@ -3358,6 +3387,25 @@ static PBool p_chart_lscm_solve(PHandle *handle, PChart *chart)
return P_FALSE;
}
static void p_chart_lscm_transform_single_pin(PChart *chart)
{
PVert *pin = chart->u.lscm.single_pin;
/* If only one pin, keep UV area the same. */
const float new_area = p_chart_uv_area(chart);
if (new_area > 0.0f) {
const float scale = chart->u.lscm.single_pin_area / new_area;
if (scale > 0.0f) {
p_chart_uv_scale(chart, sqrtf(scale));
}
}
/* Translate to keep the pinned vertex in place. */
float offset[2];
sub_v2_v2v2(offset, chart->u.lscm.single_pin_uv, pin->uv);
p_chart_uv_translate(chart, offset);
}
static void p_chart_lscm_end(PChart *chart)
{
if (chart->u.lscm.context) {
@ -3372,6 +3420,8 @@ static void p_chart_lscm_end(PChart *chart)
chart->u.lscm.context = NULL;
chart->u.lscm.pin1 = NULL;
chart->u.lscm.pin2 = NULL;
chart->u.lscm.single_pin = NULL;
chart->u.lscm.single_pin_area = 0.0f;
}
/* Stretch */
@ -3781,6 +3831,23 @@ static void p_chart_rotate_minimum_area(PChart *chart)
}
}
static void p_chart_rotate_fit_aabb(PChart *chart)
{
float(*points)[2] = MEM_mallocN(sizeof(*points) * chart->nverts, __func__);
p_chart_uv_to_array(chart, points);
float angle = BLI_convexhull_aabb_fit_points_2d(points, chart->nverts);
MEM_freeN(points);
if (angle != 0.0f) {
float mat[2][2];
angle_to_mat2(mat, angle);
p_chart_uv_transform(chart, mat);
}
}
/* Area Smoothing */
/* 2d bsp tree for inverse mapping - that's a bit silly */
@ -4576,8 +4643,12 @@ void param_lscm_solve(ParamHandle *handle)
if (result && !(chart->flag & PCHART_HAS_PINS)) {
p_chart_rotate_minimum_area(chart);
}
else if (result && chart->u.lscm.single_pin) {
p_chart_rotate_fit_aabb(chart);
p_chart_lscm_transform_single_pin(chart);
}
if (!result || (chart->u.lscm.pin1)) {
if (!result || !(chart->flag & PCHART_HAS_PINS)) {
p_chart_lscm_end(chart);
}
}
@ -4692,28 +4763,13 @@ static void param_pack_rotate(ParamHandle *handle, bool ignore_pinned)
PHandle *phandle = (PHandle *)handle;
for (i = 0; i < phandle->ncharts; i++) {
float(*points)[2];
float angle;
chart = phandle->charts[i];
if (ignore_pinned && (chart->flag & PCHART_HAS_PINS)) {
continue;
}
points = MEM_mallocN(sizeof(*points) * chart->nverts, __func__);
p_chart_uv_to_array(chart, points);
angle = BLI_convexhull_aabb_fit_points_2d(points, chart->nverts);
MEM_freeN(points);
if (angle != 0.0f) {
float mat[2][2];
angle_to_mat2(mat, angle);
p_chart_uv_transform(chart, mat);
}
p_chart_rotate_fit_aabb(chart);
}
}