Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/uvedit/uvedit_unwrap_ops.c')
-rw-r--r--source/blender/editors/uvedit/uvedit_unwrap_ops.c303
1 files changed, 186 insertions, 117 deletions
diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
index 34fae2ffb2a..3618286ec01 100644
--- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c
+++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
@@ -103,7 +103,7 @@ static bool ED_uvedit_ensure_uvs(Object *obedit)
int cd_loop_uv_offset;
if (em && em->bm->totface && !CustomData_has_layer(&em->bm->ldata, CD_MLOOPUV)) {
- ED_mesh_uv_texture_add(obedit->data, NULL, true, true, NULL);
+ ED_mesh_uv_add(obedit->data, NULL, true, true, NULL);
}
/* Happens when there are no faces. */
@@ -267,60 +267,104 @@ static bool uvedit_have_selection_multi(const Scene *scene,
return have_select;
}
+void ED_uvedit_get_aspect_from_material(Object *ob,
+ const int material_index,
+ float *r_aspx,
+ float *r_aspy)
+{
+ if (UNLIKELY(material_index < 0 || material_index >= ob->totcol)) {
+ *r_aspx = 1.0f;
+ *r_aspy = 1.0f;
+ return;
+ }
+ Image *ima;
+ ED_object_get_active_image(ob, material_index + 1, &ima, NULL, NULL, NULL);
+ ED_image_get_uv_aspect(ima, NULL, r_aspx, r_aspy);
+}
+
void ED_uvedit_get_aspect(Object *ob, float *r_aspx, float *r_aspy)
{
BMEditMesh *em = BKE_editmesh_from_object(ob);
BLI_assert(em != NULL);
bool sloppy = true;
bool selected = false;
- BMFace *efa;
- Image *ima;
+ BMFace *efa = BM_mesh_active_face_get(em->bm, sloppy, selected);
+ if (!efa) {
+ *r_aspx = 1.0f;
+ *r_aspy = 1.0f;
+ return;
+ }
- efa = BM_mesh_active_face_get(em->bm, sloppy, selected);
+ ED_uvedit_get_aspect_from_material(ob, efa->mat_nr, r_aspx, r_aspy);
+}
- if (efa) {
- ED_object_get_active_image(ob, efa->mat_nr + 1, &ima, NULL, NULL, NULL);
+static bool uvedit_is_face_affected(const Scene *scene,
+ BMFace *efa,
+ const UnwrapOptions *options,
+ const int cd_loop_uv_offset)
+{
+ if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
+ return false;
+ }
- ED_image_get_uv_aspect(ima, NULL, r_aspx, r_aspy);
+ if (options->only_selected_faces && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+ return false;
}
- else {
- *r_aspx = 1.0f;
- *r_aspy = 1.0f;
+
+ if (options->topology_from_uvs && options->only_selected_uvs &&
+ !uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) {
+ return false;
+ }
+
+ return true;
+}
+
+/* Prepare unique indices for each unique pinned UV, even if it shares a BMVert.
+ */
+static void uvedit_prepare_pinned_indices(ParamHandle *handle,
+ BMFace *efa,
+ const int cd_loop_uv_offset)
+{
+ BMIter liter;
+ BMLoop *l;
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (luv->flag & MLOOPUV_PINNED) {
+ int bmvertindex = BM_elem_index_get(l->v);
+ GEO_uv_prepare_pin_index(handle, bmvertindex, luv->uv);
+ }
}
}
static void construct_param_handle_face_add(ParamHandle *handle,
const Scene *scene,
BMFace *efa,
- int face_index,
+ ParamKey face_index,
const int cd_loop_uv_offset)
{
- ParamKey key;
ParamKey *vkeys = BLI_array_alloca(vkeys, efa->len);
- ParamBool *pin = BLI_array_alloca(pin, efa->len);
- ParamBool *select = BLI_array_alloca(select, efa->len);
- float **co = BLI_array_alloca(co, efa->len);
+ bool *pin = BLI_array_alloca(pin, efa->len);
+ bool *select = BLI_array_alloca(select, efa->len);
+ const float **co = BLI_array_alloca(co, efa->len);
float **uv = BLI_array_alloca(uv, efa->len);
int i;
BMIter liter;
BMLoop *l;
- key = (ParamKey)face_index;
-
/* let parametrizer split the ngon, it can make better decisions
* about which split is best for unwrapping than poly-fill. */
BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- vkeys[i] = (ParamKey)BM_elem_index_get(l->v);
+ vkeys[i] = GEO_uv_find_pin_index(handle, BM_elem_index_get(l->v), luv->uv);
co[i] = l->v->co;
uv[i] = luv->uv;
pin[i] = (luv->flag & MLOOPUV_PINNED) != 0;
select[i] = uvedit_uv_select_test(scene, l, cd_loop_uv_offset);
}
- GEO_uv_parametrizer_face_add(handle, key, i, vkeys, co, uv, pin, select);
+ GEO_uv_parametrizer_face_add(handle, face_index, i, vkeys, co, uv, pin, select);
}
/* See: construct_param_handle_multi to handle multiple objects at once. */
@@ -330,16 +374,12 @@ static ParamHandle *construct_param_handle(const Scene *scene,
const UnwrapOptions *options,
UnwrapResultInfo *result_info)
{
- ParamHandle *handle;
BMFace *efa;
- BMLoop *l;
BMEdge *eed;
- BMIter iter, liter;
+ BMIter iter;
int i;
- const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
-
- handle = GEO_uv_parametrizer_construct_begin();
+ ParamHandle *handle = GEO_uv_parametrizer_construct_begin();
if (options->correct_aspect) {
float aspx, aspy;
@@ -354,30 +394,17 @@ static ParamHandle *construct_param_handle(const Scene *scene,
/* we need the vert indices */
BM_mesh_elem_index_ensure(bm, BM_VERT);
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) {
-
- if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ||
- (options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) {
- continue;
+ if (uvedit_is_face_affected(scene, efa, options, cd_loop_uv_offset)) {
+ uvedit_prepare_pinned_indices(handle, efa, cd_loop_uv_offset);
}
+ }
- if (options->topology_from_uvs) {
- bool is_loopsel = false;
-
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- if (options->only_selected_uvs &&
- (uvedit_uv_select_test(scene, l, cd_loop_uv_offset) == false)) {
- continue;
- }
- is_loopsel = true;
- break;
- }
- if (is_loopsel == false) {
- continue;
- }
+ BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) {
+ if (uvedit_is_face_affected(scene, efa, options, cd_loop_uv_offset)) {
+ construct_param_handle_face_add(handle, scene, efa, i, cd_loop_uv_offset);
}
-
- construct_param_handle_face_add(handle, scene, efa, i, cd_loop_uv_offset);
}
if (!options->topology_from_uvs || options->topology_from_uvs_use_seams) {
@@ -408,14 +435,12 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene,
const UnwrapOptions *options,
int *count_fail)
{
- ParamHandle *handle;
BMFace *efa;
- BMLoop *l;
BMEdge *eed;
- BMIter iter, liter;
+ BMIter iter;
int i;
- handle = GEO_uv_parametrizer_construct_begin();
+ ParamHandle *handle = GEO_uv_parametrizer_construct_begin();
if (options->correct_aspect) {
Object *ob = objects[0];
@@ -444,29 +469,15 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene,
}
BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) {
-
- if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) ||
- (options->only_selected_faces && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) {
- continue;
+ if (uvedit_is_face_affected(scene, efa, options, cd_loop_uv_offset)) {
+ uvedit_prepare_pinned_indices(handle, efa, cd_loop_uv_offset);
}
+ }
- if (options->topology_from_uvs) {
- bool is_loopsel = false;
-
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- if (options->only_selected_uvs &&
- (uvedit_uv_select_test(scene, l, cd_loop_uv_offset) == false)) {
- continue;
- }
- is_loopsel = true;
- break;
- }
- if (is_loopsel == false) {
- continue;
- }
+ BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) {
+ if (uvedit_is_face_affected(scene, efa, options, cd_loop_uv_offset)) {
+ construct_param_handle_face_add(handle, scene, efa, i + offset, cd_loop_uv_offset);
}
-
- construct_param_handle_face_add(handle, scene, efa, i + offset, cd_loop_uv_offset);
}
if (!options->topology_from_uvs || options->topology_from_uvs_use_seams) {
@@ -493,8 +504,8 @@ static void texface_from_original_index(const Scene *scene,
BMFace *efa,
int index,
float **r_uv,
- ParamBool *r_pin,
- ParamBool *r_select)
+ bool *r_pin,
+ bool *r_select)
{
BMLoop *l;
BMIter liter;
@@ -530,7 +541,6 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
const UnwrapOptions *options,
UnwrapResultInfo *result_info)
{
- ParamHandle *handle;
/* index pointers */
MPoly *mpoly;
MLoop *mloop;
@@ -562,7 +572,7 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
- handle = GEO_uv_parametrizer_construct_begin();
+ ParamHandle *handle = GEO_uv_parametrizer_construct_begin();
if (options->correct_aspect) {
float aspx, aspy;
@@ -627,8 +637,8 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
/* Prepare and feed faces to the solver */
for (i = 0, mpoly = subsurfedPolys; i < numOfFaces; i++, mpoly++) {
ParamKey key, vkeys[4];
- ParamBool pin[4], select[4];
- float *co[4];
+ bool pin[4], select[4];
+ const float *co[4];
float *uv[4];
BMFace *origFace = faceMap[i];
@@ -646,7 +656,7 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
mloop = &subsurfedLoops[mpoly->loopstart];
- /* We will not check for v4 here. Subsurfed mfaces always have 4 vertices. */
+ /* We will not check for v4 here. Sub-surface faces always have 4 vertices. */
BLI_assert(mpoly->totloop == 4);
key = (ParamKey)i;
vkeys[0] = (ParamKey)mloop[0].v;
@@ -1014,8 +1024,7 @@ static void uvedit_pack_islands(const Scene *scene, Object *ob, BMesh *bm)
bool rotate = true;
bool ignore_pinned = false;
- ParamHandle *handle;
- handle = construct_param_handle(scene, ob, bm, &options, NULL);
+ ParamHandle *handle = construct_param_handle(scene, ob, bm, &options, NULL);
GEO_uv_parametrizer_pack(handle, scene->toolsettings->uvcalc_margin, rotate, ignore_pinned);
GEO_uv_parametrizer_flush(handle);
GEO_uv_parametrizer_delete(handle);
@@ -1034,8 +1043,7 @@ static void uvedit_pack_islands_multi(const Scene *scene,
bool rotate,
bool ignore_pinned)
{
- ParamHandle *handle;
- handle = construct_param_handle_multi(scene, objects, objects_len, options, NULL);
+ ParamHandle *handle = construct_param_handle_multi(scene, objects, objects_len, options, NULL);
GEO_uv_parametrizer_pack(handle, scene->toolsettings->uvcalc_margin, rotate, ignore_pinned);
GEO_uv_parametrizer_flush(handle);
GEO_uv_parametrizer_delete(handle);
@@ -1241,7 +1249,7 @@ void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit)
handle = construct_param_handle(scene, obedit, em->bm, &options, NULL);
}
- GEO_uv_parametrizer_lscm_begin(handle, PARAM_TRUE, abf);
+ GEO_uv_parametrizer_lscm_begin(handle, true, abf);
/* Create or increase size of g_live_unwrap.handles array */
if (g_live_unwrap.handles == NULL) {
@@ -1527,49 +1535,88 @@ static void uv_transform_properties(wmOperatorType *ot, int radius)
}
}
-static void correct_uv_aspect(Object *ob, BMEditMesh *em)
+static void shrink_loop_uv_by_aspect_ratio(BMFace *efa,
+ const int cd_loop_uv_offset,
+ const float aspect_y)
{
+ BLI_assert(aspect_y != 1.0f); /* Nothing to do, should be handled by caller. */
+ BLI_assert(aspect_y > 0.0f); /* Negative aspect ratios are not supported. */
+
BMLoop *l;
- BMIter iter, liter;
- MLoopUV *luv;
- BMFace *efa;
- float scale, aspx, aspy;
+ BMIter iter;
+ BM_ITER_ELEM (l, &iter, efa, BM_LOOPS_OF_FACE) {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (aspect_y > 1.0f) {
+ /* Reduce round-off error, i.e. `u = (u - 0.5) / aspect_y + 0.5`. */
+ luv->uv[0] = luv->uv[0] / aspect_y + (0.5f - 0.5f / aspect_y);
+ }
+ else {
+ /* Reduce round-off error, i.e. `v = (v - 0.5) * aspect_y + 0.5`. */
+ luv->uv[1] = luv->uv[1] * aspect_y + (0.5f - 0.5f * aspect_y);
+ }
+ }
+}
+static void correct_uv_aspect(Object *ob, BMEditMesh *em)
+{
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
-
+ float aspx, aspy;
ED_uvedit_get_aspect(ob, &aspx, &aspy);
+ const float aspect_y = aspx / aspy;
+ if (aspect_y == 1.0f) {
+ /* Scaling by 1.0 has no effect. */
+ return;
+ }
+ BMFace *efa;
+ BMIter iter;
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+ shrink_loop_uv_by_aspect_ratio(efa, cd_loop_uv_offset, aspect_y);
+ }
+ }
+}
- if (aspx == aspy) {
+static void correct_uv_aspect_per_face(Object *ob, BMEditMesh *em)
+{
+ const int materials_num = ob->totcol;
+ if (materials_num == 0) {
+ /* Without any materials, there is no aspect_y information and nothing to do. */
return;
}
- if (aspx > aspy) {
- scale = aspy / aspx;
+ float *material_aspect_y = BLI_array_alloca(material_aspect_y, materials_num);
+ /* Lazily initialize aspect ratio for materials. */
+ copy_vn_fl(material_aspect_y, materials_num, -1.0f);
- BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
- if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
- continue;
- }
+ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- luv->uv[0] = ((luv->uv[0] - 0.5f) * scale) + 0.5f;
- }
+ BMFace *efa;
+ BMIter iter;
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+ continue;
}
- }
- else {
- scale = aspx / aspy;
- BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
- if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
- continue;
- }
+ const int material_index = efa->mat_nr;
+ if (UNLIKELY(material_index < 0 || material_index >= materials_num)) {
+ /* The index might be for a material slot which is not currently setup. */
+ continue;
+ }
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- luv->uv[1] = ((luv->uv[1] - 0.5f) * scale) + 0.5f;
- }
+ float aspect_y = material_aspect_y[material_index];
+ if (aspect_y == -1.0f) {
+ /* Lazily initialize aspect ratio for materials. */
+ float aspx, aspy;
+ ED_uvedit_get_aspect_from_material(ob, material_index, &aspx, &aspy);
+ aspect_y = aspx / aspy;
+ material_aspect_y[material_index] = aspect_y;
+ }
+
+ if (aspect_y == 1.0f) {
+ /* Scaling by 1.0 has no effect. */
+ continue;
}
+ shrink_loop_uv_by_aspect_ratio(efa, cd_loop_uv_offset, aspect_y);
}
}
@@ -1613,7 +1660,17 @@ static void uv_map_clip_correct_properties(wmOperatorType *ot)
uv_map_clip_correct_properties_ex(ot, true);
}
-static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOperator *op)
+/**
+ * \param per_face_aspect: Calculate the aspect ratio per-face,
+ * otherwise use a single aspect for all UV's based on the material of the active face.
+ * TODO: using per-face aspect may split UV islands so more advanced UV projection methods
+ * such as "Unwrap" & "Smart UV Projections" will need to handle aspect correction themselves.
+ * For now keep using a single aspect for all faces in this case.
+ */
+static void uv_map_clip_correct_multi(Object **objects,
+ uint objects_len,
+ wmOperator *op,
+ bool per_face_aspect)
{
BMFace *efa;
BMLoop *l;
@@ -1633,9 +1690,14 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper
BMEditMesh *em = BKE_editmesh_from_object(ob);
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
- /* correct for image aspect ratio */
+ /* Correct for image aspect ratio. */
if (correct_aspect) {
- correct_uv_aspect(ob, em);
+ if (per_face_aspect) {
+ correct_uv_aspect_per_face(ob, em);
+ }
+ else {
+ correct_uv_aspect(ob, em);
+ }
}
if (scale_to_bounds) {
@@ -1678,6 +1740,11 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper
dy = 1.0f / dy;
}
+ if (dx == 1.0f && dy == 1.0f) {
+ /* Scaling by 1.0 has no effect. */
+ return;
+ }
+
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
@@ -1702,7 +1769,7 @@ static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOper
static void uv_map_clip_correct(Object *ob, wmOperator *op)
{
- uv_map_clip_correct_multi(&ob, 1, op);
+ uv_map_clip_correct_multi(&ob, 1, op, true);
}
/** \} */
@@ -1733,7 +1800,7 @@ static void uvedit_unwrap(const Scene *scene,
handle = construct_param_handle(scene, obedit, em->bm, options, result_info);
}
- GEO_uv_parametrizer_lscm_begin(handle, PARAM_FALSE, scene->toolsettings->unwrapper == 0);
+ GEO_uv_parametrizer_lscm_begin(handle, false, scene->toolsettings->unwrapper == 0);
GEO_uv_parametrizer_lscm_solve(handle,
result_info ? &result_info->count_changed : NULL,
result_info ? &result_info->count_failed : NULL);
@@ -2283,7 +2350,9 @@ static int smart_project_exec(bContext *C, wmOperator *op)
.use_seams = true,
});
- uv_map_clip_correct_multi(objects_changed, object_changed_len, op);
+ /* #ED_uvedit_pack_islands_multi only supports `per_face_aspect = false`. */
+ const bool per_face_aspect = false;
+ uv_map_clip_correct_multi(objects_changed, object_changed_len, op, per_face_aspect);
}
MEM_freeN(objects_changed);
@@ -2485,7 +2554,7 @@ static int uv_from_view_exec(bContext *C, wmOperator *op)
}
if (changed_multi) {
- uv_map_clip_correct_multi(objects, objects_len, op);
+ uv_map_clip_correct_multi(objects, objects_len, op, true);
}
MEM_freeN(objects);
@@ -2968,7 +3037,7 @@ void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob)
* since we are not in edit mode we need to ensure only the uv flags are tested */
scene->toolsettings->uv_flag &= ~UV_SYNC_SELECTION;
- ED_mesh_uv_texture_ensure(me, NULL);
+ ED_mesh_uv_ensure(me, NULL);
BM_mesh_bm_from_me(bm,
me,