/* * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. * All rights reserved. * * The Original Code is: all of this file. * * Contributor(s): Antony Riakiotakis. * * ***** END GPL LICENSE BLOCK ***** */ /** \file blender/editors/uvedit/uvedit_ops.c * \ingroup eduv */ #include #include #include #include #include "MEM_guardedalloc.h" #include "DNA_object_types.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_node_types.h" #include "DNA_image_types.h" #include "DNA_space_types.h" #include "DNA_scene_types.h" #include "BLI_math.h" #include "BLI_blenlib.h" #include "BLI_array.h" #include "BLI_utildefines.h" #include "BKE_context.h" #include "BKE_customdata.h" #include "BKE_depsgraph.h" #include "BKE_image.h" #include "BKE_library.h" #include "BKE_main.h" #include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_node.h" #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_tessmesh.h" #include "ED_image.h" #include "ED_mesh.h" #include "ED_node.h" #include "ED_uvedit.h" #include "ED_object.h" #include "ED_screen.h" #include "ED_transform.h" #include "RNA_access.h" #include "RNA_define.h" #include "WM_api.h" #include "WM_types.h" #include "UI_view2d.h" #include "uvedit_intern.h" #define EFA_F1_FLAG 2 /************************* state testing ************************/ int ED_uvedit_test(Object *obedit) { BMEditMesh *em; int ret; if (!obedit) return 0; if (obedit->type != OB_MESH) return 0; em = BMEdit_FromObject(obedit); ret = EDBM_texFaceCheck(em); return ret; } static int ED_operator_uvedit_can_uv_sculpt(struct bContext *C) { SpaceImage *sima= CTX_wm_space_image(C); ToolSettings *toolsettings = CTX_data_tool_settings(C); Object *obedit= CTX_data_edit_object(C); return ED_space_image_show_uvedit(sima, obedit) && !(toolsettings->use_uv_sculpt); } static int UNUSED_FUNCTION(ED_operator_uvmap_mesh)(bContext *C) { Object *ob= CTX_data_active_object(C); if (ob && ob->type==OB_MESH) { Mesh *me = ob->data; if (CustomData_get_layer(&me->fdata, CD_MTFACE) != NULL) return 1; } return 0; } /**************************** object active image *****************************/ static int is_image_texture_node(bNode *node) { return ELEM(node->type, SH_NODE_TEX_IMAGE, SH_NODE_TEX_ENVIRONMENT); } int ED_object_get_active_image(Object *ob, int mat_nr, Image **ima, ImageUser **iuser, bNode **node_r) { Material *ma= give_current_material(ob, mat_nr); bNode *node= (ma && ma->use_nodes)? nodeGetActiveTexture(ma->nodetree): NULL; if (node && is_image_texture_node(node)) { if (ima) *ima= (Image*)node->id; if (iuser) *iuser= NULL; if (node_r) *node_r= node; return TRUE; } if (ima) *ima= NULL; if (iuser) *iuser= NULL; if (node_r) *node_r= node; return FALSE; } void ED_object_assign_active_image(Main *bmain, Object *ob, int mat_nr, Image *ima) { Material *ma= give_current_material(ob, mat_nr); bNode *node= (ma && ma->use_nodes)? nodeGetActiveTexture(ma->nodetree): NULL; if (node && is_image_texture_node(node)) { node->id= &ima->id; ED_node_generic_update(bmain, ma->nodetree, node); } } /************************* assign image ************************/ void ED_uvedit_assign_image(Main *bmain, Scene *scene, Object *obedit, Image *ima, Image *previma) { BMEditMesh *em; BMFace *efa; BMIter iter; MTexPoly *tf; int update= 0; /* skip assigning these procedural images... */ if (ima && (ima->type==IMA_TYPE_R_RESULT || ima->type==IMA_TYPE_COMPOSITE)) return; /* verify we have a mesh we can work with */ if (!obedit || (obedit->type != OB_MESH)) return; em = BMEdit_FromObject(obedit); if (!em || !em->bm->totface) { return; } if (scene_use_new_shading_nodes(scene)) { /* new shading system, assign image in material */ int sloppy= 1; BMFace *efa= BM_active_face_get(em->bm, sloppy); if (efa) ED_object_assign_active_image(bmain, obedit, efa->mat_nr, ima); } else { /* old shading system, assign image to selected faces */ /* ensure we have a uv map */ if (!CustomData_has_layer(&em->bm->pdata, CD_MTEXPOLY)) { BM_data_layer_add(em->bm, &em->bm->pdata, CD_MTEXPOLY); BM_data_layer_add(em->bm, &em->bm->ldata, CD_MLOOPUV); update= 1; } /* now assign to all visible faces */ BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) { tf = CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY); if (uvedit_face_visible(scene, previma, efa, tf)) { if (ima) { tf->tpage= ima; if (ima->id.us==0) id_us_plus(&ima->id); else id_lib_extern(&ima->id); } else { tf->tpage= NULL; } update = 1; } } /* and update depdency graph */ if (update) DAG_id_tag_update(obedit->data, 0); } } /* dotile - 1, set the tile flag (from the space image) * 2, set the tile index for the faces. */ static int uvedit_set_tile(Object *obedit, Image *ima, int curtile) { BMEditMesh *em; BMFace *efa; BMIter iter; MTexPoly *tf; /* verify if we have something to do */ if (!ima || !ED_uvedit_test(obedit)) return 0; if ((ima->tpageflag & IMA_TILES) == 0) return 0; /* skip assigning these procedural images... */ if (ima->type==IMA_TYPE_R_RESULT || ima->type==IMA_TYPE_COMPOSITE) return 0; em = BMEdit_FromObject(obedit); BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) { tf = CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY); if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) && BM_elem_flag_test(efa, BM_ELEM_SELECT)) tf->tile= curtile; /* set tile index */ } DAG_id_tag_update(obedit->data, 0); return 1; } /*********************** space conversion *********************/ static void uvedit_pixel_to_float(SpaceImage *sima, float *dist, float pixeldist) { int width, height; if (sima) { ED_space_image_size(sima, &width, &height); } else { width= 256; height= 256; } dist[0]= pixeldist/width; dist[1]= pixeldist/height; } /*************** visibility and selection utilities **************/ int uvedit_face_visible_nolocal(Scene *scene, BMFace *efa) { ToolSettings *ts= scene->toolsettings; if (ts->uv_flag & UV_SYNC_SELECTION) return (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)==0); else return (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)==0 && BM_elem_flag_test(efa, BM_ELEM_SELECT)); } int uvedit_face_visible(Scene *scene, Image *ima, BMFace *efa, MTexPoly *tf) { ToolSettings *ts= scene->toolsettings; if (ts->uv_flag & UV_SHOW_SAME_IMAGE) return (tf->tpage==ima)? uvedit_face_visible_nolocal(scene, efa): 0; else return uvedit_face_visible_nolocal(scene, efa); } int uvedit_face_selected(Scene *scene, BMEditMesh *em, BMFace *efa) { ToolSettings *ts= scene->toolsettings; if (ts->uv_flag & UV_SYNC_SELECTION) return (BM_elem_flag_test(efa, BM_ELEM_SELECT)); else { BMLoop *l; MLoopUV *luv; BMIter liter; BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, efa) { luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); if (!(luv->flag & MLOOPUV_VERTSEL)) return 0; } return 1; } } int uvedit_face_select(Scene *scene, BMEditMesh *em, BMFace *efa) { ToolSettings *ts= scene->toolsettings; if (ts->uv_flag & UV_SYNC_SELECTION) BM_elem_select_set(em->bm, efa, TRUE); else { BMLoop *l; MLoopUV *luv; BMIter liter; BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, efa) { luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); luv->flag |= MLOOPUV_VERTSEL; } return 1; } return 0; } int uvedit_face_deselect(Scene *scene, BMEditMesh *em, BMFace *efa) { ToolSettings *ts= scene->toolsettings; if (ts->uv_flag & UV_SYNC_SELECTION) { BM_elem_select_set(em->bm, efa, FALSE); } else { BMLoop *l; MLoopUV *luv; BMIter liter; BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, efa) { luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); luv->flag &= ~MLOOPUV_VERTSEL; } return 1; } return 0; } int uvedit_edge_selected(BMEditMesh *em, Scene *scene, BMLoop *l) { ToolSettings *ts= scene->toolsettings; if (ts->uv_flag & UV_SYNC_SELECTION) { if (ts->selectmode & SCE_SELECT_FACE) { return BM_elem_flag_test(l->f, BM_ELEM_SELECT); } else if (ts->selectmode == SCE_SELECT_EDGE) { return BM_elem_flag_test(l->e, BM_ELEM_SELECT); } else { return BM_elem_flag_test(l->v, BM_ELEM_SELECT) && BM_elem_flag_test(l->next->v, BM_ELEM_SELECT); } } else { MLoopUV *luv1, *luv2; luv1 = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); luv2 = CustomData_bmesh_get(&em->bm->ldata, l->next->head.data, CD_MLOOPUV); return (luv1->flag & MLOOPUV_VERTSEL) && (luv2->flag & MLOOPUV_VERTSEL); } } void uvedit_edge_select(BMEditMesh *em, Scene *scene, BMLoop *l) { ToolSettings *ts= scene->toolsettings; if (ts->uv_flag & UV_SYNC_SELECTION) { if (ts->selectmode & SCE_SELECT_FACE) BM_elem_select_set(em->bm, l->f, TRUE); else if (ts->selectmode & SCE_SELECT_EDGE) BM_elem_select_set(em->bm, l->e, TRUE); else { BM_elem_select_set(em->bm, l->e->v1, TRUE); BM_elem_select_set(em->bm, l->e->v2, TRUE); } } else { MLoopUV *luv1, *luv2; luv1 = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); luv2 = CustomData_bmesh_get(&em->bm->ldata, l->next->head.data, CD_MLOOPUV); luv1->flag |= MLOOPUV_VERTSEL; luv2->flag |= MLOOPUV_VERTSEL; } } void uvedit_edge_deselect(BMEditMesh *em, Scene *scene, BMLoop *l) { ToolSettings *ts= scene->toolsettings; if (ts->uv_flag & UV_SYNC_SELECTION) { if (ts->selectmode & SCE_SELECT_FACE) BM_elem_select_set(em->bm, l->f, FALSE); else if (ts->selectmode & SCE_SELECT_EDGE) BM_elem_select_set(em->bm, l->e, FALSE); else { BM_elem_select_set(em->bm, l->e->v1, FALSE); BM_elem_select_set(em->bm, l->e->v2, FALSE); } } else { MLoopUV *luv1, *luv2; luv1 = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); luv2 = CustomData_bmesh_get(&em->bm->ldata, l->next->head.data, CD_MLOOPUV); luv1->flag &= ~MLOOPUV_VERTSEL; luv2->flag &= ~MLOOPUV_VERTSEL; } } int uvedit_uv_selected(BMEditMesh *em, Scene *scene, BMLoop *l) { ToolSettings *ts= scene->toolsettings; if (ts->uv_flag & UV_SYNC_SELECTION) { if (ts->selectmode & SCE_SELECT_FACE) return BM_elem_flag_test(l->f, BM_ELEM_SELECT); else return BM_elem_flag_test(l->v, BM_ELEM_SELECT); } else { MLoopUV *luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); return luv->flag & MLOOPUV_VERTSEL; } } void uvedit_uv_select(BMEditMesh *em, Scene *scene, BMLoop *l) { ToolSettings *ts= scene->toolsettings; if (ts->uv_flag & UV_SYNC_SELECTION) { if (ts->selectmode & SCE_SELECT_FACE) BM_elem_select_set(em->bm, l->f, TRUE); else BM_elem_select_set(em->bm, l->v, TRUE); } else { MLoopUV *luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); luv->flag |= MLOOPUV_VERTSEL; } } void uvedit_uv_deselect(BMEditMesh *em, Scene *scene, BMLoop *l) { ToolSettings *ts= scene->toolsettings; if (ts->uv_flag & UV_SYNC_SELECTION) { if (ts->selectmode & SCE_SELECT_FACE) BM_elem_select_set(em->bm, l->f, FALSE); else BM_elem_select_set(em->bm, l->v, FALSE); } else { MLoopUV *luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); luv->flag &= ~MLOOPUV_VERTSEL; } } /*********************** live unwrap utilities ***********************/ void uvedit_live_unwrap_update(SpaceImage *sima, Scene *scene, Object *obedit) { if (sima && (sima->flag & SI_LIVE_UNWRAP)) { ED_uvedit_live_unwrap_begin(scene, obedit); ED_uvedit_live_unwrap_re_solve(); ED_uvedit_live_unwrap_end(0); } } /*********************** geometric utilities ***********************/ void poly_uv_center(BMEditMesh *em, BMFace *f, float cent[2]) { BMLoop *l; MLoopUV *luv; BMIter liter; zero_v2(cent); BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, f) { luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); add_v2_v2(cent, luv->uv); } mul_v2_fl(cent, 1.0f / (float)f->len); } void uv_center(float uv[][2], float cent[2], int quad) { if (quad) { cent[0] = (uv[0][0] + uv[1][0] + uv[2][0] + uv[3][0]) / 4.0f; cent[1] = (uv[0][1] + uv[1][1] + uv[2][1] + uv[3][1]) / 4.0f; } else { cent[0] = (uv[0][0] + uv[1][0] + uv[2][0]) / 3.0f; cent[1] = (uv[0][1] + uv[1][1] + uv[2][1]) / 3.0f; } } float uv_area(float uv[][2], int quad) { if (quad) return area_tri_v2(uv[0], uv[1], uv[2]) + area_tri_v2(uv[0], uv[2], uv[3]); else return area_tri_v2(uv[0], uv[1], uv[2]); } float poly_uv_area(float uv[][2], int len) { //BMESH_TODO: make this not suck //maybe use scanfill? I dunno. if (len >= 4) return area_tri_v2(uv[0], uv[1], uv[2]) + area_tri_v2(uv[0], uv[2], uv[3]); else return area_tri_v2(uv[0], uv[1], uv[2]); return 1.0; } void poly_copy_aspect(float uv_orig[][2], float uv[][2], float aspx, float aspy, int len) { int i; for (i=0; ibm, BM_FACES_OF_MESH, NULL) { tf = CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY); if (!uvedit_face_visible(scene, ima, efa, tf)) continue; BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, efa) { if (uvedit_uv_selected(em, scene, l)) { luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); DO_MINMAX2(luv->uv, min, max); sel = 1; } } } return sel; } static int ED_uvedit_median(Scene *scene, Image *ima, Object *obedit, float co[3]) { BMEditMesh *em = BMEdit_FromObject(obedit); BMFace *efa; BMLoop *l; BMIter iter, liter; MTexPoly *tf; MLoopUV *luv; unsigned int sel= 0; zero_v3(co); BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) { tf= CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY); if (!uvedit_face_visible(scene, ima, efa, tf)) continue; BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, efa) { luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); if (uvedit_uv_selected(em, scene, l)) { add_v2_v2(co, luv->uv); sel++; } } } mul_v3_fl(co, 1.0f/(float)sel); return (sel != 0); } static int uvedit_center(Scene *scene, Image *ima, Object *obedit, float *cent, char mode) { float min[2], max[2]; int change= 0; if (mode==V3D_CENTER) { /* bounding box */ if (ED_uvedit_minmax(scene, ima, obedit, min, max)) { change = 1; cent[0]= (min[0]+max[0])/2.0f; cent[1]= (min[1]+max[1])/2.0f; } } else { if (ED_uvedit_median(scene, ima, obedit, cent)) { change = 1; } } if (change) { return 1; } return 0; } /************************** find nearest ****************************/ void uv_find_nearest_edge(Scene *scene, Image *ima, BMEditMesh *em, float co[2], NearestHit *hit) { MTexPoly *tf; BMFace *efa; BMLoop *l; BMIter iter, liter; MLoopUV *luv, *nextluv; float mindist, dist; int i; mindist= 1e10f; memset(hit, 0, sizeof(*hit)); BM_mesh_elem_index_ensure(em->bm, BM_VERT); BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) { tf= CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY); if (!uvedit_face_visible(scene, ima, efa, tf)) continue; i = 0; BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, efa) { luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); nextluv = CustomData_bmesh_get(&em->bm->ldata, l->next->head.data, CD_MLOOPUV); dist= dist_to_line_segment_v2(co, luv->uv, nextluv->uv); if (dist < mindist) { hit->tf= tf; hit->efa= efa; hit->l = l; hit->nextl = l->next; hit->luv = luv; hit->nextluv = nextluv; hit->lindex = i; hit->vert1 = BM_elem_index_get(hit->l->v); hit->vert2 = BM_elem_index_get(hit->l->next->v); mindist = dist; } i++; } } } static void find_nearest_uv_face(Scene *scene, Image *ima, BMEditMesh *em, float co[2], NearestHit *hit) { MTexPoly *tf; BMFace *efa; BMLoop *l; BMIter iter, liter; MLoopUV *luv; float mindist, dist, cent[2]; mindist= 1e10f; memset(hit, 0, sizeof(*hit)); /*this will fill in hit.vert1 and hit.vert2*/ uv_find_nearest_edge(scene, ima, em, co, hit); hit->l = hit->nextl = NULL; hit->luv = hit->nextluv = NULL; BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) { tf= CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY); if (!uvedit_face_visible(scene, ima, efa, tf)) continue; cent[0]= cent[1]= 0.0f; BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, efa) { luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); add_v2_v2(cent, luv->uv); } cent[0] /= efa->len; cent[1] /= efa->len; dist= fabs(co[0]- cent[0]) + fabs(co[1]- cent[1]); if (dist < mindist) { hit->tf= tf; hit->efa= efa; mindist= dist; } } } static int nearest_uv_between(BMEditMesh *em, BMFace *efa, int UNUSED(nverts), int id, float co[2], float uv[2]) { BMLoop *l; MLoopUV *luv; BMIter iter; float m[3], v1[3], v2[3], c1, c2, *uv1, /* *uv2, */ /* UNUSED */ *uv3; int id1, id2, i; id1= (id+efa->len-1)%efa->len; id2= (id+efa->len+1)%efa->len; m[0]= co[0]-uv[0]; m[1]= co[1]-uv[1]; i = 0; BM_ITER(l, &iter, em->bm, BM_LOOPS_OF_FACE, efa) { luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); if (i == id1) uv1 = luv->uv; else if (i == id) ; /* uv2 = luv->uv; */ /* UNUSED */ else if (i == id2) uv3 = luv->uv; i++; } sub_v3_v3v3(v1, uv1, uv); sub_v3_v3v3(v2, uv3, uv); /* m and v2 on same side of v-v1? */ c1= v1[0]*m[1] - v1[1]*m[0]; c2= v1[0]*v2[1] - v1[1]*v2[0]; if (c1*c2 < 0.0f) return 0; /* m and v1 on same side of v-v2? */ c1= v2[0]*m[1] - v2[1]*m[0]; c2= v2[0]*v1[1] - v2[1]*v1[0]; return (c1*c2 >= 0.0f); } void uv_find_nearest_vert(Scene *scene, Image *ima, BMEditMesh *em, float co[2], float penalty[2], NearestHit *hit) { BMFace *efa; BMLoop *l; BMIter iter, liter; MTexPoly *tf; MLoopUV *luv; float mindist, dist; int i; /*this will fill in hit.vert1 and hit.vert2*/ uv_find_nearest_edge(scene, ima, em, co, hit); hit->l = hit->nextl = NULL; hit->luv = hit->nextluv = NULL; mindist= 1e10f; memset(hit, 0, sizeof(*hit)); BM_mesh_elem_index_ensure(em->bm, BM_VERT); BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) { tf= CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY); if (!uvedit_face_visible(scene, ima, efa, tf)) continue; i = 0; BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, efa) { luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); if (penalty && uvedit_uv_selected(em, scene, l)) dist= fabs(co[0]-luv->uv[0])+penalty[0] + fabs(co[1]-luv->uv[1])+penalty[1]; else dist= fabs(co[0]-luv->uv[0]) + fabs(co[1]-luv->uv[1]); if (dist<=mindist) { if (dist==mindist) if (!nearest_uv_between(em, efa, efa->len, i, co, luv->uv)) { i++; continue; } mindist= dist; hit->l = l; hit->nextl = l->next; hit->luv = luv; hit->nextluv = CustomData_bmesh_get(&em->bm->ldata, l->next->head.data, CD_MLOOPUV); hit->tf= tf; hit->efa= efa; hit->lindex = i; hit->vert1 = BM_elem_index_get(hit->l->v); } i++; } } } int ED_uvedit_nearest_uv(Scene *scene, Object *obedit, Image *ima, float co[2], float uv[2]) { BMEditMesh *em = BMEdit_FromObject(obedit); BMFace *efa; BMLoop *l; BMIter iter, liter; MTexPoly *tf; MLoopUV *luv; float mindist, dist; int found= 0; mindist= 1e10f; uv[0]= co[0]; uv[1]= co[1]; BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) { tf= CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY); if (!uvedit_face_visible(scene, ima, efa, tf)) continue; BM_ITER(l, &liter, em->bm, BM_LOOPS_OF_FACE, efa) { luv = CustomData_bmesh_get(&em->bm->ldata, l->head.data, CD_MLOOPUV); dist= fabs(co[0]-luv->uv[0]) + fabs(co[1]-luv->uv[1]); if (dist<=mindist) { mindist= dist; uv[0]= luv->uv[0]; uv[1]= luv->uv[1]; found= 1; } } } return found; } /*********************** loop select ***********************/ static void uv_vertex_loop_flag(UvMapVert *first) { UvMapVert *iterv; int count= 0; for (iterv=first; iterv; iterv=iterv->next) { if (iterv->separate && iterv!=first) break; count++; } if (count < 5) first->flag= 1; } static UvMapVert *uv_vertex_map_get(UvVertMap *vmap, BMFace *efa, int a) { UvMapVert *iterv, *first; BMLoop *l; l = BM_iter_at_index(NULL, BM_LOOPS_OF_FACE, efa, a); first= EDBM_get_uv_map_vert(vmap, BM_elem_index_get(l->v)); for (iterv=first; iterv; iterv=iterv->next) { if (iterv->separate) first= iterv; if (iterv->f == BM_elem_index_get(efa)) return first; } return NULL; } UvElement *ED_get_uv_element(UvElementMap *map, BMFace *efa, BMLoop *l) { UvElement *element; element = map->vert[BM_elem_index_get(l->v)]; for (; element; element = element->next) if (element->face == efa) return element; return NULL; } static int uv_edge_tag_faces(BMEditMesh *em, UvMapVert *first1, UvMapVert *first2, int *totface) { UvMapVert *iterv1, *iterv2; BMFace *efa; int tot = 0; /* count number of faces this edge has */ for (iterv1=first1; iterv1; iterv1=iterv1->next) { if (iterv1->separate && iterv1 != first1) break; for (iterv2=first2; iterv2; iterv2=iterv2->next) { if (iterv2->separate && iterv2 != first2) break; if (iterv1->f == iterv2->f) { /* if face already tagged, don't do this edge */ efa= EDBM_get_face_for_index(em, iterv1->f); if (BMO_elem_flag_test(em->bm, efa, EFA_F1_FLAG)) return 0; tot++; break; } } } if (*totface == 0) /* start edge */ *totface= tot; else if (tot != *totface) /* check for same number of faces as start edge */ return 0; /* tag the faces */ for (iterv1=first1; iterv1; iterv1=iterv1->next) { if (iterv1->separate && iterv1 != first1) break; for (iterv2=first2; iterv2; iterv2=iterv2->next) { if (iterv2->separate && iterv2 != first2) break; if (iterv1->f == iterv2->f) { efa= EDBM_get_face_for_index(em, iterv1->f); BMO_elem_flag_enable(em->bm, efa, EFA_F1_FLAG); break; } } } return 1; } static int select_edgeloop(Scene *scene, Image *ima, BMEditMesh *em, NearestHit *hit, float limit[2], int extend) { BMFace *efa; BMIter iter, liter; BMLoop *l; MTexPoly *tf; UvVertMap *vmap; UvMapVert *iterv1, *iterv2; int a, count, looking, nverts, starttotf, select; /* setup */ EDBM_init_index_arrays(em, 0, 0, 1); vmap= EDBM_make_uv_vert_map(em, 0, 0, limit); BM_mesh_elem_index_ensure(em->bm, BM_VERT); count = 0; BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) { if (!extend) { uvedit_face_deselect(scene, em, efa); } BMO_elem_flag_disable(em->bm, efa, EFA_F1_FLAG); BM_elem_index_set(efa, count); /* set_inline */ count++; } em->bm->elem_index_dirty &= ~BM_FACE; /* set flags for first face and verts */ nverts= hit->efa->len; iterv1= uv_vertex_map_get(vmap, hit->efa, hit->lindex); iterv2= uv_vertex_map_get(vmap, hit->efa, (hit->lindex+1)%nverts); uv_vertex_loop_flag(iterv1); uv_vertex_loop_flag(iterv2); starttotf= 0; uv_edge_tag_faces(em, iterv1, iterv2, &starttotf); /* sorry, first edge isn't even ok */ if (iterv1->flag==0 && iterv2->flag==0) looking= 0; else looking= 1; /* iterate */ while (looking) { looking= 0; /* find correct valence edges which are not tagged yet, but connect to tagged one */ BM_ITER(efa, &iter, em->bm, BM_FACES_OF_MESH, NULL) { tf= CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY); if (!BMO_elem_flag_test(em->bm, efa, EFA_F1_FLAG) && uvedit_face_visible(scene, ima, efa, tf)) { nverts= efa->len; for (a=0; a