Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/uvedit/uvedit_islands.cc
| Show First 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | ) | ||||
| * In general, post-transforms are easier to work with when using homogenous co-ordinates. | * In general, post-transforms are easier to work with when using homogenous co-ordinates. | ||||
| * | * | ||||
| * When UV mapping into the unit square, post-transforms can lose precision on small islands. | * When UV mapping into the unit square, post-transforms can lose precision on small islands. | ||||
| * Instead we're using a pre-transform to maintain precision. | * Instead we're using a pre-transform to maintain precision. | ||||
| * | * | ||||
| * To convert post-transform to pre-transform, use `A * x + b == A * (x + c), c = A^-1 * b` | * To convert post-transform to pre-transform, use `A * x + b == A * (x + c), c = A^-1 * b` | ||||
| */ | */ | ||||
| const int cd_loop_uv_offset = island->cd_loop_uv_offset; | const int cd_loop_uv_offset = island->offsets.uv; | ||||
| const int faces_len = island->faces_len; | const int faces_len = island->faces_len; | ||||
| for (int i = 0; i < faces_len; i++) { | for (int i = 0; i < faces_len; i++) { | ||||
| BMFace *f = island->faces[i]; | BMFace *f = island->faces[i]; | ||||
| BMLoop *l; | BMLoop *l; | ||||
| BMIter iter; | BMIter iter; | ||||
| BM_ITER_ELEM (l, &iter, f, BM_LOOPS_OF_FACE) { | BM_ITER_ELEM (l, &iter, f, BM_LOOPS_OF_FACE) { | ||||
| MLoopUV *luv = (MLoopUV *)BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); | float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset); | ||||
| mul_v2_m2_add_v2v2(luv->uv, matrix, luv->uv, pre_translate); | mul_v2_m2_add_v2v2(luv, matrix, luv, pre_translate); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name UV Face Array Utilities | /** \name UV Face Array Utilities | ||||
| * \{ */ | * \{ */ | ||||
| ▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | for (int i = 0; i < faces_len; i++) { | ||||
| l_iter = l_first = BM_FACE_FIRST_LOOP(f); | l_iter = l_first = BM_FACE_FIRST_LOOP(f); | ||||
| do { | do { | ||||
| if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) { | if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) { | ||||
| /* Already walked over, continue. */ | /* Already walked over, continue. */ | ||||
| continue; | continue; | ||||
| } | } | ||||
| BM_elem_flag_disable(l_iter, BM_ELEM_TAG); | BM_elem_flag_disable(l_iter, BM_ELEM_TAG); | ||||
| const MLoopUV *luv = static_cast<const MLoopUV *>( | const float *luv = BM_ELEM_CD_GET_FLOAT_P(l_iter, cd_loop_uv_offset); | ||||
| BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset)); | copy_v2_v2(coords[coords_len++], luv); | ||||
| copy_v2_v2(coords[coords_len++], luv->uv); | |||||
| /* Un tag all connected so we don't add them twice. | /* Un tag all connected so we don't add them twice. | ||||
| * Note that we will tag other loops not part of `faces` but this is harmless, | * Note that we will tag other loops not part of `faces` but this is harmless, | ||||
| * since we're only turning off a tag. */ | * since we're only turning off a tag. */ | ||||
| BMVert *v_pivot = l_iter->v; | BMVert *v_pivot = l_iter->v; | ||||
| BMEdge *e_first = v_pivot->e; | BMEdge *e_first = v_pivot->e; | ||||
| const BMEdge *e = e_first; | const BMEdge *e = e_first; | ||||
| do { | do { | ||||
| if (e->l != nullptr) { | if (e->l != nullptr) { | ||||
| const BMLoop *l_radial = e->l; | const BMLoop *l_radial = e->l; | ||||
| do { | do { | ||||
| if (l_radial->v == l_iter->v) { | if (l_radial->v == l_iter->v) { | ||||
| if (BM_elem_flag_test(l_radial, BM_ELEM_TAG)) { | if (BM_elem_flag_test(l_radial, BM_ELEM_TAG)) { | ||||
| const MLoopUV *luv_radial = static_cast<const MLoopUV *>( | const float *luv_radial = BM_ELEM_CD_GET_FLOAT_P(l_radial, cd_loop_uv_offset); | ||||
| BM_ELEM_CD_GET_VOID_P(l_radial, cd_loop_uv_offset)); | if (equals_v2v2(luv, luv_radial)) { | ||||
| if (equals_v2v2(luv->uv, luv_radial->uv)) { | |||||
| /* Don't add this UV when met in another face in `faces`. */ | /* Don't add this UV when met in another face in `faces`. */ | ||||
| BM_elem_flag_disable(l_iter, BM_ELEM_TAG); | BM_elem_flag_disable(l_iter, BM_ELEM_TAG); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } while ((l_radial = l_radial->radial_next) != e->l); | } while ((l_radial = l_radial->radial_next) != e->l); | ||||
| } | } | ||||
| } while ((e = BM_DISK_EDGE_NEXT(e, v_pivot)) != e_first); | } while ((e = BM_DISK_EDGE_NEXT(e, v_pivot)) != e_first); | ||||
| } while ((l_iter = l_iter->next) != l_first); | } while ((l_iter = l_iter->next) != l_first); | ||||
| } | } | ||||
| *r_coords_len = coords_len; | *r_coords_len = coords_len; | ||||
| return coords; | return coords; | ||||
| } | } | ||||
| static void face_island_uv_rotate_fit_aabb(FaceIsland *island) | static void face_island_uv_rotate_fit_aabb(FaceIsland *island) | ||||
| { | { | ||||
| BMFace **faces = island->faces; | BMFace **faces = island->faces; | ||||
| const int faces_len = island->faces_len; | const int faces_len = island->faces_len; | ||||
| const float aspect_y = island->aspect_y; | const float aspect_y = island->aspect_y; | ||||
| const int cd_loop_uv_offset = island->cd_loop_uv_offset; | const int cd_loop_uv_offset = island->offsets.uv; | ||||
| /* Calculate unique coordinates since calculating a convex hull can be an expensive operation. */ | /* Calculate unique coordinates since calculating a convex hull can be an expensive operation. */ | ||||
| int coords_len; | int coords_len; | ||||
| float(*coords)[2] = bm_face_array_calc_unique_uv_coords( | float(*coords)[2] = bm_face_array_calc_unique_uv_coords( | ||||
| faces, faces_len, cd_loop_uv_offset, &coords_len); | faces, faces_len, cd_loop_uv_offset, &coords_len); | ||||
| /* Correct aspect ratio. */ | /* Correct aspect ratio. */ | ||||
| if (aspect_y != 1.0f) { | if (aspect_y != 1.0f) { | ||||
| ▲ Show 20 Lines • Show All 131 Lines • ▼ Show 20 Lines | |||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Calculate UV Islands | /** \name Calculate UV Islands | ||||
| * \{ */ | * \{ */ | ||||
| struct SharedUVLoopData { | struct SharedUVLoopData { | ||||
| int cd_loop_uv_offset; | BMUVOffsets offsets; | ||||
| bool use_seams; | bool use_seams; | ||||
| }; | }; | ||||
| static bool bm_loop_uv_shared_edge_check(const BMLoop *l_a, const BMLoop *l_b, void *user_data) | static bool bm_loop_uv_shared_edge_check(const BMLoop *l_a, const BMLoop *l_b, void *user_data) | ||||
| { | { | ||||
| const struct SharedUVLoopData *data = static_cast<const struct SharedUVLoopData *>(user_data); | const struct SharedUVLoopData *data = static_cast<const struct SharedUVLoopData *>(user_data); | ||||
| if (data->use_seams) { | if (data->use_seams) { | ||||
| if (BM_elem_flag_test(l_a->e, BM_ELEM_SEAM)) { | if (BM_elem_flag_test(l_a->e, BM_ELEM_SEAM)) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| } | } | ||||
| return BM_loop_uv_share_edge_check((BMLoop *)l_a, (BMLoop *)l_b, data->cd_loop_uv_offset); | return BM_loop_uv_share_edge_check((BMLoop *)l_a, (BMLoop *)l_b, data->offsets.uv); | ||||
| } | } | ||||
| /** | /** | ||||
| * Calculate islands and add them to \a island_list returning the number of items added. | * Calculate islands and add them to \a island_list returning the number of items added. | ||||
| */ | */ | ||||
| int bm_mesh_calc_uv_islands(const Scene *scene, | int bm_mesh_calc_uv_islands(const Scene *scene, | ||||
| BMesh *bm, | BMesh *bm, | ||||
| ListBase *island_list, | ListBase *island_list, | ||||
| const bool only_selected_faces, | const bool only_selected_faces, | ||||
| const bool only_selected_uvs, | const bool only_selected_uvs, | ||||
| const bool use_seams, | const bool use_seams, | ||||
| const float aspect_y, | const float aspect_y, | ||||
| const int cd_loop_uv_offset) | const BMUVOffsets uv_offsets) | ||||
| { | { | ||||
| BLI_assert(cd_loop_uv_offset >= 0); | BLI_assert(uv_offsets.uv >= 0); | ||||
| int island_added = 0; | int island_added = 0; | ||||
| BM_mesh_elem_table_ensure(bm, BM_FACE); | BM_mesh_elem_table_ensure(bm, BM_FACE); | ||||
| int *groups_array = static_cast<int *>( | int *groups_array = static_cast<int *>( | ||||
| MEM_mallocN(sizeof(*groups_array) * size_t(bm->totface), __func__)); | MEM_mallocN(sizeof(*groups_array) * size_t(bm->totface), __func__)); | ||||
| int(*group_index)[2]; | int(*group_index)[2]; | ||||
| /* Calculate the tag to use. */ | /* Calculate the tag to use. */ | ||||
| uchar hflag_face_test = 0; | uchar hflag_face_test = 0; | ||||
| if (only_selected_faces) { | if (only_selected_faces) { | ||||
| if (only_selected_uvs) { | if (only_selected_uvs) { | ||||
| BMFace *f; | BMFace *f; | ||||
| BMIter iter; | BMIter iter; | ||||
| BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { | BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { | ||||
| bool value = false; | bool value = false; | ||||
| if (BM_elem_flag_test(f, BM_ELEM_SELECT) && | if (BM_elem_flag_test(f, BM_ELEM_SELECT) && | ||||
| uvedit_face_select_test(scene, f, cd_loop_uv_offset)) { | uvedit_face_select_test(scene, f, uv_offsets)) { | ||||
| value = true; | value = true; | ||||
| } | } | ||||
| BM_elem_flag_set(f, BM_ELEM_TAG, value); | BM_elem_flag_set(f, BM_ELEM_TAG, value); | ||||
| } | } | ||||
| hflag_face_test = BM_ELEM_TAG; | hflag_face_test = BM_ELEM_TAG; | ||||
| } | } | ||||
| else { | else { | ||||
| hflag_face_test = BM_ELEM_SELECT; | hflag_face_test = BM_ELEM_SELECT; | ||||
| } | } | ||||
| } | } | ||||
| struct SharedUVLoopData user_data = {0}; | struct SharedUVLoopData user_data = {0}; | ||||
| user_data.cd_loop_uv_offset = cd_loop_uv_offset; | user_data.offsets = uv_offsets; | ||||
| user_data.use_seams = use_seams; | user_data.use_seams = use_seams; | ||||
| const int group_len = BM_mesh_calc_face_groups(bm, | const int group_len = BM_mesh_calc_face_groups(bm, | ||||
| groups_array, | groups_array, | ||||
| &group_index, | &group_index, | ||||
| nullptr, | nullptr, | ||||
| bm_loop_uv_shared_edge_check, | bm_loop_uv_shared_edge_check, | ||||
| &user_data, | &user_data, | ||||
| Show All 11 Lines | for (int i = 0; i < group_len; i++) { | ||||
| for (int j = 0; j < faces_len; j++) { | for (int j = 0; j < faces_len; j++) { | ||||
| faces[j] = BM_face_at_index(bm, groups_array[faces_start + j]); | faces[j] = BM_face_at_index(bm, groups_array[faces_start + j]); | ||||
| } | } | ||||
| struct FaceIsland *island = static_cast<struct FaceIsland *>( | struct FaceIsland *island = static_cast<struct FaceIsland *>( | ||||
| MEM_callocN(sizeof(*island), __func__)); | MEM_callocN(sizeof(*island), __func__)); | ||||
| island->faces = faces; | island->faces = faces; | ||||
| island->faces_len = faces_len; | island->faces_len = faces_len; | ||||
| island->cd_loop_uv_offset = cd_loop_uv_offset; | island->offsets = uv_offsets; | ||||
| island->aspect_y = aspect_y; | island->aspect_y = aspect_y; | ||||
| BLI_addtail(island_list, island); | BLI_addtail(island_list, island); | ||||
| island_added += 1; | island_added += 1; | ||||
| } | } | ||||
| MEM_freeN(groups_array); | MEM_freeN(groups_array); | ||||
| MEM_freeN(group_index); | MEM_freeN(group_index); | ||||
| return island_added; | return island_added; | ||||
| ▲ Show 20 Lines • Show All 201 Lines • ▼ Show 20 Lines | |||||
| static bool island_has_pins(const Scene *scene, | static bool island_has_pins(const Scene *scene, | ||||
| FaceIsland *island, | FaceIsland *island, | ||||
| const UVPackIsland_Params *params) | const UVPackIsland_Params *params) | ||||
| { | { | ||||
| const bool pin_unselected = params->pin_unselected; | const bool pin_unselected = params->pin_unselected; | ||||
| const bool only_selected_faces = params->only_selected_faces; | const bool only_selected_faces = params->only_selected_faces; | ||||
| BMLoop *l; | BMLoop *l; | ||||
| BMIter iter; | BMIter iter; | ||||
| const int cd_loop_uv_offset = island->cd_loop_uv_offset; | const int pin_offset = island->offsets.pin; | ||||
| for (int i = 0; i < island->faces_len; i++) { | for (int i = 0; i < island->faces_len; i++) { | ||||
| BMFace *efa = island->faces[i]; | BMFace *efa = island->faces[i]; | ||||
| if (pin_unselected && only_selected_faces && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) { | if (pin_unselected && only_selected_faces && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| BM_ITER_ELEM (l, &iter, efa, BM_LOOPS_OF_FACE) { | BM_ITER_ELEM (l, &iter, island->faces[i], BM_LOOPS_OF_FACE) { | ||||
| MLoopUV *luv = static_cast<MLoopUV *>(BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset)); | if (BM_ELEM_CD_GET_BOOL(l, pin_offset)) { | ||||
| if (luv->flag & MLOOPUV_PINNED) { | |||||
| return true; | return true; | ||||
| } | } | ||||
| if (pin_unselected && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { | if (pin_unselected && !uvedit_uv_select_test(scene, l, island->offsets)) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| Show All 18 Lines | if (bmesh_override) { | ||||
| /* Note: obedit is still required for aspect ratio and ID_RECALC_GEOMETRY. */ | /* Note: obedit is still required for aspect ratio and ID_RECALC_GEOMETRY. */ | ||||
| bm = bmesh_override[ob_index]; | bm = bmesh_override[ob_index]; | ||||
| } | } | ||||
| else { | else { | ||||
| BMEditMesh *em = BKE_editmesh_from_object(obedit); | BMEditMesh *em = BKE_editmesh_from_object(obedit); | ||||
| bm = em->bm; | bm = em->bm; | ||||
| } | } | ||||
| BLI_assert(bm); | BLI_assert(bm); | ||||
| const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); | |||||
| if (cd_loop_uv_offset == -1) { | const BMUVOffsets offsets = BM_uv_map_get_offsets(bm); | ||||
| if (offsets.uv == -1) { | |||||
| continue; | continue; | ||||
| } | } | ||||
| float aspect_y = 1.0f; | float aspect_y = 1.0f; | ||||
| if (params->correct_aspect) { | if (params->correct_aspect) { | ||||
| float aspx, aspy; | float aspx, aspy; | ||||
| ED_uvedit_get_aspect(obedit, &aspx, &aspy); | ED_uvedit_get_aspect(obedit, &aspx, &aspy); | ||||
| if (aspx != aspy) { | if (aspx != aspy) { | ||||
| Show All 10 Lines | for (uint ob_index = 0; ob_index < objects_len; ob_index++) { | ||||
| ListBase island_list = {nullptr}; | ListBase island_list = {nullptr}; | ||||
| bm_mesh_calc_uv_islands(scene, | bm_mesh_calc_uv_islands(scene, | ||||
| bm, | bm, | ||||
| &island_list, | &island_list, | ||||
| only_selected_faces, | only_selected_faces, | ||||
| only_selected_uvs, | only_selected_uvs, | ||||
| params->use_seams, | params->use_seams, | ||||
| aspect_y, | aspect_y, | ||||
| cd_loop_uv_offset); | offsets); | ||||
| /* Remove from linked list and append to blender::Vector. */ | /* Remove from linked list and append to blender::Vector. */ | ||||
| LISTBASE_FOREACH_MUTABLE (struct FaceIsland *, island, &island_list) { | LISTBASE_FOREACH_MUTABLE (struct FaceIsland *, island, &island_list) { | ||||
| BLI_remlink(&island_list, island); | BLI_remlink(&island_list, island); | ||||
| if (params->ignore_pinned && island_has_pins(scene, island, params)) { | if (params->ignore_pinned && island_has_pins(scene, island, params)) { | ||||
| MEM_freeN(island->faces); | MEM_freeN(island->faces); | ||||
| MEM_freeN(island); | MEM_freeN(island); | ||||
| continue; | continue; | ||||
| Show All 11 Lines | void ED_uvedit_pack_islands_multi(const Scene *scene, | ||||
| INIT_MINMAX2(selection_min_co, selection_max_co); | INIT_MINMAX2(selection_min_co, selection_max_co); | ||||
| for (int index = 0; index < island_vector.size(); index++) { | for (int index = 0; index < island_vector.size(); index++) { | ||||
| FaceIsland *island = island_vector[index]; | FaceIsland *island = island_vector[index]; | ||||
| if (closest_udim) { | if (closest_udim) { | ||||
| /* Only calculate selection bounding box if using closest_udim. */ | /* Only calculate selection bounding box if using closest_udim. */ | ||||
| for (int i = 0; i < island->faces_len; i++) { | for (int i = 0; i < island->faces_len; i++) { | ||||
| BMFace *f = island->faces[i]; | BMFace *f = island->faces[i]; | ||||
| BM_face_uv_minmax(f, selection_min_co, selection_max_co, island->cd_loop_uv_offset); | BM_face_uv_minmax(f, selection_min_co, selection_max_co, island->offsets.uv); | ||||
| } | } | ||||
| } | } | ||||
| if (params->rotate) { | if (params->rotate) { | ||||
| face_island_uv_rotate_fit_aabb(island); | face_island_uv_rotate_fit_aabb(island); | ||||
| } | } | ||||
| bm_face_array_calc_bounds( | bm_face_array_calc_bounds( | ||||
| island->faces, island->faces_len, island->cd_loop_uv_offset, &island->bounds_rect); | island->faces, island->faces_len, island->offsets.uv, &island->bounds_rect); | ||||
| } | } | ||||
| /* Center of bounding box containing all selected UVs. */ | /* Center of bounding box containing all selected UVs. */ | ||||
| float selection_center[2]; | float selection_center[2]; | ||||
| if (closest_udim) { | if (closest_udim) { | ||||
| selection_center[0] = (selection_min_co[0] + selection_max_co[0]) / 2.0f; | selection_center[0] = (selection_min_co[0] + selection_max_co[0]) / 2.0f; | ||||
| selection_center[1] = (selection_min_co[1] + selection_max_co[1]) / 2.0f; | selection_center[1] = (selection_min_co[1] + selection_max_co[1]) / 2.0f; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 73 Lines • Show Last 20 Lines | |||||