diff options
Diffstat (limited to 'source/blender/editors/uvedit/uvedit_unwrap_ops.c')
-rw-r--r-- | source/blender/editors/uvedit/uvedit_unwrap_ops.c | 303 |
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, |