diff options
author | Antony Riakiotakis <kalast@gmail.com> | 2012-01-17 20:31:13 +0400 |
---|---|---|
committer | Antony Riakiotakis <kalast@gmail.com> | 2012-01-17 20:31:13 +0400 |
commit | a8081c1d2bb9115833493b011bb93d6c08112b2d (patch) | |
tree | 4383d080b80786257b291068e48a06bdb0ef5bd6 /source/blender/editors/uvedit | |
parent | fcc54520d1f029b86cb8c6f43c239ef81090a99a (diff) |
Uv Tools branch GSOC 2011
=========================
Documentation: http://wiki.blender.org/index.php/User:Psy-Fi/UV_Tools
Major features include:
*16 bit image support in viewport
*Subsurf aware unwrapping
*Smart Stitch(snap/rotate islands, preview, middlepoint/endpoint stitching)
*Seams from islands tool (marks seams and sharp, depending on settings)
*Uv Sculpting(Grab/Pinch/Rotate)
All tools are complete apart from stitching that is considered stable but with an extra edge mode under development(will be in soc-2011-onion-uv-tools).
Diffstat (limited to 'source/blender/editors/uvedit')
-rw-r--r-- | source/blender/editors/uvedit/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/editors/uvedit/uvedit_draw.c | 110 | ||||
-rw-r--r-- | source/blender/editors/uvedit/uvedit_intern.h | 58 | ||||
-rw-r--r-- | source/blender/editors/uvedit/uvedit_ops.c | 435 | ||||
-rw-r--r-- | source/blender/editors/uvedit/uvedit_parametrizer.c | 18 | ||||
-rw-r--r-- | source/blender/editors/uvedit/uvedit_smart_stitch.c | 1442 | ||||
-rw-r--r-- | source/blender/editors/uvedit/uvedit_unwrap_ops.c | 228 |
7 files changed, 2058 insertions, 234 deletions
diff --git a/source/blender/editors/uvedit/CMakeLists.txt b/source/blender/editors/uvedit/CMakeLists.txt index ce078c2b1f1..a747c2ac6bd 100644 --- a/source/blender/editors/uvedit/CMakeLists.txt +++ b/source/blender/editors/uvedit/CMakeLists.txt @@ -39,6 +39,7 @@ set(SRC uvedit_draw.c uvedit_ops.c uvedit_parametrizer.c + uvedit_smart_stitch.c uvedit_unwrap_ops.c uvedit_intern.h diff --git a/source/blender/editors/uvedit/uvedit_draw.c b/source/blender/editors/uvedit/uvedit_draw.c index 0da3f66fc6f..6ced91f0d01 100644 --- a/source/blender/editors/uvedit/uvedit_draw.c +++ b/source/blender/editors/uvedit/uvedit_draw.c @@ -380,12 +380,9 @@ static void draw_uvs_stretch(SpaceImage *sima, Scene *scene, EditMesh *em, MTFac } } -static void draw_uvs_other(Scene *scene, Object *obedit, MTFace *activetf) +static void draw_uvs_other(Scene *scene, Object *obedit, Image *curimage) { Base *base; - Image *curimage; - - curimage= (activetf)? activetf->tpage: NULL; glColor3ub(96, 96, 96); @@ -419,6 +416,34 @@ static void draw_uvs_other(Scene *scene, Object *obedit, MTFace *activetf) } } +static void draw_uvs_texpaint(SpaceImage *sima, Scene *scene, Object *ob) +{ + Mesh *me= ob->data; + Image *curimage = ED_space_image(sima); + + if(sima->flag & SI_DRAW_OTHER) + draw_uvs_other(scene, ob, curimage); + + glColor3ub(112, 112, 112); + + if(me->mtface) { + MFace *mface= me->mface; + MTFace *tface= me->mtface; + int a; + + for(a=me->totface; a>0; a--, tface++, mface++) { + if(tface->tpage == curimage) { + glBegin(GL_LINE_LOOP); + glVertex2fv(tface->uv[0]); + glVertex2fv(tface->uv[1]); + glVertex2fv(tface->uv[2]); + if(mface->v4) glVertex2fv(tface->uv[3]); + glEnd(); + } + } + } +} + /* draws uv's in the image space */ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit) { @@ -432,6 +457,7 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit) float pointsize; int drawfaces, interpedges; Image *ima= sima->image; + StitchPreviewer *stitch_preview = uv_get_stitch_previewer(); em= BKE_mesh_get_editmesh(me); activetf= EM_get_active_mtface(em, &efa_act, NULL, 0); /* will be set to NULL if hidden */ @@ -445,8 +471,11 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit) interpedges= (ts->uv_selectmode == UV_SELECT_VERTEX); /* draw other uvs */ - if(sima->flag & SI_DRAW_OTHER) - draw_uvs_other(scene, obedit, activetf); + if(sima->flag & SI_DRAW_OTHER) { + Image *curimage= (activetf)? activetf->tpage: NULL; + + draw_uvs_other(scene, obedit, curimage); + } /* 1. draw shadow mesh */ @@ -522,7 +551,7 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit) } } - + /* 3. draw active face stippled */ if(activetf) { @@ -831,24 +860,81 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit) bglEnd(); } + /* finally draw stitch preview */ + if(stitch_preview) { + glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT); + glEnableClientState(GL_VERTEX_ARRAY); + + glEnable(GL_BLEND); + + UI_ThemeColor4(TH_STITCH_PREVIEW_ACTIVE); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glVertexPointer(2, GL_FLOAT, 0, stitch_preview->static_tris); + glDrawArrays(GL_TRIANGLES, 0, stitch_preview->num_static_tris*3); + + glVertexPointer(2, GL_FLOAT, 0, stitch_preview->static_quads); + glDrawArrays(GL_QUADS, 0, stitch_preview->num_static_quads*4); + + glVertexPointer(2, GL_FLOAT, 0, stitch_preview->preview_tris); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + UI_ThemeColor4(TH_STITCH_PREVIEW_FACE); + glDrawArrays(GL_TRIANGLES, 0, stitch_preview->num_tris*3); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + UI_ThemeColor4(TH_STITCH_PREVIEW_EDGE); + glDrawArrays(GL_TRIANGLES, 0, stitch_preview->num_tris*3); + glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); + /*UI_ThemeColor4(TH_STITCH_PREVIEW_VERT); + glDrawArrays(GL_TRIANGLES, 0, stitch_preview->num_tris*3);*/ + + glVertexPointer(2, GL_FLOAT, 0, stitch_preview->preview_quads); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + UI_ThemeColor4(TH_STITCH_PREVIEW_FACE); + glDrawArrays(GL_QUADS, 0, stitch_preview->num_quads*4); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + UI_ThemeColor4(TH_STITCH_PREVIEW_EDGE); + glDrawArrays(GL_QUADS, 0, stitch_preview->num_quads*4); + glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); + /*UI_ThemeColor4(TH_STITCH_PREVIEW_VERT); + glDrawArrays(GL_QUADS, 0, stitch_preview->num_quads*4);*/ + + glDisable(GL_BLEND); + + /* draw vert preview */ + glPointSize(pointsize*2.0); + UI_ThemeColor4(TH_STITCH_PREVIEW_STITCHABLE); + glVertexPointer(2, GL_FLOAT, 0, stitch_preview->preview_stitchable); + glDrawArrays(GL_POINTS, 0, stitch_preview->num_stitchable); + + UI_ThemeColor4(TH_STITCH_PREVIEW_UNSTITCHABLE); + glVertexPointer(2, GL_FLOAT, 0, stitch_preview->preview_unstitchable); + glDrawArrays(GL_POINTS, 0, stitch_preview->num_unstitchable); + + glPopClientAttrib(); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } + glPointSize(1.0); BKE_mesh_end_editmesh(obedit->data, em); } -void draw_uvedit_main(SpaceImage *sima, ARegion *ar, Scene *scene, Object *obedit) +void draw_uvedit_main(SpaceImage *sima, ARegion *ar, Scene *scene, Object *obedit, Object *obact) { - int show_uvedit, show_uvshadow; + ToolSettings *toolsettings = scene->toolsettings; + int show_uvedit, show_uvshadow, show_texpaint_uvshadow; + show_texpaint_uvshadow = (obact && obact->type == OB_MESH && obact->mode == OB_MODE_TEXTURE_PAINT); show_uvedit= ED_space_image_show_uvedit(sima, obedit); show_uvshadow= ED_space_image_show_uvshadow(sima, obedit); - if(show_uvedit || show_uvshadow) { + if(show_uvedit || show_uvshadow || show_texpaint_uvshadow) { if(show_uvshadow) draw_uvs_shadow(obedit); - else + else if(show_uvedit) draw_uvs(sima, scene, obedit); + else + draw_uvs_texpaint(sima, scene, obact); - if(show_uvedit) + if(show_uvedit && !(toolsettings->use_uv_sculpt)) drawcursor_sima(sima, ar); } } diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h index 7d83f529d92..ef25159a3af 100644 --- a/source/blender/editors/uvedit/uvedit_intern.h +++ b/source/blender/editors/uvedit/uvedit_intern.h @@ -32,25 +32,74 @@ #ifndef ED_UVEDIT_INTERN_H #define ED_UVEDIT_INTERN_H -struct SpaceImage; struct EditFace; -struct MTFace; -struct Scene; +struct EditMesh; struct Image; +struct MTFace; struct Object; +struct Scene; +struct SpaceImage; +struct UvElementMap; struct wmOperatorType; /* id can be from 0 to 3 */ #define TF_PIN_MASK(id) (TF_PIN1 << id) #define TF_SEL_MASK(id) (TF_SEL1 << id) - /* geometric utilities */ + void uv_center(float uv[][2], float cent[2], int quad); float uv_area(float uv[][2], int quad); void uv_copy_aspect(float uv_orig[][2], float uv[][2], float aspx, float aspy); +/* find nearest */ + +typedef struct NearestHit { + struct EditFace *efa; + struct MTFace *tf; + + int vert, uv; + int edge, vert2; +} NearestHit; + +void uv_find_nearest_vert(struct Scene *scene, struct Image *ima, struct EditMesh *em, float co[2], float penalty[2], struct NearestHit *hit); +void uv_find_nearest_edge(struct Scene *scene, struct Image *ima, struct EditMesh *em, float co[2], struct NearestHit *hit); + +/* utility tool functions */ + +struct UvElement *ED_get_uv_element(struct UvElementMap *map, struct EditFace *efa, int index); +void uvedit_live_unwrap_update(struct SpaceImage *sima, struct Scene *scene, struct Object *obedit); + +/* smart stitch */ + +/* object that stores display data for previewing before accepting stitching */ +typedef struct StitchPreviewer { + /* OpenGL requires different calls for Triangles and Quads. + * here we'll store the quads of the mesh */ + float *preview_quads; + /* ...and here we'll store the triangles*/ + float *preview_tris; + /* preview data. These will be either the previewed vertices or edges depending on stitch mode settings */ + float *preview_stitchable; + float *preview_unstitchable; + /* here we'll store the number of triangles and quads to be drawn */ + unsigned int num_tris; + unsigned int num_quads; + unsigned int num_stitchable; + unsigned int num_unstitchable; + + /* store static island Quads */ + float *static_quads; + /* ...and here we'll store the triangles*/ + float *static_tris; + unsigned int num_static_tris; + unsigned int num_static_quads; +} StitchPreviewer; + +StitchPreviewer *uv_get_stitch_previewer(void); + /* operators */ + void UV_OT_average_islands_scale(struct wmOperatorType *ot); void UV_OT_cube_project(struct wmOperatorType *ot); void UV_OT_cylinder_project(struct wmOperatorType *ot); @@ -60,6 +109,7 @@ void UV_OT_pack_islands(struct wmOperatorType *ot); void UV_OT_reset(struct wmOperatorType *ot); void UV_OT_sphere_project(struct wmOperatorType *ot); void UV_OT_unwrap(struct wmOperatorType *ot); +void UV_OT_stitch(struct wmOperatorType *ot); #endif /* ED_UVEDIT_INTERN_H */ diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 98fef075940..612b46746b8 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -20,7 +20,7 @@ * * The Original Code is: all of this file. * - * Contributor(s): none yet. + * Contributor(s): Antony Riakiotakis. * * ***** END GPL LICENSE BLOCK ***** */ @@ -94,6 +94,28 @@ int ED_uvedit_test(Object *obedit) 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 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) @@ -400,7 +422,7 @@ void uvedit_uv_deselect(Scene *scene, EditFace *efa, MTFace *tf, int i) /*********************** live unwrap utilities ***********************/ -static void uvedit_live_unwrap_update(SpaceImage *sima, Scene *scene, Object *obedit) +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); @@ -527,15 +549,7 @@ static int uvedit_center(Scene *scene, Image *ima, Object *obedit, float *cent, /************************** 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) +void uv_find_nearest_edge(Scene *scene, Image *ima, EditMesh *em, float co[2], NearestHit *hit) { MTFace *tf; EditFace *efa; @@ -633,7 +647,7 @@ static int nearest_uv_between(MTFace *tf, int nverts, int id, float co[2], float 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) +void uv_find_nearest_vert(Scene *scene, Image *ima, EditMesh *em, float co[2], float penalty[2], NearestHit *hit) { EditFace *efa; EditVert *eve; @@ -750,6 +764,17 @@ static UvMapVert *uv_vertex_map_get(UvVertMap *vmap, EditFace *efa, int a) return NULL; } +UvElement *ED_get_uv_element(UvElementMap *map, EditFace *efa, int index) +{ + UvElement *element = map->vert[(*(&efa->v1 + index))->tmp.l]; + + for(; element; element = element->next) + if(element->face == efa) + return element; + + return NULL; +} + static int uv_edge_tag_faces(UvMapVert *first1, UvMapVert *first2, int *totface) { UvMapVert *iterv1, *iterv2; @@ -1296,197 +1321,6 @@ static void UV_OT_weld(wmOperatorType *ot) ot->poll= ED_operator_uvedit; } -/* ******************** stitch operator **************** */ - -/* just for averaging UVs */ -typedef struct UVVertAverage { - float uv[2]; - int count; -} UVVertAverage; - -static int stitch_exec(bContext *C, wmOperator *op) -{ - SpaceImage *sima; - Scene *scene; - Object *obedit; - EditMesh *em; - EditFace *efa; - EditVert *eve; - Image *ima; - MTFace *tf; - - scene= CTX_data_scene(C); - obedit= CTX_data_edit_object(C); - em= BKE_mesh_get_editmesh((Mesh*)obedit->data); - ima= CTX_data_edit_image(C); - sima= CTX_wm_space_image(C); - - if(RNA_boolean_get(op->ptr, "use_limit")) { - UvVertMap *vmap; - UvMapVert *vlist, *iterv; - float newuv[2], limit[2]; - int a, vtot; - - limit[0]= RNA_float_get(op->ptr, "limit"); - limit[1]= limit[0]; - - EM_init_index_arrays(em, 0, 0, 1); - vmap= EM_make_uv_vert_map(em, 1, 0, limit); - - if(vmap == NULL) { - BKE_mesh_end_editmesh(obedit->data, em); - return OPERATOR_CANCELLED; - } - - for(a=0, eve= em->verts.first; eve; a++, eve= eve->next) { - vlist= EM_get_uv_map_vert(vmap, a); - - while(vlist) { - newuv[0]= 0; newuv[1]= 0; - vtot= 0; - - for(iterv=vlist; iterv; iterv=iterv->next) { - if((iterv != vlist) && iterv->separate) - break; - - efa = EM_get_face_for_index(iterv->f); - tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); - - if(uvedit_uv_selected(scene, efa, tf, iterv->tfindex)) { - newuv[0] += tf->uv[iterv->tfindex][0]; - newuv[1] += tf->uv[iterv->tfindex][1]; - vtot++; - } - } - - if(vtot > 1) { - newuv[0] /= vtot; newuv[1] /= vtot; - - for(iterv=vlist; iterv; iterv=iterv->next) { - if((iterv != vlist) && iterv->separate) - break; - - efa = EM_get_face_for_index(iterv->f); - tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); - - if(uvedit_uv_selected(scene, efa, tf, iterv->tfindex)) { - tf->uv[iterv->tfindex][0]= newuv[0]; - tf->uv[iterv->tfindex][1]= newuv[1]; - } - } - } - - vlist= iterv; - } - } - - EM_free_uv_vert_map(vmap); - EM_free_index_arrays(); - } - else { - UVVertAverage *uv_average, *uvav; - int count; - - // index and count verts - for(count=0, eve=em->verts.first; eve; count++, eve= eve->next) - eve->tmp.l = count; - - uv_average= MEM_callocN(sizeof(UVVertAverage)*count, "Stitch"); - - // gather uv averages per vert - 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)) { - uvav = uv_average + efa->v1->tmp.l; - uvav->count++; - uvav->uv[0] += tf->uv[0][0]; - uvav->uv[1] += tf->uv[0][1]; - } - - if(uvedit_uv_selected(scene, efa, tf, 1)) { - uvav = uv_average + efa->v2->tmp.l; - uvav->count++; - uvav->uv[0] += tf->uv[1][0]; - uvav->uv[1] += tf->uv[1][1]; - } - - if(uvedit_uv_selected(scene, efa, tf, 2)) { - uvav = uv_average + efa->v3->tmp.l; - uvav->count++; - uvav->uv[0] += tf->uv[2][0]; - uvav->uv[1] += tf->uv[2][1]; - } - - if(efa->v4 && uvedit_uv_selected(scene, efa, tf, 3)) { - uvav = uv_average + efa->v4->tmp.l; - uvav->count++; - uvav->uv[0] += tf->uv[3][0]; - uvav->uv[1] += tf->uv[3][1]; - } - } - } - - // apply uv welding - 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)) { - uvav = uv_average + efa->v1->tmp.l; - tf->uv[0][0] = uvav->uv[0]/uvav->count; - tf->uv[0][1] = uvav->uv[1]/uvav->count; - } - - if(uvedit_uv_selected(scene, efa, tf, 1)) { - uvav = uv_average + efa->v2->tmp.l; - tf->uv[1][0] = uvav->uv[0]/uvav->count; - tf->uv[1][1] = uvav->uv[1]/uvav->count; - } - - if(uvedit_uv_selected(scene, efa, tf, 2)) { - uvav = uv_average + efa->v3->tmp.l; - tf->uv[2][0] = uvav->uv[0]/uvav->count; - tf->uv[2][1] = uvav->uv[1]/uvav->count; - } - - if(efa->v4 && uvedit_uv_selected(scene, efa, tf, 3)) { - uvav = uv_average + efa->v4->tmp.l; - tf->uv[3][0] = uvav->uv[0]/uvav->count; - tf->uv[3][1] = uvav->uv[1]/uvav->count; - } - } - } - - MEM_freeN(uv_average); - } - - uvedit_live_unwrap_update(sima, scene, obedit); - DAG_id_tag_update(obedit->data, 0); - WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); - - BKE_mesh_end_editmesh(obedit->data, em); - return OPERATOR_FINISHED; -} - -static void UV_OT_stitch(wmOperatorType *ot) -{ - /* identifiers */ - ot->name= "Stitch"; - ot->description= "Stitch selected UV vertices by proximity"; - ot->idname= "UV_OT_stitch"; - ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; - - /* api callbacks */ - ot->exec= stitch_exec; - ot->poll= ED_operator_uvedit; - - /* properties */ - RNA_def_boolean(ot->srna, "use_limit", 1, "Use Limit", "Stitch UVs within a specified limit distance"); - RNA_def_float(ot->srna, "limit", 0.01f, 0.0f, FLT_MAX, "Limit", "Limit distance in normalized coordinates", -FLT_MAX, FLT_MAX); -} - /* ******************** (de)select all operator **************** */ static void select_all_perform(bContext *C, int action) @@ -1660,7 +1494,7 @@ static int mouse_select(bContext *C, float co[2], int extend, int loop) /* find nearest element */ if(loop) { /* find edge */ - find_nearest_uv_edge(scene, ima, em, co, &hit); + uv_find_nearest_edge(scene, ima, em, co, &hit); if(hit.efa == NULL) { BKE_mesh_end_editmesh(obedit->data, em); return OPERATOR_CANCELLED; @@ -1668,7 +1502,7 @@ static int mouse_select(bContext *C, float co[2], int extend, int loop) } else if(selectmode == UV_SELECT_VERTEX) { /* find vertex */ - find_nearest_uv_vert(scene, ima, em, co, penalty, &hit); + uv_find_nearest_vert(scene, ima, em, co, penalty, &hit); if(hit.efa == NULL) { BKE_mesh_end_editmesh(obedit->data, em); return OPERATOR_CANCELLED; @@ -1683,7 +1517,7 @@ static int mouse_select(bContext *C, float co[2], int extend, int loop) } else if(selectmode == UV_SELECT_EDGE) { /* find edge */ - find_nearest_uv_edge(scene, ima, em, co, &hit); + uv_find_nearest_edge(scene, ima, em, co, &hit); if(hit.efa == NULL) { BKE_mesh_end_editmesh(obedit->data, em); return OPERATOR_CANCELLED; @@ -1723,7 +1557,7 @@ static int mouse_select(bContext *C, float co[2], int extend, int loop) else hitv[3]= 0xFFFFFFFF; } else if(selectmode == UV_SELECT_ISLAND) { - find_nearest_uv_vert(scene, ima, em, co, NULL, &hit); + uv_find_nearest_vert(scene, ima, em, co, NULL, &hit); if(hit.efa==NULL) { BKE_mesh_end_editmesh(obedit->data, em); @@ -2015,7 +1849,7 @@ static int select_linked_internal(bContext *C, wmOperator *op, wmEvent *event, i RNA_float_get_array(op->ptr, "location", co); } - find_nearest_uv_vert(scene, ima, em, co, NULL, &hit); + uv_find_nearest_vert(scene, ima, em, co, NULL, &hit); hit_p= &hit; } @@ -3312,6 +3146,180 @@ static void UV_OT_tile_set(wmOperatorType *ot) RNA_def_int_vector(ot->srna, "tile", 2, NULL, 0, INT_MAX, "Tile", "Tile coordinate", 0, 10); } + +static int seams_from_islands_exec(bContext *C, wmOperator *op) +{ + UvVertMap *vmap; + Object *ob = CTX_data_edit_object(C); + Mesh *me= (Mesh*)ob->data; + EditMesh *em; + EditEdge *editedge; + float limit[2] = {STD_UV_CONNECT_LIMIT, STD_UV_CONNECT_LIMIT}; + char mark_seams = RNA_boolean_get(op->ptr, "mark_seams"); + char mark_sharp = RNA_boolean_get(op->ptr, "mark_sharp"); + + em = BKE_mesh_get_editmesh(me); + + if(!EM_texFaceCheck(em)) { + BKE_mesh_end_editmesh(ob->data, em); + return OPERATOR_CANCELLED; + } + + /* This code sets editvert->tmp.l to the index. This will be useful later on. */ + EM_init_index_arrays(em, 0, 0, 1); + vmap = EM_make_uv_vert_map(em, 0, 0, limit); + + for(editedge = em->edges.first; editedge; editedge = editedge->next) { + /* flags to determine if we uv is separated from first editface match */ + char separated1 = 0, separated2; + /* set to denote edge must be flagged as seam */ + char faces_separated = 0; + /* flag to keep track if uv1 is disconnected from first editface match */ + char v1coincident = 1; + /* For use with v1coincident. v1coincident will change only if we've had commonFaces */ + int commonFaces = 0; + + EditFace *efa1, *efa2; + + UvMapVert *mv1, *mvinit1, *mv2, *mvinit2, *mviter; + /* mv2cache stores the first of the list of coincident uv's for later comparison + * mv2sep holds the last separator and is copied to mv2cache when a hit is first found */ + UvMapVert *mv2cache = NULL, *mv2sep = NULL; + + mvinit1 = vmap->vert[editedge->v1->tmp.l]; + if(mark_seams) + editedge->seam = 0; + + for(mv1 = mvinit1; mv1 && !faces_separated; mv1 = mv1->next) { + if(mv1->separate && commonFaces) + v1coincident = 0; + + separated2 = 0; + efa1 = EM_get_face_for_index(mv1->f); + mvinit2 = vmap->vert[editedge->v2->tmp.l]; + + for(mv2 = mvinit2; mv2; mv2 = mv2->next) { + if(mv2->separate) + mv2sep = mv2; + + efa2 = EM_get_face_for_index(mv2->f); + if(efa1 == efa2) { + /* if v1 is not coincident no point in comparing */ + if(v1coincident) { + /* have we found previously anything? */ + if(mv2cache) { + /* flag seam unless proved to be coincident with previous hit */ + separated2 = 1; + for(mviter = mv2cache; mviter; mviter = mviter->next) { + if(mviter->separate && mviter != mv2cache) + break; + /* coincident with previous hit, do not flag seam */ + if(mviter == mv2) + separated2 = 0; + } + } + /* First hit case, store the hit in the cache */ + else { + mv2cache = mv2sep; + commonFaces = 1; + } + } + else + separated1 = 1; + + if(separated1 || separated2) { + faces_separated = 1; + break; + } + } + } + } + + if(faces_separated) { + if(mark_seams) + editedge->seam = 1; + if(mark_sharp) + editedge->sharp = 1; + } + } + + me->drawflag |= ME_DRAWSEAMS; + + EM_free_uv_vert_map(vmap); + EM_free_index_arrays(); + BKE_mesh_end_editmesh(me, em); + + DAG_id_tag_update(&me->id, 0); + WM_event_add_notifier(C, NC_GEOM|ND_DATA, me); + + return OPERATOR_FINISHED; +} + + +static void UV_OT_seams_from_islands(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Seams From Islands"; + ot->description= "Set mesh seams according to island setup in the UV editor"; + ot->idname= "UV_OT_seams_from_islands"; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* api callbacks */ + ot->exec= seams_from_islands_exec; + ot->poll= ED_operator_uvedit; + + RNA_def_boolean(ot->srna, "mark_seams", 1, "Mark Seams", "Mark boundary edges as seams"); + RNA_def_boolean(ot->srna, "mark_sharp", 0, "Mark Sharp", "Mark boundary edges as sharp"); +} + +static int mark_seam_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = CTX_data_edit_object(C); + Scene *scene = CTX_data_scene(C); + Mesh *me= (Mesh*)ob->data; + EditMesh *em= BKE_mesh_get_editmesh(me); + EditFace *efa; + + for(efa = em->faces.first; efa; efa = efa->next) { + MTFace *mt = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + int i, nverts = efa->v4? 4 : 3; + + for(i = 0; i < nverts; i++) + if(uvedit_edge_selected(scene, efa, mt, i)) + (*(&efa->e1 + i))->seam = 1; + } + + me->drawflag |= ME_DRAWSEAMS; + + if(scene->toolsettings->edge_mode_live_unwrap) + ED_unwrap_lscm(scene, ob, FALSE); + + BKE_mesh_end_editmesh(me, em); + + DAG_id_tag_update(&me->id, 0); + WM_event_add_notifier(C, NC_GEOM|ND_DATA, me); + + return OPERATOR_FINISHED; +} + +static void UV_OT_mark_seam(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Mark Seams"; + ot->description= "Mark selected UV edges as seams"; + ot->idname= "UV_OT_mark_seam"; + + /* flags */ + ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; + + /* api callbacks */ + ot->exec= mark_seam_exec; + ot->poll= ED_operator_uvedit; +} + + /* ************************** registration **********************************/ void ED_operatortypes_uvedit(void) @@ -3331,6 +3339,8 @@ void ED_operatortypes_uvedit(void) WM_operatortype_append(UV_OT_align); WM_operatortype_append(UV_OT_stitch); + WM_operatortype_append(UV_OT_seams_from_islands); + WM_operatortype_append(UV_OT_mark_seam); WM_operatortype_append(UV_OT_weld); WM_operatortype_append(UV_OT_pin); @@ -3357,7 +3367,14 @@ void ED_keymap_uvedit(wmKeyConfig *keyconf) wmKeyMapItem *kmi; keymap= WM_keymap_find(keyconf, "UV Editor", 0, 0); - keymap->poll= ED_operator_uvedit; + keymap->poll= ED_operator_uvedit_can_uv_sculpt; + + /* Uv sculpt toggle */ + kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", QKEY, KM_PRESS, 0, 0); + RNA_string_set(kmi->ptr, "data_path", "tool_settings.use_uv_sculpt"); + + /* Mark edge seam */ + WM_keymap_add_item(keymap, "UV_OT_mark_seam", EKEY, KM_PRESS, KM_CTRL, 0); /* pick selection */ RNA_boolean_set(WM_keymap_add_item(keymap, "UV_OT_select", SELECTMOUSE, KM_PRESS, 0, 0)->ptr, "extend", FALSE); diff --git a/source/blender/editors/uvedit/uvedit_parametrizer.c b/source/blender/editors/uvedit/uvedit_parametrizer.c index 7ad573231c9..d651a17b624 100644 --- a/source/blender/editors/uvedit/uvedit_parametrizer.c +++ b/source/blender/editors/uvedit/uvedit_parametrizer.c @@ -91,7 +91,7 @@ typedef struct PVert { } u; struct PEdge *edge; - float *co; + float co[3]; float uv[2]; unsigned char flag; @@ -655,11 +655,15 @@ static void p_face_backup_uvs(PFace *f) { PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next; - if (e1->orig_uv && e2->orig_uv && e3->orig_uv) { + if (e1->orig_uv) { e1->old_uv[0] = e1->orig_uv[0]; e1->old_uv[1] = e1->orig_uv[1]; + } + if (e2->orig_uv) { e2->old_uv[0] = e2->orig_uv[0]; e2->old_uv[1] = e2->orig_uv[1]; + } + if (e3->orig_uv) { e3->old_uv[0] = e3->orig_uv[0]; e3->old_uv[1] = e3->orig_uv[1]; } @@ -669,11 +673,15 @@ static void p_face_restore_uvs(PFace *f) { PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next; - if (e1->orig_uv && e2->orig_uv && e3->orig_uv) { + if (e1->orig_uv) { e1->orig_uv[0] = e1->old_uv[0]; e1->orig_uv[1] = e1->old_uv[1]; + } + if (e2->orig_uv) { e2->orig_uv[0] = e2->old_uv[0]; e2->orig_uv[1] = e2->old_uv[1]; + } + if (e3->orig_uv) { e3->orig_uv[0] = e3->old_uv[0]; e3->orig_uv[1] = e3->old_uv[1]; } @@ -684,7 +692,7 @@ static void p_face_restore_uvs(PFace *f) static PVert *p_vert_add(PHandle *handle, PHashKey key, float *co, PEdge *e) { PVert *v = (PVert*)BLI_memarena_alloc(handle->arena, sizeof *v); - v->co = co; + copy_v3_v3(v->co, co); v->u.key = key; v->edge = e; v->flag = 0; @@ -708,7 +716,7 @@ static PVert *p_vert_copy(PChart *chart, PVert *v) { PVert *nv = (PVert*)BLI_memarena_alloc(chart->handle->arena, sizeof *nv); - nv->co = v->co; + copy_v3_v3(nv->co, v->co); nv->uv[0] = v->uv[0]; nv->uv[1] = v->uv[1]; nv->u.key = v->u.key; diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c new file mode 100644 index 00000000000..6a9dda3e1f8 --- /dev/null +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -0,0 +1,1442 @@ +/* + * ***** 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_stitch.c + * \ingroup eduv + */ + + +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_object_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_scene_types.h" + +#include "BLI_editVert.h" +#include "BLI_ghash.h" +#include "BLI_math.h" +#include "BLI_math_vector.h" +#include "BLI_string.h" + +#include "BKE_context.h" +#include "BKE_customdata.h" +#include "BKE_depsgraph.h" +#include "BKE_mesh.h" + +#include "ED_mesh.h" +#include "ED_uvedit.h" +#include "ED_screen.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" + +/* ********************** smart stitch operator *********************** */ + + +struct IslandStitchData; + +/* This is a straightforward implementation, count the uv's in the island that will move and take the mean displacement/rotation and apply it to all + * elements of the island except from the stitchable */ +typedef struct IslandStitchData{ + /* rotation can be used only for edges, for vertices there is no such notion */ + float rotation; + float translation[2]; + /* Used for rotation, the island will rotate around this point */ + float medianPoint[2]; + int numOfElements; + int num_rot_elements; + /* Flag to remember if island has been added for preview */ + char addedForPreview; + /* Flag an island to be considered for determining static island */ + char stitchableCandidate; +}IslandStitchData; + +/* just for averaging UVs */ +typedef struct UVVertAverage { + float uv[2]; + unsigned short count; +} UVVertAverage; + +typedef struct UvEdge { + /* index to uv buffer */ + unsigned int uv1; + unsigned int uv2; + /* general use flag (Used to check if edge is boundary here, and propagates to adjacency elements) */ + char flag; + /* element that guarantees element->face has the face on element->tfindex and element->tfindex+1 is the second uv */ + UvElement *element; +}UvEdge; + + +/* stitch state object */ +typedef struct StitchState { + /* use limit flag */ + char use_limit; + /* limit to operator, same as original operator */ + float limit_dist; + /* snap uv islands together during stitching */ + char snap_islands; + /* stich at midpoints or at islands */ + char midpoints; + /* editmesh, cached for use in modal handler */ + EditMesh *em; + /* element map for getting info about uv connectivity */ + UvElementMap *element_map; + /* edge container */ + UvEdge *uvedges; + /* container of first of a group of coincident uvs, these will be operated upon */ + UvElement **uvs; + /* maps uvelements to their first coincident uv */ + int *map; + /* 2D normals per uv to calculate rotation for snapping */ + float *normals; + /* edge storage */ + UvEdge *edges; + + /* count of separate uvs and edges */ + int total_boundary_edges; + int total_separate_uvs; + /* hold selection related information */ + UvElement **selection_stack; + int selection_size; + /* island that stays in place */ + int static_island; + /* store number of primitives per face so that we can allocate the active island buffer later */ + unsigned int *quads_per_island; + unsigned int *tris_per_island; +} StitchState; + + +/* + * defines for UvElement flags + */ +#define STITCH_SELECTED 1 +#define STITCH_STITCHABLE 2 +#define STITCH_PROCESSED 4 +#define STITCH_BOUNDARY 8 +#define STITCH_STITCHABLE_CANDIDATE 16 + +#define STITCH_NO_PREVIEW -1 + +/* previewer stuff (see uvedit_intern.h for more info) */ +static StitchPreviewer *_stitch_preview; + +/* constructor */ +static StitchPreviewer * stitch_preview_init(void) +{ + _stitch_preview = MEM_mallocN(sizeof(StitchPreviewer), "stitch_previewer"); + _stitch_preview->preview_quads = NULL; + _stitch_preview->preview_tris = NULL; + _stitch_preview->preview_stitchable = NULL; + _stitch_preview->preview_unstitchable = NULL; + + _stitch_preview->num_quads = 0; + _stitch_preview->num_tris = 0; + _stitch_preview->num_stitchable = 0; + _stitch_preview->num_unstitchable = 0; + + _stitch_preview->static_quads = NULL; + _stitch_preview->static_tris = NULL; + + _stitch_preview->num_static_tris = 0; + _stitch_preview->num_static_quads = 0; + + return _stitch_preview; +} + +/* destructor...yeah this should be C++ :) */ +static void stitch_preview_delete(void) +{ + if(_stitch_preview) + { + if(_stitch_preview->preview_quads){ + MEM_freeN(_stitch_preview->preview_quads); + _stitch_preview->preview_quads = NULL; + } + if(_stitch_preview->preview_tris){ + MEM_freeN(_stitch_preview->preview_tris); + _stitch_preview->preview_tris = NULL; + } + if(_stitch_preview->preview_stitchable){ + MEM_freeN(_stitch_preview->preview_stitchable); + _stitch_preview->preview_stitchable = NULL; + } + if(_stitch_preview->preview_unstitchable){ + MEM_freeN(_stitch_preview->preview_unstitchable); + _stitch_preview->preview_unstitchable = NULL; + } + if(_stitch_preview->static_quads){ + MEM_freeN(_stitch_preview->static_quads); + _stitch_preview->static_quads = NULL; + } + if(_stitch_preview->static_tris){ + MEM_freeN(_stitch_preview->static_tris); + _stitch_preview->static_tris = NULL; + } + MEM_freeN(_stitch_preview); + _stitch_preview = NULL; + } +} + + +/* "getter method" */ +StitchPreviewer *uv_get_stitch_previewer(void) +{ + return _stitch_preview; +} + +#define HEADER_LENGTH 256 + +/* This function updates the header of the UV editor when the stitch tool updates its settings */ +static void stitch_update_header(StitchState *stitch_state, bContext *C) +{ + static char str[] = "(S)nap %s, (M)idpoints %s, (L)imit %.2f (Alt Wheel adjust) %s, Switch (I)sland, shift select vertices"; + + char msg[HEADER_LENGTH]; + ScrArea *sa= CTX_wm_area(C); + + if(sa) { + BLI_snprintf(msg, HEADER_LENGTH, str, + stitch_state->snap_islands? "On" : "Off", + stitch_state->midpoints? "On": "Off", + stitch_state->limit_dist, + stitch_state->use_limit? "On" : "Off"); + + ED_area_headerprint(sa, msg); + } +} + +static int getNumOfIslandUvs(UvElementMap *elementMap, int island){ + if(island == elementMap->totalIslands-1){ + return elementMap->totalUVs - elementMap->islandIndices[island]; + }else{ + return elementMap->islandIndices[island+1] - elementMap->islandIndices[island]; + } +} + +static void stitch_uv_rotate(float rotation, float medianPoint[2], float uv[2]){ + float uv_rotation_result[2]; + + uv[0] -= medianPoint[0]; + uv[1] -= medianPoint[1]; + + uv_rotation_result[0] = cos(rotation)*uv[0] - sin(rotation)*uv[1]; + uv_rotation_result[1] = sin(rotation)*uv[0] + cos(rotation)*uv[1]; + + uv[0] = uv_rotation_result[0] + medianPoint[0]; + uv[1] = uv_rotation_result[1] + medianPoint[1]; +} + + +/* calculate snapping for islands */ +static void stitch_calculate_island_snapping(StitchState *state, StitchPreviewer *preview, IslandStitchData *island_stitch_data, int final){ + int i; + EditFace *efa; + MTFace *mt; + UvElement *element; + + for(i = 0; i < state->element_map->totalIslands; i++){ + if(island_stitch_data[i].addedForPreview){ + int numOfIslandUVs = 0, j; + + /* check to avoid divide by 0 */ + if(island_stitch_data[i].num_rot_elements>0){ + island_stitch_data[i].rotation /= island_stitch_data[i].num_rot_elements; + island_stitch_data[i].medianPoint[0] /= island_stitch_data[i].numOfElements; + island_stitch_data[i].medianPoint[1] /= island_stitch_data[i].numOfElements; + } + island_stitch_data[i].translation[0] /= island_stitch_data[i].numOfElements; + island_stitch_data[i].translation[1] /= island_stitch_data[i].numOfElements; + numOfIslandUVs = getNumOfIslandUvs(state->element_map, i); + element = &state->element_map->buf[state->element_map->islandIndices[i]]; + for(j = 0; j < numOfIslandUVs; j++, element++){ + /* stitchable uvs have already been processed, don't process */ + if(!(element->flag & STITCH_PROCESSED)){ + efa = element->face; + mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE); + if(final){ + + stitch_uv_rotate(island_stitch_data[i].rotation, island_stitch_data[i].medianPoint, mt->uv[element->tfindex]); + + mt->uv[element->tfindex][0] += island_stitch_data[i].translation[0]; + mt->uv[element->tfindex][1] += island_stitch_data[i].translation[1]; + } + else if(efa->tmp.l != STITCH_NO_PREVIEW){ + if(efa->v4){ + + stitch_uv_rotate(island_stitch_data[i].rotation, island_stitch_data[i].medianPoint, &preview->preview_quads[efa->tmp.l + 2*element->tfindex]); + + preview->preview_quads[efa->tmp.l + 2*element->tfindex] += island_stitch_data[i].translation[0]; + preview->preview_quads[efa->tmp.l + 2*element->tfindex + 1] += island_stitch_data[i].translation[1]; + } + else { + + stitch_uv_rotate(island_stitch_data[i].rotation, island_stitch_data[i].medianPoint, &preview->preview_tris[efa->tmp.l + 2*element->tfindex]); + + preview->preview_tris[efa->tmp.l + 2*element->tfindex] += island_stitch_data[i].translation[0]; + preview->preview_tris[efa->tmp.l + 2*element->tfindex + 1] += island_stitch_data[i].translation[1]; + } + } + } + /* cleanup */ + element->flag &= STITCH_SELECTED; + } + } + } +} + + + +static void stitch_island_calculate_edge_rotation(UvEdge *edge, StitchState *state, UVVertAverage *uv_average, unsigned int *uvfinal_map, IslandStitchData *island_stitch_data) +{ + UvElement *element; + EditFace *efa; + MTFace *mt; + int nverts; + float uv1[2], uv2[2]; + float edgecos, edgesin; + int index1, index2; + + element = edge->element; + efa = element->face; + nverts = (efa->v4)? 4 : 3; + mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE); + + index1 = uvfinal_map[(*(&element->face->v1 + element->tfindex))->tmp.l]; + index2 = uvfinal_map[(*(&element->face->v1 + (element->tfindex + 1)%nverts))->tmp.l]; + + /* the idea here is to take the directions of the edges and find the rotation between final and initial + * direction. This, using inner and outer vector products, gives the angle. Directions are differences so... */ + uv1[0] = mt->uv[(element->tfindex + 1)%nverts][0] - mt->uv[element->tfindex][0]; + uv1[1] = mt->uv[(element->tfindex + 1)%nverts][1] - mt->uv[element->tfindex][1]; + + uv2[0] = uv_average[index2].uv[0] - uv_average[index1].uv[0]; + uv2[1] = uv_average[index2].uv[1] - uv_average[index1].uv[1]; + + normalize_v2(uv1); + normalize_v2(uv2); + + edgecos = uv1[0]*uv2[0] + uv1[1]*uv2[1]; + edgesin = uv1[0]*uv2[1] - uv2[0]*uv1[1]; + island_stitch_data[element->island].num_rot_elements++; + island_stitch_data[element->island].rotation += (edgesin > 0)? acos(MAX2(-1.0, MIN2(1.0, edgecos))): -acos(MAX2(-1.0, MIN2(1.0, edgecos))); +} + +static void stitch_island_calculate_vert_rotation(UvElement *element, StitchState *state, IslandStitchData *island_stitch_data, char do_static) +{ + float edgecos = 1, edgesin = 0; + int index; + UvElement *element_iter; + + if((element->island == state->static_island) && !do_static) + return; + + index = (*(&element->face->v1 + element->tfindex))->tmp.l; + + element_iter = state->element_map->vert[index]; + + if(!do_static){ + for(; element_iter; element_iter = element_iter->next){ + if((element_iter->separate) && (element_iter->flag & STITCH_STITCHABLE) && + (element_iter != element) && (element_iter->island == state->static_island) + ){ + int index_tmp1, index_tmp2; + float normal[2]; + /* easily possible*/ + + index_tmp1 = element_iter - state->element_map->buf; + index_tmp1 = state->map[index_tmp1]; + index_tmp2 = element - state->element_map->buf; + index_tmp2 = state->map[index_tmp2]; + + negate_v2_v2(normal, state->normals + index_tmp2*2); + edgecos = dot_v2v2(normal, state->normals + index_tmp1*2); + edgesin = cross_v2v2(normal, state->normals + index_tmp1*2); + break; + } + } + } + + island_stitch_data[element->island].num_rot_elements++; + island_stitch_data[element->island].rotation += (edgesin > 0)? acos(edgecos): -acos(edgecos); +} + + +static void stitch_state_delete(StitchState *stitch_state) +{ + if(stitch_state){ + if(stitch_state->element_map){ + EM_free_uv_element_map(stitch_state->element_map); + } + if(stitch_state->uvs){ + MEM_freeN(stitch_state->uvs); + } + if(stitch_state->selection_stack){ + MEM_freeN(stitch_state->selection_stack); + } + if(stitch_state->quads_per_island){ + MEM_freeN(stitch_state->quads_per_island); + } + if(stitch_state->tris_per_island){ + MEM_freeN(stitch_state->tris_per_island); + } + if(stitch_state->map){ + MEM_freeN(stitch_state->map); + } + if(stitch_state->normals){ + MEM_freeN(stitch_state->normals); + } + if(stitch_state->edges){ + MEM_freeN(stitch_state->edges); + } + MEM_freeN(stitch_state); + } +} + + + +/* checks for remote uvs that may be stitched with a certain uv, flags them if stitchable. */ +static void determine_uv_stitchability(UvElement *element, StitchState *state, IslandStitchData *island_stitch_data){ + int vert_index; + UvElement *element_iter; + float limit= state->limit_dist; + int do_limit = state->use_limit; + + vert_index = (*(&element->face->v1 + element->tfindex))->tmp.l; + element_iter = state->element_map->vert[vert_index]; + + for(; element_iter; element_iter = element_iter->next){ + if(element_iter->separate){ + if(element_iter == element){ + continue; + } + if(do_limit){ + MTFace *mtface_orig = CustomData_em_get(&state->em->fdata, element->face->data, CD_MTFACE); + MTFace *mtface_iter = CustomData_em_get(&state->em->fdata, element_iter->face->data, CD_MTFACE); + + if(fabs(mtface_orig->uv[element->tfindex][0] - mtface_iter->uv[element_iter->tfindex][0]) < limit + && fabs(mtface_orig->uv[element->tfindex][1] - mtface_iter->uv[element_iter->tfindex][1]) < limit){ + island_stitch_data[element_iter->island].stitchableCandidate = 1; + island_stitch_data[element->island].stitchableCandidate = 1; + element->flag |= STITCH_STITCHABLE_CANDIDATE; + } + }else{ + /* if no limit exists, then the mere existence of a separate uv means that the uv is stitchable */ + island_stitch_data[element_iter->island].stitchableCandidate = 1; + island_stitch_data[element->island].stitchableCandidate = 1; + element->flag |= STITCH_STITCHABLE_CANDIDATE; + } + } + } +} + + + +/* set preview buffer position of UV face in editface->tmp.l */ +static void stitch_set_face_preview_buffer_position(EditFace *efa, StitchPreviewer *preview) +{ + if(efa->tmp.l == STITCH_NO_PREVIEW) + { + if(efa->v4) + { + efa->tmp.l = preview->num_quads*8; + preview->num_quads++; + } else { + efa->tmp.l = preview->num_tris*6; + preview->num_tris++; + } + } +} + + +/* setup face preview for all coincident uvs and their faces */ +static void stitch_setup_face_preview_for_uv_group(UvElement *element, StitchState *state, IslandStitchData *island_stitch_data){ + StitchPreviewer *preview = uv_get_stitch_previewer(); + + /* static island does not change so returning immediately */ + //if(state->snap_islands && !state->midpoints && state->static_island == element->island) + // return; + + if(state->snap_islands){ + island_stitch_data[element->island].addedForPreview = 1; + } + + do{ + stitch_set_face_preview_buffer_position(element->face, preview); + element = element->next; + }while(element && !element->separate); +} + + +/* checks if uvs are indeed stitchable and registers so that they can be shown in preview */ +static void stitch_validate_stichability(UvElement *element, StitchState *state, IslandStitchData *island_stitch_data){ + UvElement *element_iter; + StitchPreviewer *preview; + + preview = uv_get_stitch_previewer(); + element_iter = state->element_map->vert[(*(&element->face->v1 + element->tfindex))->tmp.l]; + + for(; element_iter; element_iter = element_iter->next){ + if(element_iter->separate){ + if(element_iter == element) + continue; + if(state->use_limit){ + MTFace *mtface_orig = CustomData_em_get(&state->em->fdata, element->face->data, CD_MTFACE); + MTFace *mtface_iter = CustomData_em_get(&state->em->fdata, element_iter->face->data, CD_MTFACE); + + if(fabs(mtface_orig->uv[element->tfindex][0] - mtface_iter->uv[element_iter->tfindex][0]) < state->limit_dist + && fabs(mtface_orig->uv[element->tfindex][1] - mtface_iter->uv[element_iter->tfindex][1]) < state->limit_dist){ + if(((element_iter->island == state->static_island) || (element->island == state->static_island)) && + !((element_iter->island == element->island) && state->snap_islands)){ + element->flag |= STITCH_STITCHABLE; + preview->num_stitchable++; + stitch_setup_face_preview_for_uv_group(element, state, island_stitch_data); + return; + } + } + }else{ + if(((element_iter->island == state->static_island) || (element->island == state->static_island)) && + !((element_iter->island == element->island) && state->snap_islands)){ + element->flag |= STITCH_STITCHABLE; + preview->num_stitchable++; + stitch_setup_face_preview_for_uv_group(element, state, island_stitch_data); + return; + } + } + } + } + + /* this can happen if the uvs to be stitched are not on a stitchable island */ + if(!(element->flag & STITCH_STITCHABLE)){ + preview->num_unstitchable++; + } +} + +/* main processing function. It calculates preview and final positions. */ +static int stitch_process_data(StitchState *state, Scene *scene, int final) +{ + int i; + StitchPreviewer *preview = uv_get_stitch_previewer(); + IslandStitchData *island_stitch_data = NULL; + int previous_island = state->static_island; + EditFace *efa; + EditVert *ev; + UVVertAverage *final_position; + char stitch_midpoints = state->midpoints; + /* use vertex normals for snapping rotation */ + char use_vert_normals = 1; + /* used to map uv indices to uvaverage indices for selection */ + unsigned int *uvfinal_map; + + /* cleanup previous preview */ + stitch_preview_delete(); + preview = stitch_preview_init(); + if(preview == NULL) + return 0; + /* each face holds its position in the preview buffer in tmp. -1 is uninitialized */ + for(efa = state->em->faces.first; efa; efa = efa->next){ + efa->tmp.l = STITCH_NO_PREVIEW; + } + + island_stitch_data = MEM_callocN(sizeof(*island_stitch_data)*state->element_map->totalIslands, "stitch_island_data"); + if(!island_stitch_data){ + return 0; + } + + /* store Indices to editVerts. */ + for(ev = state->em->verts.first, i = 0; ev; ev = ev->next, i++){ + ev->tmp.l = i; + } + + /***************************************** + * First determine stitchability of uvs * + *****************************************/ + + for(i = 0; i < state->selection_size; i++){ + UvElement *element = state->selection_stack[i]; + determine_uv_stitchability(element, state, island_stitch_data); + } + + /* set static island to one that is added for preview */ + state->static_island %= state->element_map->totalIslands; + while(!(island_stitch_data[state->static_island].stitchableCandidate)){ + state->static_island++; + state->static_island %= state->element_map->totalIslands; + /* this is entirely possible if for example limit stitching with no stitchable verts or no selection */ + if(state->static_island == previous_island) + break; + } + + for(i = 0; i < state->selection_size; i++){ + UvElement *element = state->selection_stack[i]; + if(element->flag & STITCH_STITCHABLE_CANDIDATE){ + element->flag &= ~STITCH_STITCHABLE_CANDIDATE; + stitch_validate_stichability(element, state, island_stitch_data); + }else{ + /* add to preview for unstitchable */ + preview->num_unstitchable++; + } + } + + /***************************************** + * Setup preview for stitchable islands * + *****************************************/ + if(state->snap_islands){ + for(i = 0; i < state->element_map->totalIslands; i++){ + if(island_stitch_data[i].addedForPreview){ + int numOfIslandUVs = 0, j; + UvElement *element; + numOfIslandUVs = getNumOfIslandUvs(state->element_map, i); + element = &state->element_map->buf[state->element_map->islandIndices[i]]; + for(j = 0; j < numOfIslandUVs; j++, element++){ + stitch_set_face_preview_buffer_position(element->face, preview); + } + } + } + } + + /********************************************************************* + * Setup the preview buffers and fill them with the appropriate data * + *********************************************************************/ + if(!final){ + unsigned int tricount = 0, quadcount = 0; + int stitchBufferIndex = 0, unstitchBufferIndex = 0; + /* initialize the preview buffers */ + preview->preview_quads = (float *)MEM_mallocN(preview->num_quads*sizeof(float)*8, "quad_uv_stitch_prev"); + preview->preview_tris = (float *)MEM_mallocN(preview->num_tris*sizeof(float)*6, "tri_uv_stitch_prev"); + + preview->preview_stitchable = (float *)MEM_mallocN(preview->num_stitchable*sizeof(float)*2, "stitch_preview_stichable_data"); + preview->preview_unstitchable = (float *)MEM_mallocN(preview->num_unstitchable*sizeof(float)*2, "stitch_preview_unstichable_data"); + + preview->static_quads = (float *)MEM_mallocN(state->quads_per_island[state->static_island]*sizeof(float)*8, "static_island_preview_quads"); + preview->static_tris = (float *)MEM_mallocN(state->tris_per_island[state->static_island]*sizeof(float)*6, "static_island_preview_tris"); + + preview->num_static_quads = state->quads_per_island[state->static_island]; + preview->num_static_tris = state->tris_per_island[state->static_island]; + /* will cause cancel and freeing of all data structures so OK */ + if(!preview->preview_quads || !preview->preview_tris || !preview->preview_stitchable || !preview->preview_unstitchable){ + return 0; + } + + /* copy data from MTFaces to the preview display buffers */ + for(efa = state->em->faces.first; efa; efa = efa->next){ + MTFace *mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE); + UvElement *element = ED_get_uv_element(state->element_map, efa, 0); + + if(element){ + if(efa->tmp.l != STITCH_NO_PREVIEW){ + if(efa->v4) { + memcpy(preview->preview_quads+efa->tmp.l, &mt->uv[0][0], 8*sizeof(float)); + } else { + memcpy(preview->preview_tris+efa->tmp.l, &mt->uv[0][0], 6*sizeof(float)); + } + } + + if(element->island == state->static_island){ + if(efa->v4) { + memcpy(preview->static_quads + quadcount*8, &mt->uv[0][0], 8*sizeof(float)); + quadcount++; + } else { + memcpy(preview->static_tris + tricount*6, &mt->uv[0][0], 6*sizeof(float)); + tricount++; + } + } + } + } + + /* fill the appropriate preview buffers */ + for(i = 0; i < state->total_separate_uvs; i++){ + UvElement *element = (UvElement *)state->uvs[i]; + if(element->flag & STITCH_STITCHABLE){ + MTFace *mt; + efa = element->face; + mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE); + + preview->preview_stitchable[stitchBufferIndex*2] = mt->uv[element->tfindex][0]; + preview->preview_stitchable[stitchBufferIndex*2 + 1] = mt->uv[element->tfindex][1]; + stitchBufferIndex++; + } + else if(element->flag & STITCH_SELECTED){ + MTFace *mt; + efa = element->face; + mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE); + + preview->preview_unstitchable[unstitchBufferIndex*2] = mt->uv[element->tfindex][0]; + preview->preview_unstitchable[unstitchBufferIndex*2 + 1] = mt->uv[element->tfindex][1]; + unstitchBufferIndex++; + } + } + } + + /****************************************************** + * Here we calculate the final coordinates of the uvs * + ******************************************************/ + + final_position = MEM_callocN(state->selection_size*sizeof(*final_position), "stitch_uv_average"); + uvfinal_map = MEM_mallocN(state->em->totvert*sizeof(*uvfinal_map), "stitch_uv_final_map"); + + /* first pass, calculate final position for stitchable uvs of the static island */ + for(i = 0; i < state->selection_size; i++){ + UvElement *element = state->selection_stack[i]; + if(element->flag & STITCH_STITCHABLE){ + UvElement *element_iter = state->element_map->vert[(*(&element->face->v1 + element->tfindex))->tmp.l]; + uvfinal_map[(*(&element->face->v1 + element->tfindex))->tmp.l] = i; + for(;element_iter; element_iter = element_iter->next){ + if(element_iter->flag & STITCH_STITCHABLE){ + MTFace *mt; + efa = element_iter->face; + mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE); + if(stitch_midpoints){ + final_position[i].uv[0] += mt->uv[element_iter->tfindex][0]; + final_position[i].uv[1] += mt->uv[element_iter->tfindex][1]; + final_position[i].count++; + }else if(element_iter->island == state->static_island){ + final_position[i].uv[0] = mt->uv[element_iter->tfindex][0]; + final_position[i].uv[1] = mt->uv[element_iter->tfindex][1]; + } + } + } + } + if(stitch_midpoints){ + final_position[i].uv[0] /= final_position[i].count; + final_position[i].uv[1] /= final_position[i].count; + } + } + + /* second pass, calculate island rotation and translation before modifying any uvs */ + if(state->snap_islands){ + for(i = 0; i < state->selection_size; i++){ + UvElement *element = state->selection_stack[i]; + if(element->flag & STITCH_STITCHABLE){ + MTFace *mt; + efa = element->face; + mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE); + + /* accumulate each islands' translation from stitchable elements. it is important to do here + * because in final pass MTFaces get modified and result is zero. */ + island_stitch_data[element->island].translation[0] += final_position[i].uv[0] - mt->uv[element->tfindex][0]; + island_stitch_data[element->island].translation[1] += final_position[i].uv[1] - mt->uv[element->tfindex][1]; + island_stitch_data[element->island].medianPoint[0] += mt->uv[element->tfindex][0]; + island_stitch_data[element->island].medianPoint[1] += mt->uv[element->tfindex][1]; + island_stitch_data[element->island].numOfElements++; + } + } + + /* only calculate rotation when an edge has been fully selected */ + for(i = 0; i < state->total_boundary_edges; i++){ + UvEdge *edge = state->edges+i; + if((state->uvs[edge->uv1]->flag & STITCH_STITCHABLE) && (state->uvs[edge->uv2]->flag & STITCH_STITCHABLE)){ + stitch_island_calculate_edge_rotation(edge, state, final_position, uvfinal_map, island_stitch_data); + use_vert_normals = 0; + } + } + if(use_vert_normals){ + for(i = 0; i < state->selection_size; i++){ + UvElement *element = state->selection_stack[i]; + if(element->flag & STITCH_STITCHABLE){ + stitch_island_calculate_vert_rotation(element, state, island_stitch_data, 0); + } + } + } + } + + /* third pass, propagate changes to stitchable uvs */ + for(i = 0; i < state->selection_size; i++){ + UvElement *element = state->selection_stack[i]; + if(element->flag & STITCH_STITCHABLE){ + UvElement *element_iter = state->element_map->vert[(*(&element->face->v1 + element->tfindex))->tmp.l]; + for(;element_iter;){ + /* determine if uv stitchable */ + if(element_iter->separate && element_iter->flag & STITCH_STITCHABLE){ + MTFace *mt; + efa = element_iter->face; + mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE); + + /* propagate to coincident uvs */ + do{ + efa = element_iter->face; + mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE); + + element_iter->flag |= STITCH_PROCESSED; + /* either flush to preview or to the MTFace, if final */ + if(final){ + mt->uv[element_iter->tfindex][0] = final_position[i].uv[0]; + mt->uv[element_iter->tfindex][1] = final_position[i].uv[1]; + + uvedit_uv_select(scene, efa, mt, element_iter->tfindex); + }else if(efa->tmp.l != STITCH_NO_PREVIEW){ + if(efa->v4){ + *(preview->preview_quads+efa->tmp.l + element_iter->tfindex*2) = final_position[i].uv[0]; + *(preview->preview_quads+efa->tmp.l + element_iter->tfindex*2 + 1) = final_position[i].uv[1]; + }else{ + *(preview->preview_tris+efa->tmp.l + element_iter->tfindex*2) = final_position[i].uv[0]; + *(preview->preview_tris+efa->tmp.l + element_iter->tfindex*2 + 1) = final_position[i].uv[1]; + } + } + + /* end of calculations, keep only the selection flag */ + if( (!state->snap_islands) || ((!stitch_midpoints) && (element_iter->island == state->static_island))) { + element_iter->flag &= STITCH_SELECTED; + } + + element_iter = element_iter->next; + }while(element_iter && !element_iter->separate); + + continue; + } + element_iter = element_iter->next; + } + } + } + + /* final pass, calculate Island translation/rotation if needed */ + if(state->snap_islands){ + stitch_calculate_island_snapping(state, preview, island_stitch_data, final); + } + + MEM_freeN(final_position); + MEM_freeN(uvfinal_map); + MEM_freeN(island_stitch_data); + + return 1; +} + +/* Stitch hash initialisation functions */ +static unsigned int uv_edge_hash(const void *key){ + UvEdge *edge = (UvEdge *)key; + return + BLI_ghashutil_inthash(SET_INT_IN_POINTER(edge->uv2)) + + BLI_ghashutil_inthash(SET_INT_IN_POINTER(edge->uv1)); +} + +static int uv_edge_compare(const void *a, const void *b){ + UvEdge *edge1 = (UvEdge *)a; + UvEdge *edge2 = (UvEdge *)b; + + if((edge1->uv1 == edge2->uv1) && (edge1->uv2 == edge2->uv2)){ + return 0; + } + return 1; +} + + +/* Select all common uvs */ +static void stitch_select_uv(UvElement *element, StitchState *stitch_state, int always_select) +{ + /* This works due to setting of tmp in find nearest uv vert */ + UvElement *element_iter; + UvElement **selection_stack = stitch_state->selection_stack; + + element_iter = stitch_state->element_map->vert[(*(&element->face->v1 + element->tfindex))->tmp.l]; + /* first deselect all common uvs */ + for(; element_iter; element_iter = element_iter->next){ + if(element_iter->separate){ + /* only separators go to selection */ + if(element_iter->flag & STITCH_SELECTED){ + int i; + if(always_select) + continue; + + element_iter->flag &= ~STITCH_SELECTED; + for(i = 0; i < stitch_state->selection_size; i++){ + if(selection_stack[i] == element_iter){ + (stitch_state->selection_size)--; + selection_stack[i] = selection_stack[stitch_state->selection_size]; + break; + } + } + }else{ + element_iter->flag |= STITCH_SELECTED; + selection_stack[(stitch_state->selection_size)++] = element_iter; + } + } + } +} + +static void stitch_calculate_edge_normal(EditMesh *em, UvEdge *edge, float *normal){ + UvElement *element = edge->element; + EditFace *efa = element->face; + MTFace *mt = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + int nverts = efa->v4?4 : 3; + int index = index = (element->tfindex + 2)%nverts; + float tangent[2], internal[2]; + + sub_v2_v2v2(tangent, mt->uv[(element->tfindex + 1)%nverts], mt->uv[element->tfindex]); + sub_v2_v2v2(internal, mt->uv[index], mt->uv[element->tfindex]); + + /* choose one of the normals */ + normal[0] = tangent[1]; + normal[1] = -tangent[0]; + + /* if normal points inside the face, invert */ + if(dot_v2v2(normal, internal) > 0){ + normal[0] = -tangent[1]; + normal[1] = tangent[0]; + } + + normalize_v2(normal); +} + +static int stitch_init(bContext *C, wmOperator *op) +{ + /* for fast edge lookup... */ + GHash *edgeHash; + /* ...and actual edge storage */ + UvEdge *edges; + int total_edges; + /* maps uvelements to their first coincident uv */ + int *map; + int counter = 0, i; + EditFace *efa; + EditMesh *em; + GHashIterator* ghi; + UvEdge *all_edges; + StitchState *state = MEM_mallocN(sizeof(StitchState), "stitch state"); + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = scene->toolsettings; + + Object *obedit = CTX_data_edit_object(C); + + op->customdata = state; + + if(!state) + return 0; + + /* initialize state */ + state->use_limit = RNA_boolean_get(op->ptr, "use_limit"); + state->limit_dist = RNA_float_get(op->ptr, "limit"); + state->em = em = BKE_mesh_get_editmesh((Mesh*)obedit->data); + state->snap_islands = RNA_boolean_get(op->ptr, "snap_islands"); + state->static_island = RNA_int_get(op->ptr, "static_island"); + state->midpoints = RNA_boolean_get(op->ptr, "midpoint_snap"); + /* in uv synch selection, all uv's are visible */ + if(ts->uv_flag & UV_SYNC_SELECTION){ + state->element_map = EM_make_uv_element_map(state->em, 0, 1); + }else{ + state->element_map = EM_make_uv_element_map(state->em, 1, 1); + } + if(!state->element_map){ + stitch_state_delete(state); + return 0; + } + + /* Entirely possible if redoing last operator that static island is bigger than total number of islands. + * This ensures we get no hang in the island checking code in stitch_process_data. */ + state->static_island %= state->element_map->totalIslands; + + /* Count 'unique' uvs */ + for(i = 0; i < state->element_map->totalUVs; i++){ + if(state->element_map->buf[i].separate){ + counter++; + } + } + + /* Allocate the unique uv buffers */ + state->uvs = MEM_mallocN(sizeof(*state->uvs)*counter, "uv_stitch_unique_uvs"); + /* internal uvs need no normals but it is hard and slow to keep a map of + * normals only for boundary uvs, so allocating for all uvs */ + state->normals = MEM_callocN(sizeof(*state->normals)*counter*2, "uv_stitch_normals"); + state->total_separate_uvs = counter; + /* we can at most have totalUVs edges or uvs selected. Actually they are less, considering we store only + * unique uvs for processing but I am accounting for all bizarre cases, especially for edges, this way */ + state->selection_stack = MEM_mallocN(sizeof(*state->selection_stack)*counter, "uv_stitch_selection_stack"); + state->map = map = MEM_mallocN(sizeof(*map)*state->element_map->totalUVs, "uv_stitch_unique_map"); + /* Allocate the edge stack */ + edgeHash = BLI_ghash_new(uv_edge_hash, uv_edge_compare, "stitch_edge_hash"); + all_edges = MEM_mallocN(sizeof(*all_edges)*state->element_map->totalUVs, "stitch_all_edges"); + + if(!state->selection_stack || !state->uvs || !map || !edgeHash || !all_edges){ + stitch_state_delete(state); + return 0; + } + + /* So that we can use this as index for the UvElements */ + counter = -1; + /* initialize the unique UVs and map */ + for(i = 0; i < state->em->totvert; i++){ + UvElement *element = state->element_map->vert[i]; + for(; element; element = element->next){ + if(element->separate){ + counter++; + state->uvs[counter] = element; + } + /* pointer arithmetic to the rescue, as always :)*/ + map[element - state->element_map->buf] = counter; + } + } + + /* Now, on to generate our uv connectivity data */ + for(efa = state->em->faces.first, counter = 0; efa; efa = efa->next){ + if((ts->uv_flag & UV_SYNC_SELECTION) || (!efa->h && efa->f & SELECT)){ + int nverts = efa->v4 ? 4 : 3; + + for(i = 0; i < nverts; i++){ + UvElement *element = ED_get_uv_element(state->element_map, efa, i); + int offset1, itmp1 = element - state->element_map->buf; + int offset2, itmp2 = ED_get_uv_element(state->element_map, efa, (i+1)%nverts) - state->element_map->buf; + + offset1 = map[itmp1]; + offset2 = map[itmp2]; + + all_edges[counter].flag = 0; + all_edges[counter].element = element; + /* using an order policy, sort uvs according to address space. This avoids + * Having two different UvEdges with the same uvs on different positions */ + if(offset1 < offset2){ + all_edges[counter].uv1 = offset1; + all_edges[counter].uv2 = offset2; + } + else{ + all_edges[counter].uv1 = offset2; + all_edges[counter].uv2 = offset1; + } + + if(BLI_ghash_haskey(edgeHash, &all_edges[counter])){ + char *flag = BLI_ghash_lookup(edgeHash, &all_edges[counter]); + *flag = 0; + } + else{ + BLI_ghash_insert(edgeHash, &all_edges[counter], &(all_edges[counter].flag)); + all_edges[counter].flag = STITCH_BOUNDARY; + } + counter++; + } + } + } + + + ghi = BLI_ghashIterator_new(edgeHash); + total_edges = 0; + /* fill the edges with data */ + for(i = 0; !BLI_ghashIterator_isDone(ghi); BLI_ghashIterator_step(ghi)){ + UvEdge *edge = ((UvEdge *)BLI_ghashIterator_getKey(ghi)); + if(edge->flag & STITCH_BOUNDARY){ + total_edges++; + } + } + state->edges = edges = MEM_mallocN(sizeof(*edges)*total_edges, "stitch_edges"); + if(!ghi || !edges){ + MEM_freeN(all_edges); + stitch_state_delete(state); + return 0; + } + + state->total_boundary_edges = total_edges; + + /* fill the edges with data */ + for(i = 0, BLI_ghashIterator_init(ghi, edgeHash); !BLI_ghashIterator_isDone(ghi); BLI_ghashIterator_step(ghi)){ + UvEdge *edge = ((UvEdge *)BLI_ghashIterator_getKey(ghi)); + if(edge->flag & STITCH_BOUNDARY){ + edges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(ghi)); + } + } + + /* cleanup temporary stuff */ + BLI_ghashIterator_free(ghi); + MEM_freeN(all_edges); + + /* refill hash with new pointers to cleanup duplicates */ + BLI_ghash_free(edgeHash, NULL, NULL); + + /***** calculate 2D normals for boundary uvs *****/ + + /* we use boundary edges to calculate 2D normals. + * to disambiguate the direction of the normal, we also need + * a point "inside" the island, that can be provided by + * the opposite uv for a quad, or the next uv for a triangle. */ + + for(i = 0; i < total_edges; i++){ + float normal[2]; + stitch_calculate_edge_normal(em, edges + i, normal); + + add_v2_v2(state->normals + edges[i].uv1*2, normal); + add_v2_v2(state->normals + edges[i].uv2*2, normal); + + normalize_v2(state->normals + edges[i].uv1*2); + normalize_v2(state->normals + edges[i].uv2*2); + } + + + /***** fill selection stack *******/ + + state->selection_size = 0; + + /* Load old selection if redoing operator with different settings */ + if(RNA_property_is_set(op->ptr, "selection")){ + int faceIndex, elementIndex; + UvElement *element; + + EM_init_index_arrays(em, 0, 0, 1); + + + RNA_BEGIN(op->ptr, itemptr, "selection") { + faceIndex = RNA_int_get(&itemptr, "face_index"); + elementIndex = RNA_int_get(&itemptr, "element_index"); + efa = EM_get_face_for_index(faceIndex); + element = ED_get_uv_element(state->element_map, efa, elementIndex); + stitch_select_uv(element, state, 1); + } + RNA_END; + + EM_free_index_arrays(); + /* Clear the selection */ + RNA_collection_clear(op->ptr, "selection"); + + } else { + for(efa = state->em->faces.first ; efa; efa = efa->next){ + int numOfVerts; + MTFace *mt; + mt = CustomData_em_get(&state->em->fdata, efa->data, CD_MTFACE); + numOfVerts = efa->v4 ? 4 : 3; + + for(i = 0; i < numOfVerts; i++){ + if(uvedit_uv_selected(scene, efa, mt, i)){ + UvElement *element = ED_get_uv_element(state->element_map, efa, i); + stitch_select_uv(element, state, 1); + } + } + } + } + + /***** initialise static island preview data *****/ + + state->quads_per_island = MEM_mallocN(sizeof(*state->quads_per_island)*state->element_map->totalIslands, + "stitch island quads"); + state->tris_per_island = MEM_mallocN(sizeof(*state->tris_per_island)*state->element_map->totalIslands, + "stitch island tris"); + for(i = 0; i < state->element_map->totalIslands; i++){ + state->quads_per_island[i] = 0; + state->tris_per_island[i] = 0; + } + + for(efa = state->em->faces.first; efa; efa = efa->next){ + UvElement *element = ED_get_uv_element(state->element_map, efa, 0); + + if(element){ + if(efa->v4){ + state->quads_per_island[element->island]++; + } + else { + state->tris_per_island[element->island]++; + } + } + } + + if(!stitch_process_data(state, scene, 0)){ + stitch_state_delete(state); + return 0; + } + + stitch_update_header(state, C); + return 1; +} + +static int stitch_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event)) +{ + Object *obedit = CTX_data_edit_object(C); + if(!stitch_init(C, op)) + return OPERATOR_CANCELLED; + + WM_event_add_modal_handler(C, op); + WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); + return OPERATOR_RUNNING_MODAL; +} + +static void stitch_exit(bContext *C, wmOperator *op, int finished) +{ + StitchState *stitch_state; + Scene *scene; + SpaceImage *sima; + ScrArea *sa= CTX_wm_area(C); + Object *obedit; + + scene= CTX_data_scene(C); + obedit= CTX_data_edit_object(C); + sima= CTX_wm_space_image(C); + + stitch_state = (StitchState *)op->customdata; + + if(finished){ + EditFace *efa; + int i; + + RNA_float_set(op->ptr, "limit", stitch_state->limit_dist); + RNA_boolean_set(op->ptr, "use_limit", stitch_state->use_limit); + RNA_boolean_set(op->ptr, "snap_islands", stitch_state->snap_islands); + RNA_int_set(op->ptr, "static_island", stitch_state->static_island); + RNA_boolean_set(op->ptr, "midpoint_snap", stitch_state->midpoints); + + for(i = 0, efa = stitch_state->em->faces.first; efa; efa = efa->next, i++){ + efa->tmp.l = i; + } + + /* Store selection for re-execution of stitch */ + for(i = 0; i < stitch_state->selection_size; i++){ + PointerRNA itemptr; + UvElement *element = stitch_state->selection_stack[i]; + + RNA_collection_add(op->ptr, "selection", &itemptr); + + RNA_int_set(&itemptr, "face_index", element->face->tmp.l); + RNA_int_set(&itemptr, "element_index", element->tfindex); + } + + + uvedit_live_unwrap_update(sima, scene, obedit); + } + + if(sa) + ED_area_headerprint(sa, NULL); + + DAG_id_tag_update(obedit->data, 0); + WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); + BKE_mesh_end_editmesh(obedit->data, stitch_state->em); + + stitch_state_delete(stitch_state); + op->customdata = NULL; + + stitch_preview_delete(); +} + + +static int stitch_cancel(bContext *C, wmOperator *op) +{ + stitch_exit(C, op, 0); + return OPERATOR_CANCELLED; +} + + +static int stitch_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + + if(!stitch_init(C, op)) + return OPERATOR_CANCELLED; + if(stitch_process_data((StitchState *)op->customdata, scene, 1)){ + stitch_exit(C, op, 1); + return OPERATOR_FINISHED; + }else { + return stitch_cancel(C, op); + } +} + +static void stitch_select(bContext *C, Scene *scene, wmEvent *event, StitchState *stitch_state){ + /* add uv under mouse to processed uv's */ + float co[2]; + NearestHit hit; + ARegion *ar= CTX_wm_region(C); + Image *ima= CTX_data_edit_image(C); + + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]); + uv_find_nearest_vert(scene, ima, stitch_state->em, co, NULL, &hit); + + if(hit.efa) + { + /* Add vertex to selection, deselect all common uv's of vert other + * than selected and update the preview. This behavior was decided so that + * you can do stuff like deselect the opposite stitchable vertex and the initial still gets deselected */ + + /* This works due to setting of tmp in find nearest uv vert */ + UvElement *element = ED_get_uv_element(stitch_state->element_map, hit.efa, hit.uv); + stitch_select_uv(element, stitch_state, 0); + + } +} + +static int stitch_modal(bContext *C, wmOperator *op, wmEvent *event) +{ + StitchState *stitch_state; + Scene *scene = CTX_data_scene(C); + + stitch_state = (StitchState *)op->customdata; + + switch(event->type){ + case MIDDLEMOUSE: + return OPERATOR_PASS_THROUGH; + + /* Cancel */ + case ESCKEY: + return stitch_cancel(C, op); + + + case LEFTMOUSE: + if(event->shift && (U.flag & USER_LMOUSESELECT)){ + if(event->val == KM_RELEASE){ + stitch_select(C, scene, event, stitch_state); + + if(!stitch_process_data(stitch_state, scene, 0)){ + return stitch_cancel(C, op); + } + } + break; + } + case PADENTER: + case RETKEY: + if(stitch_process_data(stitch_state, scene, 1)){ + stitch_exit(C, op, 1); + return OPERATOR_FINISHED; + } + else { + return stitch_cancel(C, op); + } + + /* Increase limit */ + case PADPLUSKEY: + case WHEELUPMOUSE: + if(event->alt){ + stitch_state->limit_dist += 0.01; + if(!stitch_process_data(stitch_state, scene, 0)){ + return stitch_cancel(C, op); + } + break; + } + else{ + return OPERATOR_PASS_THROUGH; + } + /* Decrease limit */ + case PADMINUS: + case WHEELDOWNMOUSE: + if(event->alt){ + stitch_state->limit_dist -= 0.01; + stitch_state->limit_dist = MAX2(0.01, stitch_state->limit_dist); + if(!stitch_process_data(stitch_state, scene, 0)){ + return stitch_cancel(C, op); + } + break; + }else{ + return OPERATOR_PASS_THROUGH; + } + + /* Use Limit (Default off)*/ + case LKEY: + if(event->val == KM_PRESS){ + stitch_state->use_limit = !stitch_state->use_limit; + if(!stitch_process_data(stitch_state, scene, 0)){ + return stitch_cancel(C, op); + } + break; + } + return OPERATOR_RUNNING_MODAL; + + case IKEY: + if(event->val == KM_PRESS){ + stitch_state->static_island++; + stitch_state->static_island %= stitch_state->element_map->totalIslands; + + if(!stitch_process_data(stitch_state, scene, 0)){ + return stitch_cancel(C, op); + } + break; + } + return OPERATOR_RUNNING_MODAL; + + case MKEY: + if(event->val == KM_PRESS){ + stitch_state->midpoints = !stitch_state->midpoints; + if(!stitch_process_data(stitch_state, scene, 0)){ + return stitch_cancel(C, op); + } + } + break; + + /* Select geometry*/ + case RIGHTMOUSE: + if(!event->shift){ + return stitch_cancel(C, op); + } + if(event->val == KM_RELEASE && !(U.flag & USER_LMOUSESELECT)){ + stitch_select(C, scene, event, stitch_state); + + if(!stitch_process_data(stitch_state, scene, 0)){ + return stitch_cancel(C, op); + } + break; + } + return OPERATOR_RUNNING_MODAL; + + /* snap islands on/off */ + case SKEY: + if(event->val == KM_PRESS){ + stitch_state->snap_islands = !stitch_state->snap_islands; + if(!stitch_process_data(stitch_state, scene, 0)){ + return stitch_cancel(C, op); + } + break; + } else + return OPERATOR_RUNNING_MODAL; + + default: + return OPERATOR_RUNNING_MODAL; + } + + /* if updated settings, renew feedback message */ + stitch_update_header(stitch_state, C); + ED_region_tag_redraw(CTX_wm_region(C)); + return OPERATOR_RUNNING_MODAL; +} + +void UV_OT_stitch(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Stitch"; + ot->description = "Stitch selected UV vertices by proximity"; + ot->idname = "UV_OT_stitch"; + ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; + + /* api callbacks */ + ot->invoke = stitch_invoke; + ot->modal = stitch_modal; + ot->exec = stitch_exec; + ot->cancel = stitch_cancel; + ot->poll= ED_operator_uvedit; + + /* properties */ + RNA_def_boolean(ot->srna, "use_limit", 0, "Use Limit", "Stitch UVs within a specified limit distance"); + RNA_def_boolean(ot->srna, "snap_islands", 1, "Snap Islands", "Snap islands together. On edge stitch mode, rotates the islands too"); + + RNA_def_float(ot->srna, "limit", 0.01f, 0.0f, FLT_MAX, "Limit", "Limit distance in normalized coordinates", 0.0, FLT_MAX); + RNA_def_int(ot->srna, "static_island", 0, 0, INT_MAX, "Static Island", "Island that stays in place when stitching islands", 0, INT_MAX); + RNA_def_boolean(ot->srna, "midpoint_snap", 0, "Snap At Midpoint", "Uv's are stitched at midpoint instead of at static island"); + prop = RNA_def_collection_runtime(ot->srna, "selection", &RNA_SelectedUvElement, "Selection", ""); + /* Selection should not be editable or viewed in toolbar */ + RNA_def_property_flag(prop, PROP_HIDDEN); +} + + diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index 17e5bebd6b0..3ed4df66778 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -40,6 +40,7 @@ #include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_modifier_types.h" #include "BLI_math.h" #include "BLI_edgehash.h" @@ -48,12 +49,15 @@ #include "BLI_utildefines.h" #include "BLI_string.h" +#include "BKE_cdderivedmesh.h" +#include "BKE_subsurf.h" #include "BKE_context.h" #include "BKE_customdata.h" #include "BKE_depsgraph.h" #include "BKE_image.h" #include "BKE_main.h" #include "BKE_mesh.h" +#include "BKE_report.h" #include "PIL_time.h" @@ -272,6 +276,201 @@ static ParamHandle *construct_param_handle(Scene *scene, EditMesh *em, short imp return handle; } + +static void texface_from_original_index(EditFace *editFace, MTFace *texFace, int index, float **uv, ParamBool *pin, ParamBool *select, Scene *scene) +{ + int i, nverts = (editFace->v4)? 4: 3; + + *uv = NULL; + *pin = 0; + *select = 1; + + if(index == ORIGINDEX_NONE) + return; + + for(i = 0; i < nverts; i++) { + if((*(&editFace->v1 + i))->tmp.t == index) { + *uv = texFace->uv[i]; + *pin = ((texFace->unwrap & TF_PIN_MASK(i)) != 0); + *select = (uvedit_uv_selected(scene, editFace, texFace, i) != 0); + } + } +} + +/* unwrap handle initialization for subsurf aware-unwrapper. The many modifications required to make the original function(see above) + * work justified the existence of a new function. */ +static ParamHandle *construct_param_handle_subsurfed(Scene *scene, EditMesh *editMesh, short fill, short sel, short correct_aspect) +{ + ParamHandle *handle; + /* index pointers */ + MFace *face; + MEdge *edge; + EditVert *editVert; + MTFace *texface; + EditFace *editFace, **editFaceTmp; + EditEdge *editEdge, **editEdgeTmp; + int i; + + /* modifier initialization data, will control what type of subdivision will happen*/ + SubsurfModifierData smd = {{0}}; + /* Used to hold subsurfed Mesh */ + DerivedMesh *derivedMesh, *initialDerived; + /* holds original indices for subsurfed mesh */ + int *origVertIndices, *origFaceIndices, *origEdgeIndices; + /* Holds vertices of subsurfed mesh */ + MVert *subsurfedVerts; + MEdge *subsurfedEdges; + MFace *subsurfedFaces; + MTFace *subsurfedTexfaces; + /* number of vertices and faces for subsurfed mesh*/ + int numOfEdges, numOfFaces; + + /* holds a map to editfaces for every subsurfed MFace. These will be used to get hidden/ selected flags etc. */ + EditFace **faceMap; + /* Mini container to hold all EditFaces so that they may be indexed easily and fast. */ + EditFace **editFaceArray; + /* similar to the above, we need a way to map edges to their original ones */ + EditEdge **edgeMap; + EditEdge **editEdgeArray; + + handle = param_construct_begin(); + + if(correct_aspect) { + EditFace *eface = EM_get_actFace(editMesh, 1); + + if(eface) { + float aspx, aspy; + texface= CustomData_em_get(&editMesh->fdata, eface->data, CD_MTFACE); + + ED_image_uv_aspect(texface->tpage, &aspx, &aspy); + + if(aspx!=aspy) + param_aspect_ratio(handle, aspx, aspy); + } + } + + /* number of subdivisions to perform */ + smd.levels = scene->toolsettings->uv_subsurf_level; + smd.subdivType = ME_CC_SUBSURF; + + initialDerived = CDDM_from_editmesh(editMesh, NULL); + derivedMesh = subsurf_make_derived_from_derived(initialDerived, &smd, + 0, NULL, 0, 0, 1); + + initialDerived->release(initialDerived); + + /* get the derived data */ + subsurfedVerts = derivedMesh->getVertArray(derivedMesh); + subsurfedEdges = derivedMesh->getEdgeArray(derivedMesh); + subsurfedFaces = derivedMesh->getFaceArray(derivedMesh); + + origVertIndices = derivedMesh->getVertDataArray(derivedMesh, CD_ORIGINDEX); + origEdgeIndices = derivedMesh->getEdgeDataArray(derivedMesh, CD_ORIGINDEX); + origFaceIndices = derivedMesh->getFaceDataArray(derivedMesh, CD_ORIGINDEX); + + subsurfedTexfaces = derivedMesh->getFaceDataArray(derivedMesh, CD_MTFACE); + + numOfEdges = derivedMesh->getNumEdges(derivedMesh); + numOfFaces = derivedMesh->getNumFaces(derivedMesh); + + faceMap = MEM_mallocN(numOfFaces*sizeof(EditFace *), "unwrap_edit_face_map"); + editFaceArray = MEM_mallocN(editMesh->totface*sizeof(EditFace *), "unwrap_editFaceArray"); + + /* fill edit face array with edit faces */ + for(editFace = editMesh->faces.first, editFaceTmp = editFaceArray; editFace; editFace= editFace->next, editFaceTmp++) + *editFaceTmp = editFace; + + /* map subsurfed faces to original editFaces */ + for(i = 0; i < numOfFaces; i++) + faceMap[i] = editFaceArray[origFaceIndices[i]]; + + MEM_freeN(editFaceArray); + + edgeMap = MEM_mallocN(numOfEdges*sizeof(EditEdge *), "unwrap_edit_edge_map"); + editEdgeArray = MEM_mallocN(editMesh->totedge*sizeof(EditEdge *), "unwrap_editEdgeArray"); + + /* fill edit edge array with edit edges */ + for(editEdge = editMesh->edges.first, editEdgeTmp = editEdgeArray; editEdge; editEdge= editEdge->next, editEdgeTmp++) + *editEdgeTmp = editEdge; + + /* map subsurfed edges to original editEdges */ + for(i = 0; i < numOfEdges; i++) { + /* not all edges correspond to an old edge */ + edgeMap[i] = (origEdgeIndices[i] != -1)? + editEdgeArray[origEdgeIndices[i]] : NULL; + } + + MEM_freeN(editEdgeArray); + + /* we need the editvert indices too */ + for(editVert = editMesh->verts.first, i=0; editVert; editVert = editVert->next, i++) + editVert->tmp.t = i; + + /* Prepare and feed faces to the solver */ + for(i = 0; i < numOfFaces; i++) { + ParamKey key, vkeys[4]; + ParamBool pin[4], select[4]; + float *co[4]; + float *uv[4]; + EditFace *origFace = faceMap[i]; + MTFace *origtexface = (MTFace *)CustomData_em_get(&editMesh->fdata, origFace->data, CD_MTFACE); + + face = subsurfedFaces+i; + + if(scene->toolsettings->uv_flag & UV_SYNC_SELECTION) { + if(origFace->h) + continue; + } + else { + if((origFace->h) || (sel && (origFace->f & SELECT)==0)) + continue; + } + + /* Now we feed the rest of the data from the subsurfed faces */ + texface= subsurfedTexfaces+i; + + /* We will not check for v4 here. Subsurfed mfaces always have 4 vertices. */ + key = (ParamKey)face; + vkeys[0] = (ParamKey)face->v1; + vkeys[1] = (ParamKey)face->v2; + vkeys[2] = (ParamKey)face->v3; + vkeys[3] = (ParamKey)face->v4; + + co[0] = subsurfedVerts[face->v1].co; + co[1] = subsurfedVerts[face->v2].co; + co[2] = subsurfedVerts[face->v3].co; + co[3] = subsurfedVerts[face->v4].co; + + /* This is where all the magic is done. If the vertex exists in the, we pass the original uv pointer to the solver, thus + * flushing the solution to the edit mesh. */ + texface_from_original_index(origFace, origtexface, origVertIndices[face->v1], &uv[0], &pin[0], &select[0], scene); + texface_from_original_index(origFace, origtexface, origVertIndices[face->v2], &uv[1], &pin[1], &select[1], scene); + texface_from_original_index(origFace, origtexface, origVertIndices[face->v3], &uv[2], &pin[2], &select[2], scene); + texface_from_original_index(origFace, origtexface, origVertIndices[face->v4], &uv[3], &pin[3], &select[3], scene); + + param_face_add(handle, key, 4, vkeys, co, uv, pin, select); + } + + /* these are calculated from original mesh too */ + for(edge = subsurfedEdges, i = 0; i < numOfEdges; i++, edge++) { + if((edgeMap[i] != NULL) && edgeMap[i]->seam) { + ParamKey vkeys[2]; + vkeys[0] = (ParamKey)edge->v1; + vkeys[1] = (ParamKey)edge->v2; + param_edge_set_seam(handle, vkeys); + } + } + + param_construct_end(handle, fill, 0); + + /* cleanup */ + MEM_freeN(faceMap); + MEM_freeN(edgeMap); + derivedMesh->release(derivedMesh); + + return handle; +} + /* ******************** Minimize Stretch operator **************** */ typedef struct MinStretch { @@ -582,13 +781,17 @@ void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit) EditMesh *em= BKE_mesh_get_editmesh((Mesh*)obedit->data); short abf = scene->toolsettings->unwrapper == 0; short fillholes = scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES; + short use_subsurf = scene->toolsettings->uvcalc_flag & UVCALC_USESUBSURF; if(!ED_uvedit_test(obedit)) { BKE_mesh_end_editmesh(obedit->data, em); return; } - liveHandle = construct_param_handle(scene, em, 0, fillholes, 0, 1); + if(use_subsurf) + liveHandle = construct_param_handle_subsurfed(scene, em, fillholes, 0, 1); + else + liveHandle = construct_param_handle(scene, em, 0, fillholes, 0, 1); param_lscm_begin(liveHandle, PARAM_TRUE, abf); BKE_mesh_end_editmesh(obedit->data, em); @@ -900,14 +1103,17 @@ static void uv_map_clip_correct(EditMesh *em, wmOperator *op) /* assumes UV Map is checked, doesn't run update funcs */ void ED_unwrap_lscm(Scene *scene, Object *obedit, const short sel) { - EditMesh *em= BKE_mesh_get_editmesh((Mesh*)obedit->data); ParamHandle *handle; + EditMesh *em= BKE_mesh_get_editmesh((Mesh*)obedit->data); const short fill_holes= scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES; const short correct_aspect= !(scene->toolsettings->uvcalc_flag & UVCALC_NO_ASPECT_CORRECT); - short implicit= 0; + const short use_subsurf = scene->toolsettings->uvcalc_flag & UVCALC_USESUBSURF; - handle= construct_param_handle(scene, em, implicit, fill_holes, sel, correct_aspect); + if(use_subsurf) + handle = construct_param_handle_subsurfed(scene, em, fill_holes, sel, correct_aspect); + else + handle= construct_param_handle(scene, em, 0, fill_holes, sel, correct_aspect); param_lscm_begin(handle, PARAM_FALSE, scene->toolsettings->unwrapper == 0); param_lscm_solve(handle); @@ -930,6 +1136,9 @@ static int unwrap_exec(bContext *C, wmOperator *op) int method = RNA_enum_get(op->ptr, "method"); int fill_holes = RNA_boolean_get(op->ptr, "fill_holes"); int correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect"); + int use_subsurf = RNA_boolean_get(op->ptr, "use_subsurf_data"); + int subsurf_level = RNA_int_get(op->ptr, "uv_subsurf_level"); + float obsize[3], unitsize[3] = {1.0f, 1.0f, 1.0f}; short implicit= 0; if(!uvedit_have_selection(scene, em, implicit)) { @@ -944,8 +1153,14 @@ static int unwrap_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + mat4_to_size(obsize, obedit->obmat); + if(!compare_v3v3(obsize, unitsize, 1e-4f)) + BKE_report(op->reports, RPT_INFO, "Object scale is not 1.0. Unwrap will operate on a non-scaled version of the mesh."); + /* remember last method for live unwrap */ scene->toolsettings->unwrapper = method; + + scene->toolsettings->uv_subsurf_level = subsurf_level; if(fill_holes) scene->toolsettings->uvcalc_flag |= UVCALC_FILLHOLES; else scene->toolsettings->uvcalc_flag &= ~UVCALC_FILLHOLES; @@ -953,6 +1168,9 @@ static int unwrap_exec(bContext *C, wmOperator *op) if(correct_aspect) scene->toolsettings->uvcalc_flag &= ~UVCALC_NO_ASPECT_CORRECT; else scene->toolsettings->uvcalc_flag |= UVCALC_NO_ASPECT_CORRECT; + if(use_subsurf) scene->toolsettings->uvcalc_flag |= UVCALC_USESUBSURF; + else scene->toolsettings->uvcalc_flag &= ~UVCALC_USESUBSURF; + /* execute unwrap */ ED_unwrap_lscm(scene, obedit, TRUE); @@ -986,6 +1204,8 @@ void UV_OT_unwrap(wmOperatorType *ot) "Virtual fill holes in mesh before unwrapping, to better avoid overlaps and preserve symmetry"); RNA_def_boolean(ot->srna, "correct_aspect", 1, "Correct Aspect", "Map UVs taking image aspect ratio into account"); + RNA_def_boolean(ot->srna, "use_subsurf_data", 0, "Use Subsurf Data", "Map UV's taking vertex position after subsurf into account"); + RNA_def_int(ot->srna, "uv_subsurf_level", 1, 1, 6, "SubSurf Target", "Number of times to subdivide before calculating UV's", 1, 6); } /**************** Project From View operator **************/ |