/* * $Id$ * * ***** 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): none yet. * * ***** END GPL LICENSE BLOCK ***** */ /** \file blender/editors/uvedit/uvedit_ops.c * \ingroup eduv */ #include #include #include #include "MEM_guardedalloc.h" #include "DNA_object_types.h" #include "DNA_meshdata_types.h" #include "DNA_scene_types.h" #include "BLI_math.h" #include "BLI_blenlib.h" #include "BLI_editVert.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_mesh.h" #include "BKE_report.h" #include "ED_image.h" #include "ED_mesh.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" /************************* state testing ************************/ int ED_uvedit_test(Object *obedit) { EditMesh *em; int ret; if(!obedit || obedit->type != OB_MESH) return 0; em = BKE_mesh_get_editmesh(obedit->data); ret = EM_texFaceCheck(em); BKE_mesh_end_editmesh(obedit->data, em); return ret; } /************************* assign image ************************/ void ED_uvedit_assign_image(Scene *scene, Object *obedit, Image *ima, Image *previma) { EditMesh *em; EditFace *efa; MTFace *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= BKE_mesh_get_editmesh(((Mesh*)obedit->data)); if(!em || !em->faces.first) { BKE_mesh_end_editmesh(obedit->data, em); return; } /* ensure we have a uv layer */ if(!CustomData_has_layer(&em->fdata, CD_MTFACE)) { EM_add_data_layer(em, &em->fdata, CD_MTFACE, NULL); update= 1; } /* now assign to all visible faces */ for(efa= em->faces.first; efa; efa= efa->next) { tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); if(uvedit_face_visible(scene, previma, efa, tf)) { if(ima) { tf->tpage= ima; tf->mode |= TF_TEX; if(ima->id.us==0) id_us_plus(&ima->id); else id_lib_extern(&ima->id); } else { tf->tpage= NULL; tf->mode &= ~TF_TEX; } update = 1; } } /* and update depdency graph */ if(update) DAG_id_tag_update(obedit->data, 0); BKE_mesh_end_editmesh(obedit->data, em); } /* 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) { EditMesh *em; EditFace *efa; MTFace *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= BKE_mesh_get_editmesh((Mesh*)obedit->data); for(efa= em->faces.first; efa; efa= efa->next) { tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); if(efa->h==0 && efa->f & SELECT) tf->tile= curtile; /* set tile index */ } DAG_id_tag_update(obedit->data, 0); BKE_mesh_end_editmesh(obedit->data, em); 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, EditFace *efa) { ToolSettings *ts= scene->toolsettings; if(ts->uv_flag & UV_SYNC_SELECTION) return (efa->h==0); else return (efa->h==0 && (efa->f & SELECT)); } int uvedit_face_visible(Scene *scene, Image *ima, EditFace *efa, MTFace *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, EditFace *efa, MTFace *tf) { ToolSettings *ts= scene->toolsettings; if(ts->uv_flag & UV_SYNC_SELECTION) return (efa->f & SELECT); else return (!(~tf->flag & (TF_SEL1|TF_SEL2|TF_SEL3)) &&(!efa->v4 || tf->flag & TF_SEL4)); } void uvedit_face_select(Scene *scene, EditFace *efa, MTFace *tf) { ToolSettings *ts= scene->toolsettings; if(ts->uv_flag & UV_SYNC_SELECTION) EM_select_face(efa, 1); else tf->flag |= (TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4); } void uvedit_face_deselect(Scene *scene, EditFace *efa, MTFace *tf) { ToolSettings *ts= scene->toolsettings; if(ts->uv_flag & UV_SYNC_SELECTION) EM_select_face(efa, 0); else tf->flag &= ~(TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4); } int uvedit_edge_selected(Scene *scene, EditFace *efa, MTFace *tf, int i) { ToolSettings *ts= scene->toolsettings; int nvert= (efa->v4)? 4: 3; if(ts->uv_flag & UV_SYNC_SELECTION) { if(ts->selectmode & SCE_SELECT_FACE) return (efa->f & SELECT); else if(ts->selectmode & SCE_SELECT_EDGE) return (*(&efa->e1 + i))->f & SELECT; else return (((efa->v1 + i)->f & SELECT) && ((efa->v1 + (i+1)%nvert)->f & SELECT)); } else return (tf->flag & TF_SEL_MASK(i)) && (tf->flag & TF_SEL_MASK((i+1)%nvert)); } void uvedit_edge_select(Scene *scene, EditFace *efa, MTFace *tf, int i) { ToolSettings *ts= scene->toolsettings; int nvert= (efa->v4)? 4: 3; if(ts->uv_flag & UV_SYNC_SELECTION) { if(ts->selectmode & SCE_SELECT_FACE) EM_select_face(efa, 1); else if(ts->selectmode & SCE_SELECT_EDGE) EM_select_edge((*(&efa->e1 + i)), 1); else { (efa->v1 + i)->f |= SELECT; (efa->v1 + (i+1)%nvert)->f |= SELECT; } } else tf->flag |= TF_SEL_MASK(i)|TF_SEL_MASK((i+1)%nvert); } void uvedit_edge_deselect(Scene *scene, EditFace *efa, MTFace *tf, int i) { ToolSettings *ts= scene->toolsettings; int nvert= (efa->v4)? 4: 3; if(ts->uv_flag & UV_SYNC_SELECTION) { if(ts->selectmode & SCE_SELECT_FACE) EM_select_face(efa, 0); else if(ts->selectmode & SCE_SELECT_EDGE) EM_select_edge((*(&efa->e1 + i)), 0); else { (efa->v1 + i)->f &= ~SELECT; (efa->v1 + (i+1)%nvert)->f &= ~SELECT; } } else tf->flag &= ~(TF_SEL_MASK(i)|TF_SEL_MASK((i+1)%nvert)); } int uvedit_uv_selected(Scene *scene, EditFace *efa, MTFace *tf, int i) { ToolSettings *ts= scene->toolsettings; if(ts->uv_flag & UV_SYNC_SELECTION) { if(ts->selectmode & SCE_SELECT_FACE) return (efa->f & SELECT); else return (*(&efa->v1 + i))->f & SELECT; } else return tf->flag & TF_SEL_MASK(i); } void uvedit_uv_select(Scene *scene, EditFace *efa, MTFace *tf, int i) { ToolSettings *ts= scene->toolsettings; if(ts->uv_flag & UV_SYNC_SELECTION) { if(ts->selectmode & SCE_SELECT_FACE) EM_select_face(efa, 1); else (*(&efa->v1 + i))->f |= SELECT; } else tf->flag |= TF_SEL_MASK(i); } void uvedit_uv_deselect(Scene *scene, EditFace *efa, MTFace *tf, int i) { ToolSettings *ts= scene->toolsettings; if(ts->uv_flag & UV_SYNC_SELECTION) { if(ts->selectmode & SCE_SELECT_FACE) EM_select_face(efa, 0); else (*(&efa->v1 + i))->f &= ~SELECT; } else tf->flag &= ~TF_SEL_MASK(i); } /*********************** live unwrap utilities ***********************/ static 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 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]); } void uv_copy_aspect(float uv_orig[][2], float uv[][2], float aspx, float aspy) { uv[0][0] = uv_orig[0][0]*aspx; uv[0][1] = uv_orig[0][1]*aspy; uv[1][0] = uv_orig[1][0]*aspx; uv[1][1] = uv_orig[1][1]*aspy; uv[2][0] = uv_orig[2][0]*aspx; uv[2][1] = uv_orig[2][1]*aspy; uv[3][0] = uv_orig[3][0]*aspx; uv[3][1] = uv_orig[3][1]*aspy; } int ED_uvedit_minmax(Scene *scene, Image *ima, Object *obedit, float *min, float *max) { EditMesh *em= BKE_mesh_get_editmesh((Mesh*)obedit->data); EditFace *efa; MTFace *tf; int sel; INIT_MINMAX2(min, max); sel= 0; for(efa= em->faces.first; efa; efa= efa->next) { tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); if(uvedit_face_visible(scene, ima, efa, tf)) { if(uvedit_uv_selected(scene, efa, tf, 0)) { DO_MINMAX2(tf->uv[0], min, max); sel = 1; } if(uvedit_uv_selected(scene, efa, tf, 1)) { DO_MINMAX2(tf->uv[1], min, max); sel = 1; } if(uvedit_uv_selected(scene, efa, tf, 2)) { DO_MINMAX2(tf->uv[2], min, max); sel = 1; } if(efa->v4 && (uvedit_uv_selected(scene, efa, tf, 3))) { DO_MINMAX2(tf->uv[3], min, max); sel = 1; } } } BKE_mesh_end_editmesh(obedit->data, em); return sel; } int ED_uvedit_median(Scene *scene, Image *ima, Object *obedit, float co[3]) { EditMesh *em= BKE_mesh_get_editmesh((Mesh*)obedit->data); EditFace *efa; MTFace *tf; unsigned int sel= 0; zero_v3(co); for(efa= em->faces.first; efa; efa= efa->next) { tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); if(uvedit_face_visible(scene, ima, efa, tf)) { if(uvedit_uv_selected(scene, efa, tf, 0)) { add_v3_v3(co, tf->uv[0]); sel++; } if(uvedit_uv_selected(scene, efa, tf, 1)) { add_v3_v3(co, tf->uv[1]); sel++; } if(uvedit_uv_selected(scene, efa, tf, 2)) { add_v3_v3(co, tf->uv[2]); sel++; } if(efa->v4 && (uvedit_uv_selected(scene, efa, tf, 3))) { add_v3_v3(co, tf->uv[3]); sel++; } } } mul_v3_fl(co, 1.0f/(float)sel); BKE_mesh_end_editmesh(obedit->data, em); return (sel != 0); } static int uvedit_center(Scene *scene, Image *ima, Object *obedit, float *cent, char mode) { EditMesh *em= BKE_mesh_get_editmesh((Mesh*)obedit->data); 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) { BKE_mesh_end_editmesh(obedit->data, em); return 1; } BKE_mesh_end_editmesh(obedit->data, em); return 0; } /************************** find nearest ****************************/ typedef struct NearestHit { EditFace *efa; MTFace *tf; int vert, uv; int edge, vert2; } NearestHit; static void find_nearest_uv_edge(Scene *scene, Image *ima, EditMesh *em, float co[2], NearestHit *hit) { MTFace *tf; EditFace *efa; EditVert *eve; float mindist, dist; int i, nverts; mindist= 1e10f; memset(hit, 0, sizeof(*hit)); for(i=0, eve=em->verts.first; eve; eve=eve->next, i++) eve->tmp.l = i; for(efa= em->faces.first; efa; efa= efa->next) { tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); if(uvedit_face_visible(scene, ima, efa, tf)) { nverts= efa->v4? 4: 3; for(i=0; iuv[i], tf->uv[(i+1)%nverts]); if(dist < mindist) { hit->tf= tf; hit->efa= efa; hit->edge= i; mindist= dist; hit->vert= (*(&efa->v1 + i))->tmp.l; hit->vert2= (*(&efa->v1 + ((i+1)%nverts)))->tmp.l; } } } } } static void find_nearest_uv_face(Scene *scene, Image *ima, EditMesh *em, float co[2], NearestHit *hit) { MTFace *tf; EditFace *efa; float mindist, dist, cent[2]; int i, nverts; mindist= 1e10f; memset(hit, 0, sizeof(*hit)); for(efa= em->faces.first; efa; efa= efa->next) { tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); if(uvedit_face_visible(scene, ima, efa, tf)) { nverts= efa->v4? 4: 3; cent[0]= cent[1]= 0.0f; for(i=0; iuv[i][0]; cent[1] += tf->uv[i][1]; } cent[0] /= nverts; cent[1] /= nverts; 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(MTFace *tf, int nverts, int id, float co[2], float uv[2]) { float m[3], v1[3], v2[3], c1, c2; int id1, id2; id1= (id+nverts-1)%nverts; id2= (id+nverts+1)%nverts; m[0]= co[0]-uv[0]; m[1]= co[1]-uv[1]; sub_v2_v2v2(v1, tf->uv[id1], tf->uv[id]); sub_v2_v2v2(v2, tf->uv[id2], tf->uv[id]); /* 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); } static void find_nearest_uv_vert(Scene *scene, Image *ima, EditMesh *em, float co[2], float penalty[2], NearestHit *hit) { EditFace *efa; EditVert *eve; MTFace *tf; float mindist, dist; int i, nverts; mindist= 1e10f; memset(hit, 0, sizeof(*hit)); for(i=0, eve=em->verts.first; eve; eve=eve->next, i++) eve->tmp.l = i; for(efa= em->faces.first; efa; efa= efa->next) { tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); if(uvedit_face_visible(scene, ima, efa, tf)) { nverts= efa->v4? 4: 3; for(i=0; iuv[i][0])+penalty[0] + fabsf(co[1]-tf->uv[i][1]) + penalty[1]; else dist= fabsf(co[0]-tf->uv[i][0]) + fabsf(co[1]-tf->uv[i][1]); if(dist<=mindist) { if(dist==mindist) if(!nearest_uv_between(tf, nverts, i, co, tf->uv[i])) continue; mindist= dist; hit->uv= i; hit->tf= tf; hit->efa= efa; hit->vert= (*(&efa->v1 + i))->tmp.l; } } } } } int ED_uvedit_nearest_uv(Scene *scene, Object *obedit, Image *ima, float co[2], float uv[2]) { EditMesh *em= BKE_mesh_get_editmesh((Mesh*)obedit->data); EditFace *efa; MTFace *tf; float mindist, dist; int i, nverts, found= 0; mindist= 1e10f; uv[0]= co[0]; uv[1]= co[1]; for(efa= em->faces.first; efa; efa= efa->next) { tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); if(uvedit_face_visible(scene, ima, efa, tf)) { nverts= efa->v4? 4: 3; for(i=0; iuv[i][0]) + fabs(co[1]-tf->uv[i][1]); if(dist<=mindist) { mindist= dist; uv[0]= tf->uv[i][0]; uv[1]= tf->uv[i][1]; found= 1; } } } } BKE_mesh_end_editmesh(obedit->data, em); 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, EditFace *efa, int a) { UvMapVert *iterv, *first; first= EM_get_uv_map_vert(vmap, (*(&efa->v1 + a))->tmp.l); for(iterv=first; iterv; iterv=iterv->next) { if(iterv->separate) first= iterv; if(iterv->f == efa->tmp.l) return first; } return NULL; } static int uv_edge_tag_faces(UvMapVert *first1, UvMapVert *first2, int *totface) { UvMapVert *iterv1, *iterv2; EditFace *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= EM_get_face_for_index(iterv1->f); if(efa->f1) 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= EM_get_face_for_index(iterv1->f); efa->f1= 1; break; } } } return 1; } static int select_edgeloop(Scene *scene, Image *ima, EditMesh *em, NearestHit *hit, float limit[2], int extend) { EditVert *eve; EditFace *efa; MTFace *tf; UvVertMap *vmap; UvMapVert *iterv1, *iterv2; int a, count, looking, nverts, starttotf, select; /* setup */ EM_init_index_arrays(em, 0, 0, 1); vmap= EM_make_uv_vert_map(em, 0, 0, limit); for(count=0, eve=em->verts.first; eve; count++, eve= eve->next) eve->tmp.l = count; for(count=0, efa= em->faces.first; efa; count++, efa= efa->next) { if(!extend) { tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); uvedit_face_deselect(scene, efa, tf); } efa->tmp.l= count; efa->f1= 0; } /* set flags for first face and verts */ nverts= (hit->efa->v4)? 4: 3; iterv1= uv_vertex_map_get(vmap, hit->efa, hit->edge); iterv2= uv_vertex_map_get(vmap, hit->efa, (hit->edge+1)%nverts); uv_vertex_loop_flag(iterv1); uv_vertex_loop_flag(iterv2); starttotf= 0; uv_edge_tag_faces(iterv1, iterv2, &starttotf); /* sorry, first edge isnt 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 */ for(efa= em->faces.first; efa; efa=efa->next) { tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); if(!efa->f1 && uvedit_face_visible(scene, ima, efa, tf)) { nverts= (efa->v4)? 4: 3; for(a=0; a