/* $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) 2004 by Blender Foundation. * All rights reserved. * * The Original Code is: all of this file. * * Contributor(s): Johnny Matthews, Geoffrey Bantle. * * ***** END GPL LICENSE BLOCK ***** */ /* editmesh_tool.c: UI called tools for editmesh, geometry changes here, otherwise in mods.c */ #include #include #include #include #include "BLO_sys_types.h" // for intptr_t support #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_key_types.h" #include "MEM_guardedalloc.h" #include "RNA_define.h" #include "RNA_access.h" #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_editVert.h" #include "BLI_rand.h" #include "BLI_ghash.h" #include "BLI_linklist.h" #include "BLI_heap.h" #include "BKE_context.h" #include "BKE_depsgraph.h" #include "BKE_global.h" #include "BKE_key.h" #include "BKE_mesh.h" #include "BKE_bmesh.h" #include "BKE_report.h" #include "WM_api.h" #include "WM_types.h" #include "ED_mesh.h" #include "ED_screen.h" #include "ED_transform.h" #include "ED_view3d.h" #include "ED_object.h" #include "mesh_intern.h" /* XXX */ static void waitcursor(int val) {} #define add_numbut(a, b, c, d, e, f, g) {} /* XXX */ /* RNA corner cut enum property - used in multiple files for tools * that need this property for esubdivideflag() */ EnumPropertyItem corner_type_items[] = { {SUBDIV_CORNER_PATH, "PATH", 0, "Path", ""}, {SUBDIV_CORNER_INNERVERT, "INNER_VERTEX", 0, "Inner Vertex", ""}, {SUBDIV_CORNER_FAN, "FAN", 0, "Fan", ""}, {0, NULL, 0, NULL, NULL}}; /* local prototypes ---------------*/ static void free_tagged_edges_faces(EditMesh *em, EditEdge *eed, EditFace *efa); int EdgeLoopDelete(EditMesh *em, wmOperator *op); /********* qsort routines *********/ typedef struct xvertsort { float x; EditVert *v1; } xvertsort; static int vergxco(const void *v1, const void *v2) { const xvertsort *x1=v1, *x2=v2; if( x1->x > x2->x ) return 1; else if( x1->x < x2->x) return -1; return 0; } struct facesort { uintptr_t x; struct EditFace *efa; }; static int vergface(const void *v1, const void *v2) { const struct facesort *x1=v1, *x2=v2; if( x1->x > x2->x ) return 1; else if( x1->x < x2->x) return -1; return 0; } /* *********************************** */ static void convert_to_triface(EditMesh *em, int direction) { EditFace *efa, *efan, *next; float fac; efa= em->faces.last; while(efa) { next= efa->prev; if(efa->v4) { if(efa->f & SELECT) { /* choose shortest diagonal for split */ fac= len_v3v3(efa->v1->co, efa->v3->co) - len_v3v3(efa->v2->co, efa->v4->co); /* this makes sure exact squares get split different in both cases */ if( (direction==0 && fac0.0f) ) { efan= EM_face_from_faces(em, efa, NULL, 0, 1, 2, -1); if(efa->f & SELECT) EM_select_face(efan, 1); efan= EM_face_from_faces(em, efa, NULL, 0, 2, 3, -1); if(efa->f & SELECT) EM_select_face(efan, 1); } else { efan= EM_face_from_faces(em, efa, NULL, 0, 1, 3, -1); if(efa->f & SELECT) EM_select_face(efan, 1); efan= EM_face_from_faces(em, efa, NULL, 1, 2, 3, -1); if(efa->f & SELECT) EM_select_face(efan, 1); } BLI_remlink(&em->faces, efa); free_editface(em, efa); } } efa= next; } EM_fgon_flags(em); // redo flags and indices for fgons } int removedoublesflag(EditMesh *em, short flag, short automerge, float limit) /* return amount */ { /* flag - Test with vert->flags automerge - Alternative operation, merge unselected into selected. Used for "Auto Weld" mode. warning. limit - Quick manhattan distance between verts. */ /* all verts with (flag & 'flag') are being evaluated */ EditVert *eve, *v1, *nextve; EditEdge *eed, *e1, *nexted; EditFace *efa, *nextvl; xvertsort *sortblock, *sb, *sb1; struct facesort *vlsortblock, *vsb, *vsb1; int a, b, test, amount; /* flag 128 is cleared, count */ /* Normal non weld operation */ eve= em->verts.first; amount= 0; while(eve) { eve->f &= ~128; if(eve->h==0 && (automerge || (eve->f & flag))) amount++; eve= eve->next; } if(amount==0) return 0; /* allocate memory and qsort */ sb= sortblock= MEM_mallocN(sizeof(xvertsort)*amount,"sortremovedoub"); eve= em->verts.first; while(eve) { if(eve->h==0 && (automerge || (eve->f & flag))) { sb->x= eve->co[0]+eve->co[1]+eve->co[2]; sb->v1= eve; sb++; } eve= eve->next; } qsort(sortblock, amount, sizeof(xvertsort), vergxco); /* test for doubles */ sb= sortblock; if (automerge) { for(a=0; av1; if( (eve->f & 128)==0 ) { sb1= sb+1; for(b=a+1; bf & 128)==0; b++, sb1++) { if(sb1->x - sb->x > limit) break; /* when automarge, only allow unselected->selected */ v1= sb1->v1; if( (v1->f & 128)==0 ) { if ((eve->f & flag)==0 && (v1->f & flag)==1) { if( (float)fabs(v1->co[0]-eve->co[0])<=limit && (float)fabs(v1->co[1]-eve->co[1])<=limit && (float)fabs(v1->co[2]-eve->co[2])<=limit) { /* unique bit */ eve->f|= 128; eve->tmp.v = v1; } } else if( (eve->f & flag)==1 && (v1->f & flag)==0 ) { if( (float)fabs(v1->co[0]-eve->co[0])<=limit && (float)fabs(v1->co[1]-eve->co[1])<=limit && (float)fabs(v1->co[2]-eve->co[2])<=limit) { /* unique bit */ v1->f|= 128; v1->tmp.v = eve; } } } } } } } else { for(a=0; av1; if( (eve->f & 128)==0 ) { sb1= sb+1; for(b=a+1; bx - sb->x > limit) break; v1= sb1->v1; /* second test: is vertex allowed */ if( (v1->f & 128)==0 ) { if( (float)fabs(v1->co[0]-eve->co[0])<=limit && (float)fabs(v1->co[1]-eve->co[1])<=limit && (float)fabs(v1->co[2]-eve->co[2])<=limit) { v1->f|= 128; v1->tmp.v = eve; } } } } } } MEM_freeN(sortblock); if (!automerge) for(eve = em->verts.first; eve; eve=eve->next) if((eve->f & flag) && (eve->f & 128)) EM_data_interp_from_verts(em, eve, eve->tmp.v, eve->tmp.v, 0.5f); /* test edges and insert again */ eed= em->edges.first; while(eed) { eed->f2= 0; eed= eed->next; } eed= em->edges.last; while(eed) { nexted= eed->prev; if(eed->f2==0) { if( (eed->v1->f & 128) || (eed->v2->f & 128) ) { remedge(em, eed); if(eed->v1->f & 128) eed->v1 = eed->v1->tmp.v; if(eed->v2->f & 128) eed->v2 = eed->v2->tmp.v; e1= addedgelist(em, eed->v1, eed->v2, eed); if(e1) { e1->f2= 1; if(eed->f & SELECT) e1->f |= SELECT; } if(e1!=eed) free_editedge(em, eed); } } eed= nexted; } /* first count amount of test faces */ efa= (struct EditFace *)em->faces.first; amount= 0; while(efa) { efa->f1= 0; if(efa->v1->f & 128) efa->f1= 1; else if(efa->v2->f & 128) efa->f1= 1; else if(efa->v3->f & 128) efa->f1= 1; else if(efa->v4 && (efa->v4->f & 128)) efa->f1= 1; if(efa->f1==1) amount++; efa= efa->next; } /* test faces for double vertices, and if needed remove them */ efa= (struct EditFace *)em->faces.first; while(efa) { nextvl= efa->next; if(efa->f1==1) { if(efa->v1->f & 128) efa->v1= efa->v1->tmp.v; if(efa->v2->f & 128) efa->v2= efa->v2->tmp.v; if(efa->v3->f & 128) efa->v3= efa->v3->tmp.v; if(efa->v4 && (efa->v4->f & 128)) efa->v4= efa->v4->tmp.v; test= 0; if(efa->v1==efa->v2) test+=1; if(efa->v2==efa->v3) test+=2; if(efa->v3==efa->v1) test+=4; if(efa->v4==efa->v1) test+=8; if(efa->v3==efa->v4) test+=16; if(efa->v2==efa->v4) test+=32; if(test) { if(efa->v4) { if(test==1 || test==2) { efa->v2= efa->v3; efa->v3= efa->v4; efa->v4= 0; EM_data_interp_from_faces(em, efa, NULL, efa, 0, 2, 3, 3); test= 0; } else if(test==8 || test==16) { efa->v4= 0; test= 0; } else { BLI_remlink(&em->faces, efa); free_editface(em, efa); amount--; } } else { BLI_remlink(&em->faces, efa); free_editface(em, efa); amount--; } } if(test==0) { /* set edge pointers */ efa->e1= findedgelist(em, efa->v1, efa->v2); efa->e2= findedgelist(em, efa->v2, efa->v3); if(efa->v4==0) { efa->e3= findedgelist(em, efa->v3, efa->v1); efa->e4= 0; } else { efa->e3= findedgelist(em, efa->v3, efa->v4); efa->e4= findedgelist(em, efa->v4, efa->v1); } } } efa= nextvl; } /* double faces: sort block */ /* count again, now all selected faces */ amount= 0; efa= em->faces.first; while(efa) { efa->f1= 0; if(faceselectedOR(efa, 1)) { efa->f1= 1; amount++; } efa= efa->next; } if(amount) { /* double faces: sort block */ vsb= vlsortblock= MEM_mallocN(sizeof(struct facesort)*amount, "sortremovedoub"); efa= em->faces.first; while(efa) { if(efa->f1 & 1) { if(efa->v4) vsb->x= (uintptr_t) MIN4( (uintptr_t)efa->v1, (uintptr_t)efa->v2, (uintptr_t)efa->v3, (uintptr_t)efa->v4); else vsb->x= (uintptr_t) MIN3( (uintptr_t)efa->v1, (uintptr_t)efa->v2, (uintptr_t)efa->v3); vsb->efa= efa; vsb++; } efa= efa->next; } qsort(vlsortblock, amount, sizeof(struct facesort), vergface); vsb= vlsortblock; for(a=0; aefa; if( (efa->f1 & 128)==0 ) { vsb1= vsb+1; for(b=a+1; bx != vsb1->x) break; /* second test: is test permitted? */ efa= vsb1->efa; if( (efa->f1 & 128)==0 ) { if( compareface(efa, vsb->efa)) efa->f1 |= 128; } vsb1++; } } vsb++; } MEM_freeN(vlsortblock); /* remove double faces */ efa= (struct EditFace *)em->faces.first; while(efa) { nextvl= efa->next; if(efa->f1 & 128) { BLI_remlink(&em->faces, efa); free_editface(em, efa); } efa= nextvl; } } /* remove double vertices */ a= 0; eve= (struct EditVert *)em->verts.first; while(eve) { nextve= eve->next; if(automerge || eve->f & flag) { if(eve->f & 128) { a++; BLI_remlink(&em->verts, eve); free_editvert(em, eve); } } eve= nextve; } return a; /* amount */ } static int removedoublesflag_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh(((Mesh *)obedit->data)); int count = removedoublesflag(em,1,0,RNA_float_get(op->ptr, "limit")); if(count) { recalc_editnormals(em); DAG_id_flush_update(obedit->data, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); } BKE_reportf(op->reports, RPT_INFO, "Removed %d vertices", count); BKE_mesh_end_editmesh(obedit->data, em); return OPERATOR_FINISHED; } void MESH_OT_remove_doubles(wmOperatorType *ot) { /* identifiers */ ot->name= "Remove Doubles"; ot->description= "Remove duplicate vertices"; ot->idname= "MESH_OT_remove_doubles"; /* api callbacks */ ot->exec= removedoublesflag_exec; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; RNA_def_float(ot->srna, "limit", 0.0001f, 0.000001f, 50.0f, "Merge Threshold", "Minimum distance between merged verts", 0.00001f, 2.0f); } // XXX is this needed? /* called from buttons */ static void xsortvert_flag__doSetX(void *userData, EditVert *eve, int x, int y, int index) { xvertsort *sortblock = userData; sortblock[index].x = x; } /* all verts with (flag & 'flag') are sorted */ void xsortvert_flag(bContext *C, int flag) { ViewContext vc; EditVert *eve; xvertsort *sortblock; ListBase tbase; int i, amount; em_setup_viewcontext(C, &vc); amount = BLI_countlist(&vc.em->verts); sortblock = MEM_callocN(sizeof(xvertsort)*amount,"xsort"); for (i=0,eve= vc.em->verts.first; eve; i++,eve=eve->next) if(eve->f & flag) sortblock[i].v1 = eve; ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d); mesh_foreachScreenVert(&vc, xsortvert_flag__doSetX, sortblock, 0); qsort(sortblock, amount, sizeof(xvertsort), vergxco); /* make temporal listbase */ tbase.first= tbase.last= 0; for (i=0; iverts, eve); BLI_addtail(&tbase, eve); } } addlisttolist(&vc.em->verts, &tbase); MEM_freeN(sortblock); } /* called from buttons */ void hashvert_flag(EditMesh *em, int flag) { /* switch vertex order using hash table */ EditVert *eve; struct xvertsort *sortblock, *sb, onth, *newsort; ListBase tbase; int amount, a, b; /* count */ eve= em->verts.first; amount= 0; while(eve) { if(eve->f & flag) amount++; eve= eve->next; } if(amount==0) return; /* allocate memory */ sb= sortblock= (struct xvertsort *)MEM_mallocN(sizeof(struct xvertsort)*amount,"sortremovedoub"); eve= em->verts.first; while(eve) { if(eve->f & flag) { sb->v1= eve; sb++; } eve= eve->next; } BLI_srand(1); sb= sortblock; for(a=0; a=0 && bv1; BLI_remlink(&em->verts, eve); BLI_addtail(&tbase, eve); sb++; } addlisttolist(&em->verts, &tbase); MEM_freeN(sortblock); } /* generic extern called extruder */ void extrude_mesh(Scene *scene, Object *obedit, EditMesh *em, wmOperator *op, short type) { float nor[3]= {0.0, 0.0, 0.0}; short transmode= 0; if(type<1) return; if(type==1) transmode= extrudeflag(obedit, em, SELECT, nor, 0); else if(type==4) transmode= extrudeflag_verts_indiv(em, SELECT, nor); else if(type==3) transmode= extrudeflag_edges_indiv(em, SELECT, nor); else transmode= extrudeflag_face_indiv(em, SELECT, nor); EM_stats_update(em); if(transmode==0) { BKE_report(op->reports, RPT_ERROR, "Not a valid selection for extrude"); } else { EM_fgon_flags(em); /* We need to force immediate calculation here because * transform may use derived objects (which are now stale). * * This shouldn't be necessary, derived queries should be * automatically building this data if invalid. Or something. */ DAG_id_flush_update(obedit->data, OB_RECALC_DATA); /* individual faces? */ // BIF_TransformSetUndo("Extrude"); if(type==2) { // initTransform(TFM_SHRINKFATTEN, CTX_NO_PET|CTX_NO_MIRROR); // Transform(); } else { // initTransform(TFM_TRANSLATION, CTX_NO_PET|CTX_NO_MIRROR); if(transmode=='n') { mul_m4_v3(obedit->obmat, nor); sub_v3_v3(nor, obedit->obmat[3]); // BIF_setSingleAxisConstraint(nor, "along normal"); } // Transform(); } } } // XXX should be a menu item static int mesh_extrude_invoke(bContext *C, wmOperator *op, wmEvent *event) { Scene *scene= CTX_data_scene(C); Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data); extrude_mesh(scene, obedit, em, op, RNA_int_get(op->ptr, "type")); BKE_mesh_end_editmesh(obedit->data, em); DAG_id_flush_update(obedit->data, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); return OPERATOR_FINISHED; } /* extrude without transform */ static int mesh_extrude_exec(bContext *C, wmOperator *op) { Scene *scene= CTX_data_scene(C); Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh(obedit->data); extrude_mesh(scene, obedit, em, op, RNA_int_get(op->ptr, "type")); DAG_id_flush_update(obedit->data, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); BKE_mesh_end_editmesh(obedit->data, em); return OPERATOR_FINISHED; } EnumPropertyItem extrude_items[] = { {1, "REGION", 0, "Region", ""}, {2, "FACES", 0, "Individual Faces", ""}, {3, "EDGES", 0, "Only Edges", ""}, {4, "VERTS", 0, "Only Vertices", ""}, {0, NULL, 0, NULL, NULL}}; static EnumPropertyItem *extrude_itemf(bContext *C, PointerRNA *ptr, int *free) { EnumPropertyItem *item= NULL; Object *obedit= CTX_data_edit_object(C); EditMesh *em; int totitem= 0; if(!obedit) return extrude_items; em = BKE_mesh_get_editmesh(obedit->data); EM_stats_update(em); if(em->selectmode & SCE_SELECT_VERTEX) { if(em->totvertsel==0) {} else if(em->totvertsel==1) { RNA_enum_item_add(&item, &totitem, &extrude_items[3]); } else if(em->totedgesel==0) { RNA_enum_item_add(&item, &totitem, &extrude_items[3]); } else if(em->totfacesel==0) { RNA_enum_item_add(&item, &totitem, &extrude_items[2]); RNA_enum_item_add(&item, &totitem, &extrude_items[3]); } else if(em->totfacesel==1) { RNA_enum_item_add(&item, &totitem, &extrude_items[0]); RNA_enum_item_add(&item, &totitem, &extrude_items[2]); RNA_enum_item_add(&item, &totitem, &extrude_items[3]); } else { RNA_enum_item_add(&item, &totitem, &extrude_items[0]); RNA_enum_item_add(&item, &totitem, &extrude_items[1]); RNA_enum_item_add(&item, &totitem, &extrude_items[2]); RNA_enum_item_add(&item, &totitem, &extrude_items[3]); } } else if(em->selectmode & SCE_SELECT_EDGE) { if (em->totedgesel==0) {} else if (em->totedgesel==1) { RNA_enum_item_add(&item, &totitem, &extrude_items[2]); } else if(em->totfacesel==0) { RNA_enum_item_add(&item, &totitem, &extrude_items[2]); } else if(em->totfacesel==1) { RNA_enum_item_add(&item, &totitem, &extrude_items[0]); RNA_enum_item_add(&item, &totitem, &extrude_items[2]); } else { RNA_enum_item_add(&item, &totitem, &extrude_items[0]); RNA_enum_item_add(&item, &totitem, &extrude_items[1]); RNA_enum_item_add(&item, &totitem, &extrude_items[2]); } } else { if (em->totfacesel == 0) {} else if (em->totfacesel == 1) { RNA_enum_item_add(&item, &totitem, &extrude_items[0]); } else { RNA_enum_item_add(&item, &totitem, &extrude_items[0]); RNA_enum_item_add(&item, &totitem, &extrude_items[1]); } } if(item) { RNA_enum_item_end(&item, &totitem); *free= 1; return item; } else { return NULL; } } void MESH_OT_extrude(wmOperatorType *ot) { PropertyRNA *prop; /* identifiers */ ot->name= "Extrude"; ot->description= "Extrude selected vertices, edges or faces"; ot->idname= "MESH_OT_extrude"; /* api callbacks */ ot->invoke= mesh_extrude_invoke; ot->exec= mesh_extrude_exec; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; /* properties */ prop= RNA_def_enum(ot->srna, "type", extrude_items, 0, "Type", ""); RNA_def_property_flag(prop, PROP_HIDDEN); RNA_def_enum_funcs(prop, extrude_itemf); ot->prop= prop; } static int split_mesh(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data); WM_cursor_wait(1); /* make duplicate first */ adduplicateflag(em, SELECT); /* old faces have flag 128 set, delete them */ delfaceflag(em, 128); recalc_editnormals(em); WM_cursor_wait(0); DAG_id_flush_update(obedit->data, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); BKE_mesh_end_editmesh(obedit->data, em); return OPERATOR_FINISHED; } void MESH_OT_split(wmOperatorType *ot) { /* identifiers */ ot->name= "Split"; ot->description= "Split selected geometry into separate disconnected mesh"; ot->idname= "MESH_OT_split"; /* api callbacks */ ot->exec= split_mesh; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; } static int extrude_repeat_mesh(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data); RegionView3D *rv3d = ED_view3d_context_rv3d(C); int steps = RNA_int_get(op->ptr,"steps"); float offs = RNA_float_get(op->ptr,"offset"); float dvec[3], tmat[3][3], bmat[3][3], nor[3]= {0.0, 0.0, 0.0}; short a; /* dvec */ dvec[0]= rv3d->persinv[2][0]; dvec[1]= rv3d->persinv[2][1]; dvec[2]= rv3d->persinv[2][2]; normalize_v3(dvec); dvec[0]*= offs; dvec[1]*= offs; dvec[2]*= offs; /* base correction */ copy_m3_m4(bmat, obedit->obmat); invert_m3_m3(tmat, bmat); mul_m3_v3(tmat, dvec); for(a=0; adata, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); BKE_mesh_end_editmesh(obedit->data, em); return OPERATOR_FINISHED; } void MESH_OT_extrude_repeat(wmOperatorType *ot) { /* identifiers */ ot->name= "Extrude Repeat Mesh"; ot->description= "Extrude selected vertices, edges or faces repeatedly"; ot->idname= "MESH_OT_extrude_repeat"; /* api callbacks */ ot->exec= extrude_repeat_mesh; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; /* props */ RNA_def_float(ot->srna, "offset", 2.0f, 0.0f, 100.0f, "Offset", "", 0.0f, FLT_MAX); RNA_def_int(ot->srna, "steps", 10, 0, 180, "Steps", "", 0, INT_MAX); } /* ************************** spin operator ******************** */ static int spin_mesh(bContext *C, wmOperator *op, float *dvec, int steps, float degr, int dupli ) { Object *obedit= CTX_data_edit_object(C); ToolSettings *ts= CTX_data_tool_settings(C); EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data); EditVert *eve,*nextve; float nor[3]= {0.0f, 0.0f, 0.0f}; float si, n[3], q[4], cmat[3][3], imat[3][3], tmat[3][3]; float cent[3], bmat[3][3]; float phi; short a, ok= 1; RNA_float_get_array(op->ptr, "center", cent); /* imat and center and size */ copy_m3_m4(bmat, obedit->obmat); invert_m3_m3(imat,bmat); cent[0]-= obedit->obmat[3][0]; cent[1]-= obedit->obmat[3][1]; cent[2]-= obedit->obmat[3][2]; mul_m3_v3(imat, cent); phi= degr*M_PI/360.0; phi/= steps; if(ts->editbutflag & B_CLOCKWISE) phi= -phi; RNA_float_get_array(op->ptr, "axis", n); normalize_v3(n); q[0]= (float)cos(phi); si= (float)sin(phi); q[1]= n[0]*si; q[2]= n[1]*si; q[3]= n[2]*si; quat_to_mat3( cmat,q); mul_m3_m3m3(tmat,cmat,bmat); mul_m3_m3m3(bmat,imat,tmat); if(dupli==0) if(ts->editbutflag & B_KEEPORIG) adduplicateflag(em, 1); for(a=0; averts.first; while(eve) { nextve= eve->next; if(eve->f & SELECT) { BLI_remlink(&em->verts,eve); free_editvert(em, eve); } eve= nextve; } } else { recalc_editnormals(em); EM_fgon_flags(em); DAG_id_flush_update(obedit->data, OB_RECALC_DATA); } BKE_mesh_end_editmesh(obedit->data, em); return ok; } static int spin_mesh_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); int ok; ok= spin_mesh(C, op, NULL, RNA_int_get(op->ptr,"steps"), RNA_float_get(op->ptr,"degrees"), RNA_boolean_get(op->ptr,"dupli")); if(ok==0) { BKE_report(op->reports, RPT_ERROR, "No valid vertices are selected"); return OPERATOR_CANCELLED; } DAG_id_flush_update(obedit->data, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); return OPERATOR_FINISHED; } /* get center and axis, in global coords */ static int spin_mesh_invoke(bContext *C, wmOperator *op, wmEvent *event) { Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); RegionView3D *rv3d= ED_view3d_context_rv3d(C); RNA_float_set_array(op->ptr, "center", give_cursor(scene, v3d)); RNA_float_set_array(op->ptr, "axis", rv3d->viewinv[2]); return spin_mesh_exec(C, op); } void MESH_OT_spin(wmOperatorType *ot) { /* identifiers */ ot->name= "Spin"; ot->description= "Extrude selected vertices in a circle around the cursor in indicated viewport"; ot->idname= "MESH_OT_spin"; /* api callbacks */ ot->invoke= spin_mesh_invoke; ot->exec= spin_mesh_exec; ot->poll= EM_view3d_poll; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; /* props */ RNA_def_int(ot->srna, "steps", 9, 0, INT_MAX, "Steps", "Steps", 0, 128); RNA_def_boolean(ot->srna, "dupli", 0, "Dupli", "Make Duplicates"); RNA_def_float(ot->srna, "degrees", 90.0f, -FLT_MAX, FLT_MAX, "Degrees", "Degrees", -360.0f, 360.0f); RNA_def_float_vector_xyz(ot->srna, "center", 3, NULL, -FLT_MAX, FLT_MAX, "Center", "Center in global view space", -FLT_MAX, FLT_MAX); RNA_def_float_vector(ot->srna, "axis", 3, NULL, -1.0f, 1.0f, "Axis", "Axis in global view space", -FLT_MAX, FLT_MAX); } static int screw_mesh_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data); EditVert *eve,*v1=0,*v2=0; EditEdge *eed; float dvec[3], nor[3]; int steps, turns; turns= RNA_int_get(op->ptr, "turns"); steps= RNA_int_get(op->ptr, "steps"); /* clear flags */ for(eve= em->verts.first; eve; eve= eve->next) eve->f1= 0; /* edges set flags in verts */ for(eed= em->edges.first; eed; eed= eed->next) { if(eed->v1->f & SELECT) { if(eed->v2->f & SELECT) { /* watch: f1 is a byte */ if(eed->v1->f1<2) eed->v1->f1++; if(eed->v2->f1<2) eed->v2->f1++; } } } /* find two vertices with eve->f1==1, more or less is wrong */ for(eve= em->verts.first; eve; eve= eve->next) { if(eve->f1==1) { if(v1==NULL) v1= eve; else if(v2==NULL) v2= eve; else { v1= NULL; break; } } } if(v1==NULL || v2==NULL) { BKE_report(op->reports, RPT_ERROR, "You have to select a string of connected vertices too"); BKE_mesh_end_editmesh(obedit->data, em); return OPERATOR_CANCELLED; } /* calculate dvec */ dvec[0]= ( v1->co[0]- v2->co[0] )/steps; dvec[1]= ( v1->co[1]- v2->co[1] )/steps; dvec[2]= ( v1->co[2]- v2->co[2] )/steps; VECCOPY(nor, obedit->obmat[2]); if(nor[0]*dvec[0]+nor[1]*dvec[1]+nor[2]*dvec[2]>0.000) { dvec[0]= -dvec[0]; dvec[1]= -dvec[1]; dvec[2]= -dvec[2]; } if(spin_mesh(C, op, dvec, turns*steps, 360.0f*turns, 0)) { DAG_id_flush_update(obedit->data, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); BKE_mesh_end_editmesh(obedit->data, em); return OPERATOR_FINISHED; } else { BKE_report(op->reports, RPT_ERROR, "No valid vertices are selected"); BKE_mesh_end_editmesh(obedit->data, em); return OPERATOR_CANCELLED; } } /* get center and axis, in global coords */ static int screw_mesh_invoke(bContext *C, wmOperator *op, wmEvent *event) { Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); RegionView3D *rv3d= ED_view3d_context_rv3d(C); RNA_float_set_array(op->ptr, "center", give_cursor(scene, v3d)); RNA_float_set_array(op->ptr, "axis", rv3d->viewinv[1]); return screw_mesh_exec(C, op); } void MESH_OT_screw(wmOperatorType *ot) { /* identifiers */ ot->name= "Screw"; ot->description= "Extrude selected vertices in screw-shaped rotation around the cursor in indicated viewport"; ot->idname= "MESH_OT_screw"; /* api callbacks */ ot->invoke= screw_mesh_invoke; ot->exec= screw_mesh_exec; ot->poll= EM_view3d_poll; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; /*props */ RNA_def_int(ot->srna, "steps", 9, 0, INT_MAX, "Steps", "Steps", 0, 256); RNA_def_int(ot->srna, "turns", 1, 0, INT_MAX, "Turns", "Turns", 0, 256); RNA_def_float_vector_xyz(ot->srna, "center", 3, NULL, -FLT_MAX, FLT_MAX, "Center", "Center in global view space", -FLT_MAX, FLT_MAX); RNA_def_float_vector(ot->srna, "axis", 3, NULL, -1.0f, 1.0f, "Axis", "Axis in global view space", -FLT_MAX, FLT_MAX); } static void erase_edges(EditMesh *em, ListBase *l) { EditEdge *ed, *nexted; ed = (EditEdge *) l->first; while(ed) { nexted= ed->next; if( (ed->v1->f & SELECT) || (ed->v2->f & SELECT) ) { remedge(em, ed); free_editedge(em, ed); } ed= nexted; } } static void erase_faces(EditMesh *em, ListBase *l) { EditFace *f, *nextf; f = (EditFace *) l->first; while(f) { nextf= f->next; if( faceselectedOR(f, SELECT) ) { BLI_remlink(l, f); free_editface(em, f); } f = nextf; } } static void erase_vertices(EditMesh *em, ListBase *l) { EditVert *v, *nextv; v = (EditVert *) l->first; while(v) { nextv= v->next; if(v->f & 1) { BLI_remlink(l, v); free_editvert(em, v); } v = nextv; } } void delete_mesh(Object *obedit, EditMesh *em, wmOperator *op, int event) { EditFace *efa, *nextvl; EditVert *eve,*nextve; EditEdge *eed,*nexted; int count; char *str="Erase"; if(event<1) return; if(event==10 ) { str= "Erase Vertices"; erase_edges(em, &em->edges); erase_faces(em, &em->faces); erase_vertices(em, &em->verts); } else if(event==6) { if(!EdgeLoopDelete(em, op)) return; str= "Erase Edge Loop"; } else if(event==4) { str= "Erase Edges & Faces"; efa= em->faces.first; while(efa) { nextvl= efa->next; /* delete only faces with 1 or more edges selected */ count= 0; if(efa->e1->f & SELECT) count++; if(efa->e2->f & SELECT) count++; if(efa->e3->f & SELECT) count++; if(efa->e4 && (efa->e4->f & SELECT)) count++; if(count) { BLI_remlink(&em->faces, efa); free_editface(em, efa); } efa= nextvl; } eed= em->edges.first; while(eed) { nexted= eed->next; if(eed->f & SELECT) { remedge(em, eed); free_editedge(em, eed); } eed= nexted; } efa= em->faces.first; while(efa) { nextvl= efa->next; event=0; if( efa->v1->f & SELECT) event++; if( efa->v2->f & SELECT) event++; if( efa->v3->f & SELECT) event++; if(efa->v4 && (efa->v4->f & SELECT)) event++; if(event>1) { BLI_remlink(&em->faces, efa); free_editface(em, efa); } efa= nextvl; } } else if(event==1) { str= "Erase Edges"; // faces first efa= em->faces.first; while(efa) { nextvl= efa->next; event=0; if( efa->e1->f & SELECT) event++; if( efa->e2->f & SELECT) event++; if( efa->e3->f & SELECT) event++; if(efa->e4 && (efa->e4->f & SELECT)) event++; if(event) { BLI_remlink(&em->faces, efa); free_editface(em, efa); } efa= nextvl; } eed= em->edges.first; while(eed) { nexted= eed->next; if(eed->f & SELECT) { remedge(em, eed); free_editedge(em, eed); } eed= nexted; } /* to remove loose vertices: */ eed= em->edges.first; while(eed) { if( eed->v1->f & SELECT) eed->v1->f-=SELECT; if( eed->v2->f & SELECT) eed->v2->f-=SELECT; eed= eed->next; } eve= em->verts.first; while(eve) { nextve= eve->next; if(eve->f & SELECT) { BLI_remlink(&em->verts,eve); free_editvert(em, eve); } eve= nextve; } } else if(event==2) { str="Erase Faces"; delfaceflag(em, SELECT); } else if(event==3) { str= "Erase All"; if(em->verts.first) free_vertlist(em, &em->verts); if(em->edges.first) free_edgelist(em, &em->edges); if(em->faces.first) free_facelist(em, &em->faces); if(em->selected.first) BLI_freelistN(&(em->selected)); } else if(event==5) { str= "Erase Only Faces"; efa= em->faces.first; while(efa) { nextvl= efa->next; if(efa->f & SELECT) { BLI_remlink(&em->faces, efa); free_editface(em, efa); } efa= nextvl; } } EM_fgon_flags(em); // redo flags and indices for fgons } /* Note, these values must match delete_mesh() event values */ static EnumPropertyItem prop_mesh_delete_types[] = { {10,"VERT", 0, "Vertices", ""}, {1, "EDGE", 0, "Edges", ""}, {2, "FACE", 0, "Faces", ""}, {3, "ALL", 0, "All", ""}, {4, "EDGE_FACE",0, "Edges & Faces", ""}, {5, "ONLY_FACE",0, "Only Faces", ""}, {6, "EDGE_LOOP",0, "Edge Loop", ""}, {0, NULL, 0, NULL, NULL} }; static int delete_mesh_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data); int type= RNA_enum_get(op->ptr, "type"); if(type==6) return WM_operator_name_call(C, "MESH_OT_delete_edgeloop", WM_OP_EXEC_DEFAULT, NULL); delete_mesh(obedit, em, op, type); DAG_id_flush_update(obedit->data, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); BKE_mesh_end_editmesh(obedit->data, em); return OPERATOR_FINISHED; } void MESH_OT_delete(wmOperatorType *ot) { /* identifiers */ ot->name= "Delete"; ot->description= "Delete selected vertices, edges or faces"; ot->idname= "MESH_OT_delete"; /* api callbacks */ ot->invoke= WM_menu_invoke; ot->exec= delete_mesh_exec; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; /*props */ ot->prop= RNA_def_enum(ot->srna, "type", prop_mesh_delete_types, 10, "Type", "Method used for deleting mesh data"); } /*GB*/ /*-------------------------------------------------------------------------------*/ /*--------------------------- Edge Based Subdivide ------------------------------*/ #define EDGENEW 2 #define FACENEW 2 #define EDGEINNER 4 #define EDGEOLD 8 /*used by faceloop cut to select only edges valid for edge slide*/ #define DOUBLEOPFILL 16 /* calculates offset for co, based on fractal, sphere or smooth settings */ static void alter_co(float *co, EditEdge *edge, float smooth, float fractal, int beauty, float perc) { float vec1[3], fac; if(beauty & B_SMOOTH) { /* we calculate an offset vector vec1[], to be added to *co */ float len, fac, nor[3], nor1[3], nor2[3]; sub_v3_v3v3(nor, edge->v1->co, edge->v2->co); len= 0.5f*normalize_v3(nor); VECCOPY(nor1, edge->v1->no); VECCOPY(nor2, edge->v2->no); /* cosine angle */ fac= nor[0]*nor1[0] + nor[1]*nor1[1] + nor[2]*nor1[2] ; vec1[0]= fac*nor1[0]; vec1[1]= fac*nor1[1]; vec1[2]= fac*nor1[2]; /* cosine angle */ fac= -nor[0]*nor2[0] - nor[1]*nor2[1] - nor[2]*nor2[2] ; vec1[0]+= fac*nor2[0]; vec1[1]+= fac*nor2[1]; vec1[2]+= fac*nor2[2]; /* falloff for multi subdivide */ smooth *= sqrt(fabs(1.0f - 2.0f*fabs(0.5f-perc))); vec1[0]*= smooth*len; vec1[1]*= smooth*len; vec1[2]*= smooth*len; co[0] += vec1[0]; co[1] += vec1[1]; co[2] += vec1[2]; } else if(beauty & B_SPHERE) { /* subdivide sphere */ normalize_v3(co); co[0]*= smooth; co[1]*= smooth; co[2]*= smooth; } if(beauty & B_FRACTAL) { fac= fractal*len_v3v3(edge->v1->co, edge->v2->co); vec1[0]= fac*(float)(0.5-BLI_drand()); vec1[1]= fac*(float)(0.5-BLI_drand()); vec1[2]= fac*(float)(0.5-BLI_drand()); add_v3_v3(co, vec1); } } /* assumes in the edge is the correct interpolated vertices already */ /* percent defines the interpolation, smooth, fractal and beauty are for special options */ /* results in new vertex with correct coordinate, vertex normal and weight group info */ static EditVert *subdivide_edge_addvert(EditMesh *em, EditEdge *edge, float smooth, float fractal, int beauty, float percent) { EditVert *ev; float co[3]; co[0] = (edge->v2->co[0]-edge->v1->co[0])*percent + edge->v1->co[0]; co[1] = (edge->v2->co[1]-edge->v1->co[1])*percent + edge->v1->co[1]; co[2] = (edge->v2->co[2]-edge->v1->co[2])*percent + edge->v1->co[2]; /* offset for smooth or sphere or fractal */ alter_co(co, edge, smooth, fractal, beauty, percent); /* clip if needed by mirror modifier */ if (edge->v1->f2) { if ( edge->v1->f2 & edge->v2->f2 & 1) { co[0]= 0.0f; } if ( edge->v1->f2 & edge->v2->f2 & 2) { co[1]= 0.0f; } if ( edge->v1->f2 & edge->v2->f2 & 4) { co[2]= 0.0f; } } ev = addvertlist(em, co, NULL); /* vert data (vgroups, ..) */ EM_data_interp_from_verts(em, edge->v1, edge->v2, ev, percent); /* normal */ ev->no[0] = (edge->v2->no[0]-edge->v1->no[0])*percent + edge->v1->no[0]; ev->no[1] = (edge->v2->no[1]-edge->v1->no[1])*percent + edge->v1->no[1]; ev->no[2] = (edge->v2->no[2]-edge->v1->no[2])*percent + edge->v1->no[2]; normalize_v3(ev->no); return ev; } static void flipvertarray(EditVert** arr, short size) { EditVert *hold; int i; for(i=0; iv1->co, *v2 = source->v2->co, *v3 = source->v3->co; float *v4 = source->v4? source->v4->co: NULL; float w[4][4]; CustomData_em_copy_data(&em->fdata, &em->fdata, source->data, &target->data); target->mat_nr = source->mat_nr; target->flag = source->flag; target->h = source->h; interp_weights_face_v3( w[0],v1, v2, v3, v4, target->v1->co); interp_weights_face_v3( w[1],v1, v2, v3, v4, target->v2->co); interp_weights_face_v3( w[2],v1, v2, v3, v4, target->v3->co); if (target->v4) interp_weights_face_v3( w[3],v1, v2, v3, v4, target->v4->co); CustomData_em_interp(&em->fdata, &source->data, NULL, (float*)w, 1, target->data); } static void fill_quad_single(EditMesh *em, EditFace *efa, struct GHash *gh, int numcuts, int seltype) { EditEdge *cedge=NULL; EditVert *v[4], **verts; EditFace *hold; short start=0, end, left, right, vertsize,i; v[0] = efa->v1; v[1] = efa->v2; v[2] = efa->v3; v[3] = efa->v4; if(efa->e1->f & SELECT) { cedge = efa->e1; start = 0;} else if(efa->e2->f & SELECT) { cedge = efa->e2; start = 1;} else if(efa->e3->f & SELECT) { cedge = efa->e3; start = 2;} else if(efa->e4->f & SELECT) { cedge = efa->e4; start = 3;} // Point verts to the array of new verts for cedge verts = BLI_ghash_lookup(gh, cedge); //This is the index size of the verts array vertsize = numcuts+2; // Is the original v1 the same as the first vert on the selected edge? // if not, the edge is running the opposite direction in this face so flip // the array to the correct direction if(verts[0] != v[start]) {flipvertarray(verts,numcuts+2);} end = (start+1)%4; left = (start+2)%4; right = (start+3)%4; /* We should have something like this now end start 3 2 1 0 |---*---*---| | | | | | | ------------- left right where start,end,left, right are indexes of EditFace->v1, etc (stored in v) and 0,1,2... are the indexes of the new verts stored in verts We will fill this case like this or this depending on even or odd cuts |---*---*---| |---*---| | / \ | | / \ | | / \ | | / \ | |/ \| |/ \| ------------- --------- */ // Make center face if(vertsize % 2 == 0) { hold = addfacelist(em, verts[(vertsize-1)/2],verts[((vertsize-1)/2)+1],v[left],v[right], NULL,NULL); hold->e2->f2 |= EDGEINNER; hold->e4->f2 |= EDGEINNER; }else{ hold = addfacelist(em, verts[(vertsize-1)/2],v[left],v[right],NULL, NULL,NULL); hold->e1->f2 |= EDGEINNER; hold->e3->f2 |= EDGEINNER; } facecopy(em, efa,hold); // Make side faces for(i=0;i<(vertsize-1)/2;i++) { hold = addfacelist(em, verts[i],verts[i+1],v[right],NULL,NULL,NULL); facecopy(em, efa,hold); if(i+1 != (vertsize-1)/2) { if(seltype == SUBDIV_SELECT_INNER) { hold->e2->f2 |= EDGEINNER; } } hold = addfacelist(em, verts[vertsize-2-i],verts[vertsize-1-i],v[left],NULL,NULL,NULL); facecopy(em, efa,hold); if(i+1 != (vertsize-1)/2) { if(seltype == SUBDIV_SELECT_INNER) { hold->e3->f2 |= EDGEINNER; } } } } static void fill_tri_single(EditMesh *em, EditFace *efa, struct GHash *gh, int numcuts, int seltype) { EditEdge *cedge=NULL; EditVert *v[3], **verts; EditFace *hold; short start=0, end, op, vertsize,i; v[0] = efa->v1; v[1] = efa->v2; v[2] = efa->v3; if(efa->e1->f & SELECT) { cedge = efa->e1; start = 0;} else if(efa->e2->f & SELECT) { cedge = efa->e2; start = 1;} else if(efa->e3->f & SELECT) { cedge = efa->e3; start = 2;} // Point verts to the array of new verts for cedge verts = BLI_ghash_lookup(gh, cedge); //This is the index size of the verts array vertsize = numcuts+2; // Is the original v1 the same as the first vert on the selected edge? // if not, the edge is running the opposite direction in this face so flip // the array to the correct direction if(verts[0] != v[start]) {flipvertarray(verts,numcuts+2);} end = (start+1)%3; op = (start+2)%3; /* We should have something like this now end start 3 2 1 0 |---*---*---| \ | \ | \ | \ | \ | \ | |op where start,end,op are indexes of EditFace->v1, etc (stored in v) and 0,1,2... are the indexes of the new verts stored in verts We will fill this case like this or this depending on even or odd cuts 3 2 1 0 |---*---*---| \ \ \ | \ \ \ | \ \ \ | \ \ \| \ \\| \ | |op */ // Make side faces for(i=0;i<(vertsize-1);i++) { hold = addfacelist(em, verts[i],verts[i+1],v[op],NULL,NULL,NULL); if(i+1 != vertsize-1) { if(seltype == SUBDIV_SELECT_INNER) { hold->e2->f2 |= EDGEINNER; } } facecopy(em, efa,hold); } } static void fill_quad_double_op(EditMesh *em, EditFace *efa, struct GHash *gh, int numcuts) { EditEdge *cedge[2]={NULL, NULL}; EditVert *v[4], **verts[2]; EditFace *hold; short start=0, end, left, right, vertsize,i; v[0] = efa->v1; v[1] = efa->v2; v[2] = efa->v3; v[3] = efa->v4; if(efa->e1->f & SELECT) { cedge[0] = efa->e1; cedge[1] = efa->e3; start = 0;} else if(efa->e2->f & SELECT) { cedge[0] = efa->e2; cedge[1] = efa->e4; start = 1;} // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] verts[0] = BLI_ghash_lookup(gh, cedge[0]); verts[1] = BLI_ghash_lookup(gh, cedge[1]); //This is the index size of the verts array vertsize = numcuts+2; // Is the original v1 the same as the first vert on the selected edge? // if not, the edge is running the opposite direction in this face so flip // the array to the correct direction if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);} end = (start+1)%4; left = (start+2)%4; right = (start+3)%4; if(verts[1][0] != v[left]) {flipvertarray(verts[1],numcuts+2);} /* We should have something like this now end start 3 2 1 0 |---*---*---| | | | | | | |---*---*---| 0 1 2 3 left right We will fill this case like this or this depending on even or odd cuts |---*---*---| | | | | | | | | | | | | |---*---*---| */ // Make side faces for(i=0;ie2->f2 |= EDGEINNER; hold->e2->f2 |= DOUBLEOPFILL; } facecopy(em, efa,hold); } } static void fill_quad_double_adj_path(EditMesh *em, EditFace *efa, struct GHash *gh, int numcuts) { EditEdge *cedge[2]={NULL, NULL}; EditVert *v[4], **verts[2]; EditFace *hold; short start=0, start2=0, vertsize,i; int ctrl= 0; // XXX v[0] = efa->v1; v[1] = efa->v2; v[2] = efa->v3; v[3] = efa->v4; if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1;} if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2;} if(efa->e3->f & SELECT && efa->e4->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e4; start = 2; start2 = 3;} if(efa->e4->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e4; cedge[1] = efa->e1; start = 3; start2 = 0;} // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] verts[0] = BLI_ghash_lookup(gh, cedge[0]); verts[1] = BLI_ghash_lookup(gh, cedge[1]); //This is the index size of the verts array vertsize = numcuts+2; // Is the original v1 the same as the first vert on the selected edge? // if not, the edge is running the opposite direction in this face so flip // the array to the correct direction if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);} if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);} /* We should have something like this now end start 3 2 1 0 start2 0|---*---*---| | | 1* | | | 2* | | | end2 3|-----------| We will fill this case like this or this depending on even or odd cuts |---*---*---| | / / / | * / / | | / / | * / | | / | |-----------| */ // Make outside tris hold = addfacelist(em, verts[0][vertsize-2],verts[0][vertsize-1],verts[1][1],NULL,NULL,NULL); /* when ctrl is depressed, only want verts on the cutline selected */ if (ctrl) hold->e3->f2 |= EDGEINNER; facecopy(em, efa,hold); hold = addfacelist(em, verts[0][0],verts[1][vertsize-1],v[(start2+2)%4],NULL,NULL,NULL); /* when ctrl is depressed, only want verts on the cutline selected */ if (ctrl) hold->e1->f2 |= EDGEINNER; facecopy(em, efa,hold); //if(scene->toolsettings->editbutflag & B_AUTOFGON) { // hold->e1->h |= EM_FGON; //} // Make side faces for(i=0;ie2->f2 |= EDGEINNER; facecopy(em, efa,hold); } //EM_fgon_flags(em); } static void fill_quad_double_adj_fan(EditMesh *em, EditFace *efa, struct GHash *gh, int numcuts) { EditEdge *cedge[2]={NULL, NULL}; EditVert *v[4], *op=NULL, **verts[2]; EditFace *hold; short start=0, start2=0, vertsize,i; v[0] = efa->v1; v[1] = efa->v2; v[2] = efa->v3; v[3] = efa->v4; if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1; op = efa->v4;} if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2; op = efa->v1;} if(efa->e3->f & SELECT && efa->e4->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e4; start = 2; start2 = 3; op = efa->v2;} if(efa->e4->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e4; cedge[1] = efa->e1; start = 3; start2 = 0; op = efa->v3;} // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] verts[0] = BLI_ghash_lookup(gh, cedge[0]); verts[1] = BLI_ghash_lookup(gh, cedge[1]); //This is the index size of the verts array vertsize = numcuts+2; // Is the original v1 the same as the first vert on the selected edge? // if not, the edge is running the opposite direction in this face so flip // the array to the correct direction if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);} if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);} /* We should have something like this now end start 3 2 1 0 start2 0|---*---*---| | | 1* | | | 2* | | | end2 3|-----------|op We will fill this case like this or this (warning horrible ascii art follows) |---*---*---| | \ \ \ | *---\ \ \ | | \ \ \ \| *---- \ \ \ | | --- \\\| |-----------| */ for(i=0;i<=numcuts;i++) { hold = addfacelist(em, op,verts[1][numcuts-i],verts[1][numcuts-i+1],NULL,NULL,NULL); hold->e1->f2 |= EDGEINNER; facecopy(em, efa,hold); hold = addfacelist(em, op,verts[0][i],verts[0][i+1],NULL,NULL,NULL); hold->e3->f2 |= EDGEINNER; facecopy(em, efa,hold); } } static void fill_quad_double_adj_inner(EditMesh *em, EditFace *efa, struct GHash *gh, int numcuts) { EditEdge *cedge[2]={NULL, NULL}; EditVert *v[4], *op=NULL, **verts[2],**inner; EditFace *hold; short start=0, start2=0, vertsize,i; float co[3]; v[0] = efa->v1; v[1] = efa->v2; v[2] = efa->v3; v[3] = efa->v4; if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1; op = efa->v4;} if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2; op = efa->v1;} if(efa->e3->f & SELECT && efa->e4->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e4; start = 2; start2 = 3; op = efa->v2;} if(efa->e4->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e4; cedge[1] = efa->e1; start = 3; start2 = 0; op = efa->v3;} // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] verts[0] = BLI_ghash_lookup(gh, cedge[0]); verts[1] = BLI_ghash_lookup(gh, cedge[1]); //This is the index size of the verts array vertsize = numcuts+2; // Is the original v1 the same as the first vert on the selected edge? // if not, the edge is running the opposite direction in this face so flip // the array to the correct direction if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);} if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);} /* We should have something like this now end start 3 2 1 0 start2 0|---*---*---| | | 1* | | | 2* | | | end2 3|-----------|op We will fill this case like this or this (warning horrible ascii art follows) |---*-----*---| | * / | * \ / | | * | | / \ | * \ | | \ | |-------------| */ // Add Inner Vert(s) inner = MEM_mallocN(sizeof(EditVert*)*numcuts,"New inner verts"); for(i=0;ico[0] + verts[1][i+1]->co[0] ) / 2 ; co[1] = (verts[0][numcuts-i]->co[1] + verts[1][i+1]->co[1] ) / 2 ; co[2] = (verts[0][numcuts-i]->co[2] + verts[1][i+1]->co[2] ) / 2 ; inner[i] = addvertlist(em, co, NULL); inner[i]->f2 |= EDGEINNER; EM_data_interp_from_verts(em, verts[0][numcuts-i], verts[1][i+1], inner[i], 0.5f); } // Add Corner Quad hold = addfacelist(em, verts[0][numcuts+1],verts[1][1],inner[0],verts[0][numcuts],NULL,NULL); hold->e2->f2 |= EDGEINNER; hold->e3->f2 |= EDGEINNER; facecopy(em, efa,hold); // Add Bottom Quads hold = addfacelist(em, verts[0][0],verts[0][1],inner[numcuts-1],op,NULL,NULL); hold->e2->f2 |= EDGEINNER; facecopy(em, efa,hold); hold = addfacelist(em, op,inner[numcuts-1],verts[1][numcuts],verts[1][numcuts+1],NULL,NULL); hold->e2->f2 |= EDGEINNER; facecopy(em, efa,hold); //if(scene->toolsettings->editbutflag & B_AUTOFGON) { // hold->e1->h |= EM_FGON; //} // Add Fill Quads (if # cuts > 1) for(i=0;ie1->f2 |= EDGEINNER; hold->e3->f2 |= EDGEINNER; facecopy(em, efa,hold); hold = addfacelist(em, inner[i],inner[i+1],verts[0][numcuts-1-i],verts[0][numcuts-i],NULL,NULL); hold->e2->f2 |= EDGEINNER; hold->e4->f2 |= EDGEINNER; facecopy(em, efa,hold); //if(scene->toolsettings->editbutflag & B_AUTOFGON) { // hold->e1->h |= EM_FGON; //} } //EM_fgon_flags(em); MEM_freeN(inner); } static void fill_tri_double(EditMesh *em, EditFace *efa, struct GHash *gh, int numcuts) { EditEdge *cedge[2]={NULL, NULL}; EditVert *v[3], **verts[2]; EditFace *hold; short start=0, start2=0, vertsize,i; v[0] = efa->v1; v[1] = efa->v2; v[2] = efa->v3; if(efa->e1->f & SELECT && efa->e2->f & SELECT) {cedge[0] = efa->e1; cedge[1] = efa->e2; start = 0; start2 = 1;} if(efa->e2->f & SELECT && efa->e3->f & SELECT) {cedge[0] = efa->e2; cedge[1] = efa->e3; start = 1; start2 = 2;} if(efa->e3->f & SELECT && efa->e1->f & SELECT) {cedge[0] = efa->e3; cedge[1] = efa->e1; start = 2; start2 = 0;} // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] verts[0] = BLI_ghash_lookup(gh, cedge[0]); verts[1] = BLI_ghash_lookup(gh, cedge[1]); //This is the index size of the verts array vertsize = numcuts+2; // Is the original v1 the same as the first vert on the selected edge? // if not, the edge is running the opposite direction in this face so flip // the array to the correct direction if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);} if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);} /* We should have something like this now end start 3 2 1 0 start2 0|---*---*---| | / 1* / | / 2* / | / end2 3| We will fill this case like this or this depending on even or odd cuts |---*---*---| | / / / * / / | / / * / | / | */ // Make outside tri hold = addfacelist(em, verts[0][vertsize-2],verts[0][vertsize-1],verts[1][1],NULL,NULL,NULL); hold->e3->f2 |= EDGEINNER; facecopy(em, efa,hold); // Make side faces for(i=0;ie2->f2 |= EDGEINNER; facecopy(em, efa,hold); } } static void fill_quad_triple(EditMesh *em, EditFace *efa, struct GHash *gh, int numcuts) { EditEdge *cedge[3]={0}; EditVert *v[4], **verts[3]; EditFace *hold; short start=0, start2=0, start3=0, vertsize, i, repeats; v[0] = efa->v1; v[1] = efa->v2; v[2] = efa->v3; v[3] = efa->v4; if(!(efa->e1->f & SELECT)) { cedge[0] = efa->e2; cedge[1] = efa->e3; cedge[2] = efa->e4; start = 1;start2 = 2;start3 = 3; } if(!(efa->e2->f & SELECT)) { cedge[0] = efa->e3; cedge[1] = efa->e4; cedge[2] = efa->e1; start = 2;start2 = 3;start3 = 0; } if(!(efa->e3->f & SELECT)) { cedge[0] = efa->e4; cedge[1] = efa->e1; cedge[2] = efa->e2; start = 3;start2 = 0;start3 = 1; } if(!(efa->e4->f & SELECT)) { cedge[0] = efa->e1; cedge[1] = efa->e2; cedge[2] = efa->e3; start = 0;start2 = 1;start3 = 2; } // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] verts[0] = BLI_ghash_lookup(gh, cedge[0]); verts[1] = BLI_ghash_lookup(gh, cedge[1]); verts[2] = BLI_ghash_lookup(gh, cedge[2]); //This is the index size of the verts array vertsize = numcuts+2; // Is the original v1 the same as the first vert on the selected edge? // if not, the edge is running the opposite direction in this face so flip // the array to the correct direction if(verts[0][0] != v[start]) {flipvertarray(verts[0],numcuts+2);} if(verts[1][0] != v[start2]) {flipvertarray(verts[1],numcuts+2);} if(verts[2][0] != v[start3]) {flipvertarray(verts[2],numcuts+2);} /* We should have something like this now start2 3 2 1 0 start3 0|---*---*---|3 | | 1* *2 | | 2* *1 | | 3|-----------|0 start We will fill this case like this or this depending on even or odd cuts there are a couple of differences. For odd cuts, there is a tri in the middle as well as 1 quad at the bottom (not including the extra quads for odd cuts > 1 For even cuts, there is a quad in the middle and 2 quads on the bottom they are numbered here for clarity 1 outer tris and bottom quads 2 inner tri or quad 3 repeating quads |---*---*---*---| |1/ / \ \ 1| |/ 3 / \ 3 \| * / 2 \ * | / \ | |/ \ | *---------------* | 3 | | | *---------------* | | | 1 | | | |---------------| |---*---*---*---*---| | 1/ / \ \ 1| | / / \ \ | |/ 3 / \ 3 \| * / \ * | / \ | | / 2 \ | |/ \| *-------------------* | | | 3 | | | *-------------------* | | | 1 | | | *-------------------* | | | 1 | | | |-------------------| */ // Make outside tris hold = addfacelist(em, verts[0][vertsize-2],verts[0][vertsize-1],verts[1][1],NULL,NULL,NULL); hold->e3->f2 |= EDGEINNER; facecopy(em, efa,hold); hold = addfacelist(em, verts[1][vertsize-2],verts[1][vertsize-1],verts[2][1],NULL,NULL,NULL); hold->e3->f2 |= EDGEINNER; facecopy(em, efa,hold); // Make bottom quad hold = addfacelist(em, verts[0][0],verts[0][1],verts[2][vertsize-2],verts[2][vertsize-1],NULL,NULL); hold->e2->f2 |= EDGEINNER; facecopy(em, efa,hold); //If it is even cuts, add the 2nd lower quad if(numcuts % 2 == 0) { hold = addfacelist(em, verts[0][1],verts[0][2],verts[2][vertsize-3],verts[2][vertsize-2],NULL,NULL); hold->e2->f2 |= EDGEINNER; facecopy(em, efa,hold); // Also Make inner quad hold = addfacelist(em, verts[1][numcuts/2],verts[1][(numcuts/2)+1],verts[2][numcuts/2],verts[0][(numcuts/2)+1],NULL,NULL); hold->e3->f2 |= EDGEINNER; //if(scene->toolsettings->editbutflag & B_AUTOFGON) { // hold->e3->h |= EM_FGON; //} facecopy(em, efa,hold); repeats = (numcuts / 2) -1; } else { // Make inner tri hold = addfacelist(em, verts[1][(numcuts/2)+1],verts[2][(numcuts/2)+1],verts[0][(numcuts/2)+1],NULL,NULL,NULL); hold->e2->f2 |= EDGEINNER; //if(scene->toolsettings->editbutflag & B_AUTOFGON) { // hold->e2->h |= EM_FGON; //} facecopy(em, efa,hold); repeats = ((numcuts+1) / 2)-1; } // cuts for 1 and 2 do not have the repeating quads if(numcuts < 3) {repeats = 0;} for(i=0;ie2->f2 |= EDGEINNER; facecopy(em, efa,hold); hold = addfacelist(em, verts[1][vertsize-i-3],verts[1][vertsize-i-2],verts[2][i+1],verts[2][i+2],NULL,NULL); hold->e4->f2 |= EDGEINNER; facecopy(em, efa,hold); } // Do repeating bottom quads for(i=0;ie2->f2 |= EDGEINNER; facecopy(em, efa,hold); } //EM_fgon_flags(em); } static void fill_quad_quadruple(EditMesh *em, EditFace *efa, struct GHash *gh, int numcuts, float smooth, float fractal, int beauty) { EditVert **verts[4], ***innerverts; EditFace *hold; EditEdge temp; short vertsize, i, j; // Point verts[0] and [1] to the array of new verts for cedge[0] and cedge[1] verts[0] = BLI_ghash_lookup(gh, efa->e1); verts[1] = BLI_ghash_lookup(gh, efa->e2); verts[2] = BLI_ghash_lookup(gh, efa->e3); verts[3] = BLI_ghash_lookup(gh, efa->e4); //This is the index size of the verts array vertsize = numcuts+2; // Is the original v1 the same as the first vert on the selected edge? // if not, the edge is running the opposite direction in this face so flip // the array to the correct direction if(verts[0][0] != efa->v1) {flipvertarray(verts[0],numcuts+2);} if(verts[1][0] != efa->v2) {flipvertarray(verts[1],numcuts+2);} if(verts[2][0] == efa->v3) {flipvertarray(verts[2],numcuts+2);} if(verts[3][0] == efa->v4) {flipvertarray(verts[3],numcuts+2);} /* We should have something like this now 1 3 2 1 0 0|---*---*---|0 | | 1* *1 2 | | 4 2* *2 | | 3|---*---*---|3 3 2 1 0 3 // we will fill a 2 dim array of editvert*s to make filling easier // the innervert order is shown 0 0---1---2---3 | | | | 1 0---1---2---3 | | | | 2 0---1---2---3 | | | | 3 0---1---2---3 */ innerverts = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"quad-quad subdiv inner verts outer array"); for(i=0;ie1->f2 = EDGENEW; hold->e2->f2 = EDGENEW; hold->e3->f2 = EDGENEW; hold->e4->f2 = EDGENEW; if(i != 0) { hold->e1->f2 |= EDGEINNER; } if(j != 0) { hold->e2->f2 |= EDGEINNER; } if(i != numcuts) { hold->e3->f2 |= EDGEINNER; } if(j != numcuts) { hold->e4->f2 |= EDGEINNER; } facecopy(em, efa,hold); } } // Clean up our dynamic multi-dim array for(i=0;ie1); verts[1] = BLI_ghash_lookup(gh, efa->e2); verts[2] = BLI_ghash_lookup(gh, efa->e3); //This is the index size of the verts array vertsize = numcuts+2; // Is the original v1 the same as the first vert on the selected edge? // if not, the edge is running the opposite direction in this face so flip // the array to the correct direction if(verts[0][0] != efa->v1) {flipvertarray(verts[0],numcuts+2);} if(verts[1][0] != efa->v2) {flipvertarray(verts[1],numcuts+2);} if(verts[2][0] != efa->v3) {flipvertarray(verts[2],numcuts+2);} /* We should have something like this now 3 3 2 1 0 0|---*---*---|3 | / 1 1* *2 | / 2* *1 2 | / 3|/ 0 we will fill a 2 dim array of editvert*s to make filling easier 3 0 0---1---2---3---4 | / | / |/ | / 1 0---1----2---3 1 | / | / | / 2 0----1---2 2 | / | / |/ |/ 3 0---1 | / |/ 4 0 */ innerverts = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"tri-tri subdiv inner verts outer array"); for(i=0;ie1->f2 |= EDGENEW; hold->e2->f2 |= EDGENEW; hold->e3->f2 |= EDGENEW; if(i != 0) { hold->e1->f2 |= EDGEINNER; } if(j != 0) { hold->e2->f2 |= EDGEINNER; } if(j+1 != (numcuts+1)-i) {hold->e3->f2 |= EDGEINNER;} facecopy(em, efa,hold); //if there are more to come, we do the 2nd if(j+1 <= numcuts-i) { hold = addfacelist(em, innerverts[i+1][j],innerverts[i+1][j+1],innerverts[i][j+1],NULL,NULL,NULL); facecopy(em, efa,hold); hold->e1->f2 |= EDGENEW; hold->e2->f2 |= EDGENEW; hold->e3->f2 |= EDGENEW; } } } // Clean up our dynamic multi-dim array for(i=0;iv1, efa->v2, efa->v3, 0, efa, NULL); hold->e1->f2 |= EDGENEW; hold->e2->f2 |= EDGENEW; hold->e3->f2 |= EDGENEW; hold->e3->f2 |= EDGEINNER; facecopy(em, efa, hold); hold= addfacelist(em, efa->v1, efa->v3, efa->v4, 0, efa, NULL); hold->e1->f2 |= EDGENEW; hold->e2->f2 |= EDGENEW; hold->e3->f2 |= EDGENEW; hold->e1->f2 |= EDGEINNER; facecopy(em, efa, hold); } else{ hold= addfacelist(em, efa->v1, efa->v2, efa->v4, 0, efa, NULL); hold->e1->f2 |= EDGENEW; hold->e2->f2 |= EDGENEW; hold->e3->f2 |= EDGENEW; hold->e2->f2 |= EDGEINNER; facecopy(em, efa, hold); hold= addfacelist(em, efa->v2, efa->v3, efa->v4, 0, efa, NULL); hold->e1->f2 |= EDGENEW; hold->e2->f2 |= EDGENEW; hold->e3->f2 |= EDGENEW; hold->e3->f2 |= EDGEINNER; facecopy(em, efa, hold); } } static void fill_quad_singlevert(EditMesh *em, EditFace *efa, struct GHash *gh) { EditEdge *cedge=NULL; EditVert *v[4], **verts; EditFace *hold; short start=0, end, left, right, vertsize; v[0] = efa->v1; v[1] = efa->v2; v[2] = efa->v3; v[3] = efa->v4; if(efa->e1->f & SELECT) { cedge = efa->e1; start = 0;} else if(efa->e2->f & SELECT) { cedge = efa->e2; start = 1;} else if(efa->e3->f & SELECT) { cedge = efa->e3; start = 2;} else if(efa->e4->f & SELECT) { cedge = efa->e4; start = 3;} // Point verts to the array of new verts for cedge verts = BLI_ghash_lookup(gh, cedge); //This is the index size of the verts array vertsize = 3; // Is the original v1 the same as the first vert on the selected edge? // if not, the edge is running the opposite direction in this face so flip // the array to the correct direction if(verts[0] != v[start]) {flipvertarray(verts,3);} end = (start+1)%4; left = (start+2)%4; right = (start+3)%4; /* We should have something like this now end start 2 1 0 |-----*-----| | | | | | | ------------- left right where start,end,left, right are indexes of EditFace->v1, etc (stored in v) and 0,1,2 are the indexes of the new verts stored in verts. We fill like this, depending on whether its vertex 'left' or vertex 'right' thats been knifed through... |---*---| |---*---| | / | | \ | | / | | \ | |/ | | \| X-------- --------X */ if(v[left]->f1){ //triangle is composed of cutvert, end and left hold = addfacelist(em, verts[1],v[end],v[left],NULL, NULL,NULL); hold->e1->f2 |= EDGENEW; hold->e2->f2 |= EDGENEW; hold->e3->f2 |= EDGENEW; hold->e3->f2 |= EDGEINNER; facecopy(em, efa, hold); //quad is composed of cutvert, left, right and start hold = addfacelist(em, verts[1],v[left],v[right],v[start], NULL, NULL); hold->e1->f2 |= EDGENEW; hold->e2->f2 |= EDGENEW; hold->e3->f2 |= EDGENEW; hold->e4->f2 |= EDGENEW; hold->e1->f2 |= EDGEINNER; facecopy(em, efa, hold); } else if(v[right]->f1){ //triangle is composed of cutvert, right and start hold = addfacelist(em, verts[1],v[right],v[start], NULL, NULL, NULL); hold->e1->f2 |= EDGENEW; hold->e2->f2 |= EDGENEW; hold->e3->f2 |= EDGENEW; hold->e1->f2 |= EDGEINNER; facecopy(em, efa, hold); //quad is composed of cutvert, end, left, right hold = addfacelist(em, verts[1],v[end], v[left], v[right], NULL, NULL); hold->e1->f2 |= EDGENEW; hold->e2->f2 |= EDGENEW; hold->e3->f2 |= EDGENEW; hold->e4->f2 |= EDGENEW; hold->e4->f2 |= EDGEINNER; facecopy(em, efa, hold); } } // This function takes an example edge, the current point to create and // the total # of points to create, then creates the point and return the // editvert pointer to it. static EditVert *subdivideedgenum(EditMesh *em, EditEdge *edge, int curpoint, int totpoint, float smooth, float fractal, int beauty) { EditVert *ev; float percent; if (beauty & (B_PERCENTSUBD) && totpoint == 1) //percent=(float)(edge->tmp.l)/32768.0f; percent= edge->tmp.fp; else percent= (float)curpoint/(float)(totpoint+1); ev= subdivide_edge_addvert(em, edge, smooth, fractal, beauty, percent); ev->f = edge->v1->f; return ev; } void esubdivideflag(Object *obedit, EditMesh *em, int flag, float smooth, float fractal, int beauty, int numcuts, int corner_pattern, int seltype) { EditFace *ef; EditEdge *eed, *cedge, *sort[4]; EditVert *eve, **templist; struct GHash *gh; float length[4], v1mat[3], v2mat[3], v3mat[3], v4mat[3]; int i, j, edgecount, touchcount, facetype,hold; ModifierData *md= obedit->modifiers.first; int ctrl= 0; // XXX //Set faces f1 to 0 cause we need it later for(ef=em->faces.first;ef;ef = ef->next) ef->f1 = 0; for(eve=em->verts.first; eve; eve=eve->next) { if(!(beauty & B_KNIFE)) /* knife sets this flag for vertex cuts */ eve->f1 = 0; eve->f2 = 0; } for (; md; md=md->next) { if (md->type==eModifierType_Mirror) { MirrorModifierData *mmd = (MirrorModifierData*) md; if(mmd->flag & MOD_MIR_CLIPPING) { for (eve= em->verts.first; eve; eve= eve->next) { eve->f2= 0; switch(mmd->axis){ case 0: if (fabs(eve->co[0]) < mmd->tolerance) eve->f2 |= 1; break; case 1: if (fabs(eve->co[1]) < mmd->tolerance) eve->f2 |= 2; break; case 2: if (fabs(eve->co[2]) < mmd->tolerance) eve->f2 |= 4; break; } } } } } //Flush vertex flags upward to the edges for(eed = em->edges.first;eed;eed = eed->next) { //if(eed->f & flag && eed->v1->f == eed->v2->f) { // eed->f |= eed->v1->f; // } eed->f2 = 0; if(eed->f & flag) { eed->f2 |= EDGEOLD; } } // We store an array of verts for each edge that is subdivided, // we put this array as a value in a ghash which is keyed by the EditEdge* // Now for beauty subdivide deselect edges based on length if(beauty & B_BEAUTY) { for(ef = em->faces.first;ef;ef = ef->next) { if(!ef->v4) { continue; } if(ef->f & SELECT) { VECCOPY(v1mat, ef->v1->co); VECCOPY(v2mat, ef->v2->co); VECCOPY(v3mat, ef->v3->co); VECCOPY(v4mat, ef->v4->co); mul_mat3_m4_v3(obedit->obmat, v1mat); mul_mat3_m4_v3(obedit->obmat, v2mat); mul_mat3_m4_v3(obedit->obmat, v3mat); mul_mat3_m4_v3(obedit->obmat, v4mat); length[0] = len_v3v3(v1mat, v2mat); length[1] = len_v3v3(v2mat, v3mat); length[2] = len_v3v3(v3mat, v4mat); length[3] = len_v3v3(v4mat, v1mat); sort[0] = ef->e1; sort[1] = ef->e2; sort[2] = ef->e3; sort[3] = ef->e4; // Beauty Short Edges if(beauty & B_BEAUTY_SHORT) { for(j=0;j<2;j++) { hold = -1; for(i=0;i<4;i++) { if(length[i] < 0) { continue; } else if(hold == -1) { hold = i; } else { if(length[hold] < length[i]) { hold = i; } } } if (hold > -1) { sort[hold]->f &= ~SELECT; sort[hold]->f2 |= EDGENEW; length[hold] = -1; } } } // Beauty Long Edges else { for(j=0;j<2;j++) { hold = -1; for(i=0;i<4;i++) { if(length[i] < 0) { continue; } else if(hold == -1) { hold = i; } else { if(length[hold] > length[i]) { hold = i; } } } if (hold > -1) { sort[hold]->f &= ~SELECT; sort[hold]->f2 |= EDGENEW; length[hold] = -1; } } } } } } gh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "subdivideedgenum gh"); // If we are knifing, We only need the selected edges that were cut, so deselect if it was not cut if(beauty & B_KNIFE) { for(eed= em->edges.first;eed;eed=eed->next) { if( eed->tmp.fp == 0 ) { EM_select_edge(eed,0); } } } // So for each edge, if it is selected, we allocate an array of size cuts+2 // so we can have a place for the v1, the new verts and v2 for(eed=em->edges.first;eed;eed = eed->next) { if(eed->f & flag) { templist = MEM_mallocN(sizeof(EditVert*)*(numcuts+2),"vertlist"); templist[0] = eed->v1; for(i=0;if2 = EDGENEW; } templist[i+1] = eed->v2; //Do the last edge too cedge = addedgelist(em, templist[i],templist[i+1],eed); cedge->f2 = EDGENEW; // Now that the edge is subdivided, we can put its verts in the ghash BLI_ghash_insert(gh, eed, templist); } } // DAG_id_flush_update(obedit->data, OB_RECALC_DATA); // Now for each face in the mesh we need to figure out How many edges were cut // and which filling method to use for that face for(ef = em->faces.first;ef;ef = ef->next) { edgecount = 0; facetype = 3; if(ef->e1->f & flag) {edgecount++;} if(ef->e2->f & flag) {edgecount++;} if(ef->e3->f & flag) {edgecount++;} if(ef->v4) { facetype = 4; if(ef->e4->f & flag) {edgecount++;} } if(facetype == 4) { switch(edgecount) { case 0: if(beauty & B_KNIFE && numcuts == 1){ /*Test for when knifing through two opposite verts but no edges*/ touchcount = 0; if(ef->v1->f1) touchcount++; if(ef->v2->f1) touchcount++; if(ef->v3->f1) touchcount++; if(ef->v4->f1) touchcount++; if(touchcount == 2){ if(ef->v1->f1 && ef->v3->f1){ ef->f1 = SELECT; fill_quad_doublevert(em, ef, 1, 3); } else if(ef->v2->f1 && ef->v4->f1){ ef->f1 = SELECT; fill_quad_doublevert(em, ef, 2, 4); } } } break; case 1: if(beauty & B_KNIFE && numcuts == 1){ /*Test for when knifing through an edge and one vert*/ touchcount = 0; if(ef->v1->f1) touchcount++; if(ef->v2->f1) touchcount++; if(ef->v3->f1) touchcount++; if(ef->v4->f1) touchcount++; if(touchcount == 1){ if( (ef->e1->f & flag && ( !ef->e1->v1->f1 && !ef->e1->v2->f1 )) || (ef->e2->f & flag && ( !ef->e2->v1->f1 && !ef->e2->v2->f1 )) || (ef->e3->f & flag && ( !ef->e3->v1->f1 && !ef->e3->v2->f1 )) || (ef->e4->f & flag && ( !ef->e4->v1->f1 && !ef->e4->v2->f1 )) ){ ef->f1 = SELECT; fill_quad_singlevert(em, ef, gh); } else{ ef->f1 = SELECT; fill_quad_single(em, ef, gh, numcuts, seltype); } } else{ ef->f1 = SELECT; fill_quad_single(em, ef, gh, numcuts, seltype); } } else{ ef->f1 = SELECT; fill_quad_single(em, ef, gh, numcuts, seltype); } break; case 2: ef->f1 = SELECT; // if there are 2, we check if edge 1 and 3 are either both on or off that way // we can tell if the selected pair is Adjacent or Opposite of each other if((ef->e1->f & flag && ef->e3->f & flag) || (ef->e2->f & flag && ef->e4->f & flag)) { fill_quad_double_op(em, ef, gh, numcuts); }else{ switch(corner_pattern) { case 0: fill_quad_double_adj_path(em, ef, gh, numcuts); break; case 1: fill_quad_double_adj_inner(em, ef, gh, numcuts); break; case 2: fill_quad_double_adj_fan(em, ef, gh, numcuts); break; } } break; case 3: ef->f1 = SELECT; fill_quad_triple(em, ef, gh, numcuts); break; case 4: ef->f1 = SELECT; fill_quad_quadruple(em, ef, gh, numcuts, smooth, fractal, beauty); break; } } else { switch(edgecount) { case 0: break; case 1: ef->f1 = SELECT; fill_tri_single(em, ef, gh, numcuts, seltype); break; case 2: ef->f1 = SELECT; fill_tri_double(em, ef, gh, numcuts); break; case 3: ef->f1 = SELECT; fill_tri_triple(em, ef, gh, numcuts, smooth, fractal, beauty); break; } } } // Delete Old Edges and Faces for(eed = em->edges.first;eed;eed = eed->next) { if(BLI_ghash_haskey(gh,eed)) { eed->f1 = SELECT; } else { eed->f1 = 0; } } free_tagged_edges_faces(em, em->edges.first, em->faces.first); if(seltype == SUBDIV_SELECT_ORIG && !ctrl) { /* bugfix: vertex could get flagged as "not-selected" // solution: clear flags before, not at the same time as setting SELECT flag -dg */ for(eed = em->edges.first;eed;eed = eed->next) { if(!(eed->f2 & EDGENEW || eed->f2 & EDGEOLD)) { eed->f &= !flag; EM_select_edge(eed,0); } } for(eed = em->edges.first;eed;eed = eed->next) { if(eed->f2 & EDGENEW || eed->f2 & EDGEOLD) { eed->f |= flag; EM_select_edge(eed,1); } } } else if ((seltype == SUBDIV_SELECT_INNER || seltype == SUBDIV_SELECT_INNER_SEL)|| ctrl) { for(eed = em->edges.first;eed;eed = eed->next) { if(eed->f2 & EDGEINNER) { eed->f |= flag; EM_select_edge(eed,1); if(eed->v1->f & EDGEINNER) eed->v1->f |= SELECT; if(eed->v2->f & EDGEINNER) eed->v2->f |= SELECT; }else{ eed->f &= !flag; EM_select_edge(eed,0); } } } else if(seltype == SUBDIV_SELECT_LOOPCUT){ for(eed = em->edges.first;eed;eed = eed->next) { if(eed->f2 & DOUBLEOPFILL){ eed->f |= flag; EM_select_edge(eed,1); }else{ eed->f &= !flag; EM_select_edge(eed,0); } } } if(em->selectmode & SCE_SELECT_VERTEX) { for(eed = em->edges.first;eed;eed = eed->next) { if(eed->f & SELECT) { eed->v1->f |= SELECT; eed->v2->f |= SELECT; } } } //fix hide flags for edges. First pass, hide edges of hidden faces for(ef=em->faces.first; ef; ef=ef->next){ if(ef->h){ ef->e1->h |= 1; ef->e2->h |= 1; ef->e3->h |= 1; if(ef->e4) ef->e4->h |= 1; } } //second pass: unhide edges of visible faces adjacent to hidden faces for(ef=em->faces.first; ef; ef=ef->next){ if(ef->h == 0){ ef->e1->h &= ~1; ef->e2->h &= ~1; ef->e3->h &= ~1; if(ef->e4) ef->e4->h &= ~1; } } // Free the ghash and call MEM_freeN on all the value entries to return // that memory BLI_ghash_free(gh, NULL, (GHashValFreeFP)MEM_freeN); EM_selectmode_flush(em); for(ef=em->faces.first;ef;ef = ef->next) { if(ef->e4) { if( (ef->e1->f & SELECT && ef->e2->f & SELECT) && (ef->e3->f & SELECT && ef->e4->f & SELECT) ) { ef->f |= SELECT; } } else { if( (ef->e1->f & SELECT && ef->e2->f & SELECT) && ef->e3->f & SELECT) { ef->f |= SELECT; } } } recalc_editnormals(em); } static int count_selected_edges(EditEdge *ed) { int totedge = 0; while(ed) { ed->tmp.p = 0; if( ed->f & SELECT ) totedge++; ed= ed->next; } return totedge; } /* hurms, as if this makes code readable! It's pointerpointer hiding... (ton) */ typedef EditFace *EVPtr; typedef EVPtr EVPTuple[2]; /** builds EVPTuple array efaa of face tuples (in fact pointers to EditFaces) sharing one edge. arguments: selected edge list, face list. Edges will also be tagged accordingly (see eed->f2) */ static int collect_quadedges(EVPTuple *efaa, EditEdge *eed, EditFace *efa) { EditEdge *e1, *e2, *e3; EVPtr *evp; int i = 0; /* run through edges, if selected, set pointer edge-> facearray */ while(eed) { eed->f2= 0; eed->f1= 0; if( eed->f & SELECT ) { eed->tmp.p = (EditVert *) (&efaa[i]); i++; } else eed->tmp.p = NULL; eed= eed->next; } /* find edges pointing to 2 faces by procedure: - run through faces and their edges, increase face counter e->f1 for each face */ while(efa) { efa->f1= 0; if(efa->v4==0 && (efa->f & SELECT)) { /* if selected triangle */ e1= efa->e1; e2= efa->e2; e3= efa->e3; if(e1->f2<3 && e1->tmp.p) { if(e1->f2<2) { evp= (EVPtr *) e1->tmp.p; evp[(int)e1->f2] = efa; } e1->f2+= 1; } if(e2->f2<3 && e2->tmp.p) { if(e2->f2<2) { evp= (EVPtr *) e2->tmp.p; evp[(int)e2->f2]= efa; } e2->f2+= 1; } if(e3->f2<3 && e3->tmp.p) { if(e3->f2<2) { evp= (EVPtr *) e3->tmp.p; evp[(int)e3->f2]= efa; } e3->f2+= 1; } } else { /* set to 3 to make sure these are not flipped or joined */ efa->e1->f2= 3; efa->e2->f2= 3; efa->e3->f2= 3; if (efa->e4) efa->e4->f2= 3; } efa= efa->next; } return i; } /* returns vertices of two adjacent triangles forming a quad - can be righthand or lefthand 4-----3 |\ | | \ 2 | <- efa1 | \ | efa-> | 1 \ | | \| 1-----2 */ #define VTEST(face, num, other) \ (face->v##num != other->v1 && face->v##num != other->v2 && face->v##num != other->v3) static void givequadverts(EditFace *efa, EditFace *efa1, EditVert **v1, EditVert **v2, EditVert **v3, EditVert **v4, int *vindex) { if VTEST(efa, 1, efa1) { *v1= efa->v1; *v2= efa->v2; vindex[0]= 0; vindex[1]= 1; } else if VTEST(efa, 2, efa1) { *v1= efa->v2; *v2= efa->v3; vindex[0]= 1; vindex[1]= 2; } else if VTEST(efa, 3, efa1) { *v1= efa->v3; *v2= efa->v1; vindex[0]= 2; vindex[1]= 0; } if VTEST(efa1, 1, efa) { *v3= efa1->v1; *v4= (efa1->v2 == *v2)? efa1->v3: efa1->v2; vindex[2]= 0; vindex[3]= (efa1->v2 == *v2)? 2: 1; } else if VTEST(efa1, 2, efa) { *v3= efa1->v2; *v4= (efa1->v3 == *v2)? efa1->v1: efa1->v3; vindex[2]= 1; vindex[3]= (efa1->v3 == *v2)? 0: 2; } else if VTEST(efa1, 3, efa) { *v3= efa1->v3; *v4= (efa1->v1 == *v2)? efa1->v2: efa1->v1; vindex[2]= 2; vindex[3]= (efa1->v1 == *v2)? 1: 0; } else *v3= *v4= NULL; } /* Helper functions for edge/quad edit features*/ static void untag_edges(EditFace *f) { f->e1->f1 = 0; f->e2->f1 = 0; f->e3->f1 = 0; if (f->e4) f->e4->f1 = 0; } /** remove and free list of tagged edges and faces */ static void free_tagged_edges_faces(EditMesh *em, EditEdge *eed, EditFace *efa) { EditEdge *nexted; EditFace *nextvl; while(efa) { nextvl= efa->next; if(efa->f1) { BLI_remlink(&em->faces, efa); free_editface(em, efa); } else /* avoid deleting edges that are still in use */ untag_edges(efa); efa= nextvl; } while(eed) { nexted= eed->next; if(eed->f1) { remedge(em, eed); free_editedge(em, eed); } eed= nexted; } } /* ******************** BEGIN TRIANGLE TO QUAD ************************************* */ static float measure_facepair(EditVert *v1, EditVert *v2, EditVert *v3, EditVert *v4, float limit) { /*gives a 'weight' to a pair of triangles that join an edge to decide how good a join they would make*/ /*Note: this is more complicated than it needs to be and should be cleaned up...*/ float measure = 0.0, noA1[3], noA2[3], noB1[3], noB2[3], normalADiff, normalBDiff, edgeVec1[3], edgeVec2[3], edgeVec3[3], edgeVec4[3], diff, minarea, maxarea, areaA, areaB; /*First Test: Normal difference*/ normal_tri_v3( noA1,v1->co, v2->co, v3->co); normal_tri_v3( noA2,v1->co, v3->co, v4->co); if(noA1[0] == noA2[0] && noA1[1] == noA2[1] && noA1[2] == noA2[2]) normalADiff = 0.0; else normalADiff = RAD2DEG(angle_v2v2(noA1, noA2)); //if(!normalADiff) normalADiff = 179; normal_tri_v3( noB1,v2->co, v3->co, v4->co); normal_tri_v3( noB2,v4->co, v1->co, v2->co); if(noB1[0] == noB2[0] && noB1[1] == noB2[1] && noB1[2] == noB2[2]) normalBDiff = 0.0; else normalBDiff = RAD2DEG(angle_v2v2(noB1, noB2)); //if(!normalBDiff) normalBDiff = 179; measure += (normalADiff/360) + (normalBDiff/360); if(measure > limit) return measure; /*Second test: Colinearity*/ sub_v3_v3v3(edgeVec1, v1->co, v2->co); sub_v3_v3v3(edgeVec2, v2->co, v3->co); sub_v3_v3v3(edgeVec3, v3->co, v4->co); sub_v3_v3v3(edgeVec4, v4->co, v1->co); diff = 0.0; diff = ( fabs(RAD2DEG(angle_v2v2(edgeVec1, edgeVec2)) - 90) + fabs(RAD2DEG(angle_v2v2(edgeVec2, edgeVec3)) - 90) + fabs(RAD2DEG(angle_v2v2(edgeVec3, edgeVec4)) - 90) + fabs(RAD2DEG(angle_v2v2(edgeVec4, edgeVec1)) - 90)) / 360; if(!diff) return 0.0; measure += diff; if(measure > limit) return measure; /*Third test: Concavity*/ areaA = area_tri_v3(v1->co, v2->co, v3->co) + area_tri_v3(v1->co, v3->co, v4->co); areaB = area_tri_v3(v2->co, v3->co, v4->co) + area_tri_v3(v4->co, v1->co, v2->co); if(areaA <= areaB) minarea = areaA; else minarea = areaB; if(areaA >= areaB) maxarea = areaA; else maxarea = areaB; if(!maxarea) measure += 1; else measure += (1 - (minarea / maxarea)); return measure; } #define T2QUV_LIMIT 0.005 #define T2QCOL_LIMIT 3 static int compareFaceAttribs(EditMesh *em, EditFace *f1, EditFace *f2, EditEdge *eed) { /*Test to see if the per-face attributes for the joining edge match within limit*/ MTFace *tf1, *tf2; unsigned int *col1, *col2; short i,attrok=0, flag = 0, /* XXX scene->toolsettings->editbutflag,*/ fe1[2], fe2[2]; tf1 = CustomData_em_get(&em->fdata, f1->data, CD_MTFACE); tf2 = CustomData_em_get(&em->fdata, f2->data, CD_MTFACE); col1 = CustomData_em_get(&em->fdata, f1->data, CD_MCOL); col2 = CustomData_em_get(&em->fdata, f2->data, CD_MCOL); /*store indices for faceedges*/ f1->v1->f1 = 0; f1->v2->f1 = 1; f1->v3->f1 = 2; fe1[0] = eed->v1->f1; fe1[1] = eed->v2->f1; f2->v1->f1 = 0; f2->v2->f1 = 1; f2->v3->f1 = 2; fe2[0] = eed->v1->f1; fe2[1] = eed->v2->f1; /*compare faceedges for each face attribute. Additional per face attributes can be added later*/ /*do UVs*/ if(flag & B_JOINTRIA_UV){ if(tf1 == NULL || tf2 == NULL) attrok |= B_JOINTRIA_UV; else if(tf1->tpage != tf2->tpage); /*do nothing*/ else{ for(i = 0; i < 2; i++){ if(tf1->uv[fe1[i]][0] + T2QUV_LIMIT > tf2->uv[fe2[i]][0] && tf1->uv[fe1[i]][0] - T2QUV_LIMIT < tf2->uv[fe2[i]][0] && tf1->uv[fe1[i]][1] + T2QUV_LIMIT > tf2->uv[fe2[i]][1] && tf1->uv[fe1[i]][1] - T2QUV_LIMIT < tf2->uv[fe2[i]][1]) attrok |= B_JOINTRIA_UV; } } } /*do VCOLs*/ if(flag & B_JOINTRIA_VCOL){ if(!col1 || !col2) attrok |= B_JOINTRIA_VCOL; else{ char *f1vcol, *f2vcol; for(i = 0; i < 2; i++){ f1vcol = (char *)&(col1[fe1[i]]); f2vcol = (char *)&(col2[fe2[i]]); /*compare f1vcol with f2vcol*/ if( f1vcol[1] + T2QCOL_LIMIT > f2vcol[1] && f1vcol[1] - T2QCOL_LIMIT < f2vcol[1] && f1vcol[2] + T2QCOL_LIMIT > f2vcol[2] && f1vcol[2] - T2QCOL_LIMIT < f2vcol[2] && f1vcol[3] + T2QCOL_LIMIT > f2vcol[3] && f1vcol[3] - T2QCOL_LIMIT < f2vcol[3]) attrok |= B_JOINTRIA_VCOL; } } } if( ((attrok & B_JOINTRIA_UV) == (flag & B_JOINTRIA_UV)) && ((attrok & B_JOINTRIA_VCOL) == (flag & B_JOINTRIA_VCOL)) ) return 1; return 0; } static int fplcmp(const void *v1, const void *v2) { const EditEdge *e1= *((EditEdge**)v1), *e2=*((EditEdge**)v2); if( e1->crease > e2->crease) return 1; else if( e1->crease < e2->crease) return -1; return 0; } /*Bitflags for edges.*/ #define T2QDELETE 1 #define T2QCOMPLEX 2 #define T2QJOIN 4 void join_triangles(EditMesh *em) { EditVert *v1, *v2, *v3, *v4, *eve; EditEdge *eed, **edsortblock = NULL, **edb = NULL; EditFace *efa; EVPTuple *efaar = NULL; EVPtr *efaa = NULL; float *creases = NULL; float measure; /*Used to set tolerance*/ float limit = 0.8f; // XXX scene->toolsettings->jointrilimit; int i, ok, totedge=0, totseledge=0, complexedges, vindex[4]; /*if we take a long time on very dense meshes we want waitcursor to display*/ waitcursor(1); totseledge = count_selected_edges(em->edges.first); if(totseledge==0) return; /*abusing crease value to store weights for edge pairs. Nasty*/ for(eed=em->edges.first; eed; eed=eed->next) totedge++; if(totedge) creases = MEM_callocN(sizeof(float) * totedge, "Join Triangles Crease Array"); for(eed=em->edges.first, i = 0; eed; eed=eed->next, i++){ creases[i] = eed->crease; eed->crease = 0.0; } /*clear temp flags*/ for(eve=em->verts.first; eve; eve=eve->next) eve->f1 = eve->f2 = 0; for(eed=em->edges.first; eed; eed=eed->next) eed->f2 = eed->f1 = 0; for(efa=em->faces.first; efa; efa=efa->next) efa->f1 = efa->tmp.l = 0; /*For every selected 2 manifold edge, create pointers to its two faces.*/ efaar= (EVPTuple *) MEM_callocN(totseledge * sizeof(EVPTuple), "Tri2Quad"); ok = collect_quadedges(efaar, em->edges.first, em->faces.first); complexedges = 0; if(ok){ /*clear tmp.l flag and store number of faces that are selected and coincident to current face here.*/ for(eed=em->edges.first; eed; eed=eed->next){ /* eed->f2 is 2 only if this edge is part of exactly two triangles, and both are selected, and it has EVPTuple assigned */ if(eed->f2 == 2){ efaa= (EVPtr *) eed->tmp.p; efaa[0]->tmp.l++; efaa[1]->tmp.l++; } } for(eed=em->edges.first; eed; eed=eed->next){ if(eed->f2 == 2){ efaa= (EVPtr *) eed->tmp.p; v1 = v2 = v3 = v4 = NULL; givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex); if(v1 && v2 && v3 && v4){ /*test if simple island first. This mimics 2.42 behaviour and the tests are less restrictive.*/ if(efaa[0]->tmp.l == 1 && efaa[1]->tmp.l == 1){ eed->f1 |= T2QJOIN; efaa[0]->f1 = 1; //mark for join efaa[1]->f1 = 1; //mark for join } else{ /* The face pair is part of a 'complex' island, so the rules for dealing with it are more involved. Depending on what options the user has chosen, this face pair can be 'thrown out' based upon the following criteria: 1: the two faces do not share the same material 2: the edge joining the two faces is marked as sharp. 3: the two faces UV's do not make a good match 4: the two faces Vertex colors do not make a good match If the face pair passes all the applicable tests, it is then given a 'weight' with the measure_facepair() function. This measures things like concavity, colinearity ect. If this weight is below the threshold set by the user the edge joining them is marked as being 'complex' and will be compared against other possible pairs which contain one of the same faces in the current pair later. This technique is based upon an algorithm that Campbell Barton developed for his Tri2Quad script that was previously part of the python scripts bundled with Blender releases. */ // XXX if(scene->toolsettings->editbutflag & B_JOINTRIA_SHARP && eed->sharp); /*do nothing*/ // else if(scene->toolsettings->editbutflag & B_JOINTRIA_MAT && efaa[0]->mat_nr != efaa[1]->mat_nr); /*do nothing*/ // else if(((scene->toolsettings->editbutflag & B_JOINTRIA_UV) || (scene->toolsettings->editbutflag & B_JOINTRIA_VCOL)) && compareFaceAttribs(em, efaa[0], efaa[1], eed); // XXX == 0); /*do nothing*/ // else{ measure = measure_facepair(v1, v2, v3, v4, limit); if(measure < limit){ complexedges++; eed->f1 |= T2QCOMPLEX; eed->crease = measure; /*we dont mark edges for join yet*/ } // } } } } } /*Quicksort the complex edges according to their weighting*/ if(complexedges){ edsortblock = edb = MEM_callocN(sizeof(EditEdge*) * complexedges, "Face Pairs quicksort Array"); for(eed = em->edges.first; eed; eed=eed->next){ if(eed->f1 & T2QCOMPLEX){ *edb = eed; edb++; } } qsort(edsortblock, complexedges, sizeof(EditEdge*), fplcmp); /*now go through and mark the edges who get the highest weighting*/ for(edb=edsortblock, i=0; i < complexedges; edb++, i++){ efaa = (EVPtr *)((*edb)->tmp.p); /*suspect!*/ if( !efaa[0]->f1 && !efaa[1]->f1){ efaa[0]->f1 = 1; //mark for join efaa[1]->f1 = 1; //mark for join (*edb)->f1 |= T2QJOIN; } } } /*finally go through all edges marked for join (simple and complex) and create new faces*/ for(eed=em->edges.first; eed; eed=eed->next){ if(eed->f1 & T2QJOIN){ efaa= (EVPtr *)eed->tmp.p; v1 = v2 = v3 = v4 = NULL; givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex); if((v1 && v2 && v3 && v4) && (exist_face(em, v1, v2, v3, v4)==0)){ /*exist_face is very slow! Needs to be addressed.*/ /*flag for delete*/ eed->f1 |= T2QDELETE; /*create new quad and select*/ efa = EM_face_from_faces(em, efaa[0], efaa[1], vindex[0], vindex[1], 4+vindex[2], 4+vindex[3]); EM_select_face(efa,1); } else{ efaa[0]->f1 = 0; efaa[1]->f1 = 0; } } } } /*free data and cleanup*/ if(creases){ for(eed=em->edges.first, i = 0; eed; eed=eed->next, i++) eed->crease = creases[i]; MEM_freeN(creases); } for(eed=em->edges.first; eed; eed=eed->next){ if(eed->f1 & T2QDELETE) eed->f1 = 1; else eed->f1 = 0; } free_tagged_edges_faces(em, em->edges.first, em->faces.first); if(efaar) MEM_freeN(efaar); if(edsortblock) MEM_freeN(edsortblock); EM_selectmode_flush(em); } /* ******************** END TRIANGLE TO QUAD ************************************* */ #define FACE_MARKCLEAR(f) (f->f1 = 1) /* quick hack, basically a copy of beautify_fill */ void edge_flip(EditMesh *em) { EditVert *v1, *v2, *v3, *v4; EditEdge *eed, *nexted; EditFace *efa, *w; //void **efaar, **efaa; EVPTuple *efaar; EVPtr *efaa; int totedge, ok, vindex[4]; /* - all selected edges with two faces * - find the faces: store them in edges (using datablock) * - per edge: - test convex * - test edge: flip? - if true: remedge, addedge, all edges at the edge get new face pointers */ EM_selectmode_flush(em); // makes sure in selectmode 'face' the edges of selected faces are selected too totedge = count_selected_edges(em->edges.first); if(totedge==0) return; /* temporary array for : edge -> face[1], face[2] */ efaar= (EVPTuple *) MEM_callocN(totedge * sizeof(EVPTuple), "edgeflip"); ok = collect_quadedges(efaar, em->edges.first, em->faces.first); eed= em->edges.first; while(eed) { nexted= eed->next; if(eed->f2==2) { /* points to 2 faces */ efaa= (EVPtr *) eed->tmp.p; /* don't do it if flagged */ ok= 1; efa= efaa[0]; if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0; efa= efaa[1]; if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0; if(ok) { /* test convex */ givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex); /* 4-----3 4-----3 |\ | | /| | \ 1 | | 1 / | | \ | -> | / | | 0 \ | | / 0 | | \| |/ | 1-----2 1-----2 */ /* make new faces */ if (v1 && v2 && v3) { if( convex(v1->co, v2->co, v3->co, v4->co) ) { if(exist_face(em, v1, v2, v3, v4)==0) { /* outch this may break seams */ w= EM_face_from_faces(em, efaa[0], efaa[1], vindex[0], vindex[1], 4+vindex[2], -1); EM_select_face(w, 1); /* outch this may break seams */ w= EM_face_from_faces(em, efaa[0], efaa[1], vindex[0], 4+vindex[2], 4+vindex[3], -1); EM_select_face(w, 1); } /* tag as to-be-removed */ FACE_MARKCLEAR(efaa[1]); FACE_MARKCLEAR(efaa[0]); eed->f1 = 1; } /* endif test convex */ } } } eed= nexted; } /* clear tagged edges and faces: */ free_tagged_edges_faces(em, em->edges.first, em->faces.first); MEM_freeN(efaar); } #define DIRECTION_CW 1 #define DIRECTION_CCW 2 static const EnumPropertyItem direction_items[]= { {DIRECTION_CW, "CW", 0, "Clockwise", ""}, {DIRECTION_CCW, "CCW", 0, "Counter Clockwise", ""}, {0, NULL, 0, NULL, NULL}}; #define AXIS_X 1 #define AXIS_Y 2 static const EnumPropertyItem axis_items[]= { {AXIS_X, "X", 0, "X", ""}, {AXIS_Y, "Y", 0, "Y", ""}, {0, NULL, 0, NULL, NULL}}; static void edge_rotate(EditMesh *em, wmOperator *op, EditEdge *eed, int dir) { EditVert **verts[2]; EditFace *face[2], *efa, *newFace[2]; EditEdge **edges[2], **hiddenedges, *srchedge; int facecount, p1, p2, p3, p4, fac1, fac2, i, j; int numhidden, numshared, p[2][4]; /* check to make sure that the edge is only part of 2 faces */ facecount = 0; for(efa = em->faces.first;efa;efa = efa->next) { if((efa->e1 == eed || efa->e2 == eed) || (efa->e3 == eed || efa->e4 == eed)) { if(facecount >= 2) { /* more than two faces with this edge */ return; } else { face[facecount] = efa; facecount++; } } } if(facecount < 2) return; /* how many edges does each face have */ if(face[0]->e4) fac1= 4; else fac1= 3; if(face[1]->e4) fac2= 4; else fac2= 3; /* make a handy array for verts and edges */ verts[0]= &face[0]->v1; edges[0]= &face[0]->e1; verts[1]= &face[1]->v1; edges[1]= &face[1]->e1; /* we don't want to rotate edges between faces that share more than one edge */ numshared= 0; for(i=0; i 1) return; /* we want to construct an array of vertex indicis in both faces, starting at the last vertex of the edge being rotated. - first we find the two vertices that lie on the rotating edge - then we make sure they are ordered according to the face vertex order - and then we construct the array */ p1= p2= p3= p4= 0; for(i=0; i<4; i++) { if(eed->v1 == verts[0][i]) p1 = i; if(eed->v2 == verts[0][i]) p2 = i; if(eed->v1 == verts[1][i]) p3 = i; if(eed->v2 == verts[1][i]) p4 = i; } if((p1+1)%fac1 == p2) SWAP(int, p1, p2); if((p3+1)%fac2 == p4) SWAP(int, p3, p4); for (i = 0; i < 4; i++) { p[0][i]= (p1 + i)%fac1; p[1][i]= (p3 + i)%fac2; } /* create an Array of the Edges who have h set prior to rotate */ numhidden = 0; for(srchedge = em->edges.first;srchedge;srchedge = srchedge->next) if(srchedge->h && ((srchedge->v1->f & SELECT) || (srchedge->v2->f & SELECT))) numhidden++; hiddenedges = MEM_mallocN(sizeof(EditVert*)*numhidden+1, "RotateEdgeHiddenVerts"); if(!hiddenedges) { BKE_report(op->reports, RPT_ERROR, "Memory allocation failed"); return; } numhidden = 0; for(srchedge=em->edges.first; srchedge; srchedge=srchedge->next) if(srchedge->h && (srchedge->v1->f & SELECT || srchedge->v2->f & SELECT)) hiddenedges[numhidden++] = srchedge; /* create the 2 new faces */ if(fac1 == 3 && fac2 == 3) { /* no need of reverse setup */ newFace[0]= EM_face_from_faces(em, face[0], face[1], p[0][1], p[0][2], 4+p[1][1], -1); newFace[1]= EM_face_from_faces(em, face[1], face[0], p[1][1], p[1][2], 4+p[0][1], -1); } else if(fac1 == 4 && fac2 == 3) { if(dir == DIRECTION_CCW) { newFace[0]= EM_face_from_faces(em, face[0], face[1], p[0][1], p[0][2], p[0][3], 4+p[1][1]); newFace[1]= EM_face_from_faces(em, face[1], face[0], p[1][1], p[1][2], 4+p[0][1], -1); } else if (dir == DIRECTION_CW) { newFace[0]= EM_face_from_faces(em, face[0], face[1], p[0][2], 4+p[1][1], p[0][0], p[0][1]); newFace[1]= EM_face_from_faces(em, face[1], face[0], 4+p[0][2], p[1][0], p[1][1], -1); verts[0][p[0][2]]->f |= SELECT; verts[1][p[1][1]]->f |= SELECT; } } else if(fac1 == 3 && fac2 == 4) { if(dir == DIRECTION_CCW) { newFace[0]= EM_face_from_faces(em, face[0], face[1], p[0][1], p[0][2], 4+p[1][1], -1); newFace[1]= EM_face_from_faces(em, face[1], face[0], p[1][1], p[1][2], p[1][3], 4+p[0][1]); } else if (dir == DIRECTION_CW) { newFace[0]= EM_face_from_faces(em, face[0], face[1], p[0][0], p[0][1], 4+p[1][2], -1); newFace[1]= EM_face_from_faces(em, face[1], face[0], p[1][1], p[1][2], 4+p[0][1], 4+p[0][2]); verts[0][p[0][1]]->f |= SELECT; verts[1][p[1][2]]->f |= SELECT; } } else if(fac1 == 4 && fac2 == 4) { if(dir == DIRECTION_CCW) { newFace[0]= EM_face_from_faces(em, face[0], face[1], p[0][1], p[0][2], p[0][3], 4+p[1][1]); newFace[1]= EM_face_from_faces(em, face[1], face[0], p[1][1], p[1][2], p[1][3], 4+p[0][1]); } else if (dir == DIRECTION_CW) { newFace[0]= EM_face_from_faces(em, face[0], face[1], p[0][2], p[0][3], 4+p[1][1], 4+p[1][2]); newFace[1]= EM_face_from_faces(em, face[1], face[0], p[1][2], p[1][3], 4+p[0][1], 4+p[0][2]); verts[0][p[0][2]]->f |= SELECT; verts[1][p[1][2]]->f |= SELECT; } } else return; /* This should never happen */ if(dir == DIRECTION_CCW || (fac1 == 3 && fac2 == 3)) { verts[0][p[0][1]]->f |= SELECT; verts[1][p[1][1]]->f |= SELECT; } /* copy old edge's flags to new center edge*/ for(srchedge=em->edges.first;srchedge;srchedge=srchedge->next) { if((srchedge->v1->f & SELECT) && (srchedge->v2->f & SELECT)) { srchedge->f = eed->f; srchedge->h = eed->h; srchedge->dir = eed->dir; srchedge->seam = eed->seam; srchedge->crease = eed->crease; srchedge->bweight = eed->bweight; } } /* resetting hidden flag */ for(numhidden--; numhidden>=0; numhidden--) hiddenedges[numhidden]->h= 1; /* check for orhphan edges */ for(srchedge=em->edges.first; srchedge; srchedge=srchedge->next) srchedge->f1= -1; /* cleanup */ MEM_freeN(hiddenedges); /* get rid of the old edge and faces*/ remedge(em, eed); free_editedge(em, eed); BLI_remlink(&em->faces, face[0]); free_editface(em, face[0]); BLI_remlink(&em->faces, face[1]); free_editface(em, face[1]); } /* only accepts 1 selected edge, or 2 selected faces */ static int edge_rotate_selected(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data); EditEdge *eed; EditFace *efa; int dir = RNA_enum_get(op->ptr, "direction"); // dir == 2 when clockwise and ==1 for counter CW. short edgeCount = 0; /*clear new flag for new edges, count selected edges */ for(eed= em->edges.first; eed; eed= eed->next) { eed->f1= 0; eed->f2 &= ~2; if(eed->f & SELECT) edgeCount++; } if(edgeCount>1) { /* more selected edges, check faces */ for(efa= em->faces.first; efa; efa= efa->next) { if(efa->f & SELECT) { efa->e1->f1++; efa->e2->f1++; efa->e3->f1++; if(efa->e4) efa->e4->f1++; } } edgeCount= 0; for(eed= em->edges.first; eed; eed= eed->next) { if(eed->f1==2) edgeCount++; } if(edgeCount==1) { for(eed= em->edges.first; eed; eed= eed->next) { if(eed->f1==2) { edge_rotate(em, op, eed,dir); break; } } } else { BKE_report(op->reports, RPT_ERROR, "Select one edge or two adjacent faces"); BKE_mesh_end_editmesh(obedit->data, em); return OPERATOR_CANCELLED; } } else if(edgeCount==1) { for(eed= em->edges.first; eed; eed= eed->next) { if(eed->f & SELECT) { EM_select_edge(eed, 0); edge_rotate(em, op, eed,dir); break; } } } else { BKE_report(op->reports, RPT_ERROR, "Select one edge or two adjacent faces"); BKE_mesh_end_editmesh(obedit->data, em); return OPERATOR_CANCELLED; } /* flush selected vertices (again) to edges/faces */ EM_select_flush(em); BKE_mesh_end_editmesh(obedit->data, em); DAG_id_flush_update(obedit->data, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); return OPERATOR_FINISHED; } void MESH_OT_edge_rotate(wmOperatorType *ot) { /* identifiers */ ot->name= "Rotate Selected Edge"; ot->description= "Rotate selected edge or adjoining faces"; ot->idname= "MESH_OT_edge_rotate"; /* api callbacks */ ot->exec= edge_rotate_selected; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; /* props */ RNA_def_enum(ot->srna, "direction", direction_items, DIRECTION_CW, "Direction", "Direction to rotate the edge around."); } /******************* BEVEL CODE STARTS HERE ********************/ /* XXX old bevel not ported yet */ void bevel_menu(EditMesh *em) { BME_Mesh *bm; BME_TransData_Head *td; // TransInfo *t; int options, res, gbm_free = 0; // t = BIF_GetTransInfo(); if (!G.editBMesh) { G.editBMesh = MEM_callocN(sizeof(*(G.editBMesh)),"bevel_menu() G.editBMesh"); gbm_free = 1; } G.editBMesh->options = BME_BEVEL_RUNNING | BME_BEVEL_SELECT; G.editBMesh->res = 1; while(G.editBMesh->options & BME_BEVEL_RUNNING) { options = G.editBMesh->options; res = G.editBMesh->res; bm = BME_editmesh_to_bmesh(em); // BIF_undo_push("Pre-Bevel"); free_editMesh(em); BME_bevel(bm,0.1f,res,options,0,0,&td); BME_bmesh_to_editmesh(bm, td, em); EM_selectmode_flush(em); G.editBMesh->bm = bm; G.editBMesh->td = td; // initTransform(TFM_BEVEL,CTX_BMESH); // Transform(); BME_free_transdata(td); BME_free_mesh(bm); // if (t->state != TRANS_CONFIRM) { // BIF_undo(); // } if (options == G.editBMesh->options) { G.editBMesh->options &= ~BME_BEVEL_RUNNING; } } if (gbm_free) { MEM_freeN(G.editBMesh); G.editBMesh = NULL; } } /* *********** END BEVEL *********/ /* this utility function checks to see if 2 edit edges share a face, returns 1 if they do returns 0 if they do not, or if the function is passed the same edge 2 times */ short sharesFace(EditMesh *em, EditEdge* e1, EditEdge* e2) { EditFace *search=NULL; search = em->faces.first; if (e1 == e2){ return 0 ; } while(search){ if( ((search->e1 == e1 || search->e2 == e1) || (search->e3 == e1 || search->e4 == e1)) && ((search->e1 == e2 || search->e2 == e2) || (search->e3 == e2 || search->e4 == e2)) ) { return 1; } search = search->next; } return 0; } typedef struct SlideUv { float origuv[2]; float *uv_up, *uv_down; //float *fuv[4]; LinkNode *fuv_list; } SlideUv; typedef struct SlideVert { EditEdge *up,*down; EditVert origvert; } SlideVert; #if 0 int EdgeSlide(EditMesh *em, wmOperator *op, short immediate, float imperc) { return 0; /* XXX REFACTOR - #if 0'd for now, otherwise can't make 64bit windows builds on 64bit machine */ useless: goto useless // because it doesn't do anything right now // NumInput num; XXX Mesh *me= NULL; // XXX EditFace *efa; EditEdge *eed,*first=NULL,*last=NULL, *temp = NULL; EditVert *ev, *nearest; LinkNode *edgelist = NULL, *vertlist=NULL, *look; GHash *vertgh; SlideVert *tempsv; float perc = 0, percp = 0,vertdist; // XXX, projectMat[4][4]; float shiftlabda= 0.0f,len = 0.0f; int i = 0,j, numsel, numadded=0, timesthrough = 0, vertsel=0, prop=1, cancel = 0,flip=0; int wasshift = 0; /* UV correction vars */ GHash **uvarray= NULL; int uvlay_tot= CustomData_number_of_layers(&em->fdata, CD_MTFACE); int uvlay_idx; SlideUv *slideuvs=NULL, *suv=NULL, *suv_last=NULL; float uv_tmp[2]; LinkNode *fuv_link; short event, draw=1; short mval[2], mvalo[2]; char str[128]; float labda = 0.0f; // initNumInput(&num); // view3d_get_object_project_mat(curarea, obedit, projectMat); mvalo[0] = -1; mvalo[1] = -1; numsel =0; // Get number of selected edges and clear some flags for(eed=em->edges.first;eed;eed=eed->next) { eed->f1 = 0; eed->f2 = 0; if(eed->f & SELECT) numsel++; } for(ev=em->verts.first;ev;ev=ev->next) { ev->f1 = 0; } //Make sure each edge only has 2 faces // make sure loop doesn't cross face for(efa=em->faces.first;efa;efa=efa->next) { int ct = 0; if(efa->e1->f & SELECT) { ct++; efa->e1->f1++; if(efa->e1->f1 > 2) { BKE_report(op->reports, RPT_ERROR, "3+ face edge"); return 0; } } if(efa->e2->f & SELECT) { ct++; efa->e2->f1++; if(efa->e2->f1 > 2) { BKE_report(op->reports, RPT_ERROR, "3+ face edge"); return 0; } } if(efa->e3->f & SELECT) { ct++; efa->e3->f1++; if(efa->e3->f1 > 2) { BKE_report(op->reports, RPT_ERROR, "3+ face edge"); return 0; } } if(efa->e4 && efa->e4->f & SELECT) { ct++; efa->e4->f1++; if(efa->e4->f1 > 2) { BKE_report(op->reports, RPT_ERROR, "3+ face edge"); return 0; } } // Make sure loop is not 2 edges of same face if(ct > 1) { BKE_report(op->reports, RPT_ERROR, "Loop crosses itself"); return 0; } } // Get # of selected verts for(ev=em->verts.first;ev;ev=ev->next) { if(ev->f & SELECT) vertsel++; } // Test for multiple segments if(vertsel > numsel+1) { BKE_report(op->reports, RPT_ERROR, "Please choose a single edge loop"); return 0; } // Get the edgeloop in order - mark f1 with SELECT once added for(eed=em->edges.first;eed;eed=eed->next) { if((eed->f & SELECT) && !(eed->f1 & SELECT)) { // If this is the first edge added, just put it in if(!edgelist) { BLI_linklist_prepend(&edgelist,eed); numadded++; first = eed; last = eed; eed->f1 = SELECT; } else { if(editedge_getSharedVert(eed, last)) { BLI_linklist_append(&edgelist,eed); eed->f1 = SELECT; numadded++; last = eed; } else if(editedge_getSharedVert(eed, first)) { BLI_linklist_prepend(&edgelist,eed); eed->f1 = SELECT; numadded++; first = eed; } } } if(eed->next == NULL && numadded != numsel) { eed=em->edges.first; timesthrough++; } // It looks like there was an unexpected case - Hopefully should not happen if(timesthrough >= numsel*2) { BLI_linklist_free(edgelist,NULL); BKE_report(op->reports, RPT_ERROR, "Could not order loop"); return 0; } } // Put the verts in order in a linklist look = edgelist; while(look) { eed = look->link; if(!vertlist) { if(look->next) { temp = look->next->link; //This is the first entry takes care of extra vert if(eed->v1 != temp->v1 && eed->v1 != temp->v2) { BLI_linklist_append(&vertlist,eed->v1); eed->v1->f1 = 1; } else { BLI_linklist_append(&vertlist,eed->v2); eed->v2->f1 = 1; } } else { //This is the case that we only have 1 edge BLI_linklist_append(&vertlist,eed->v1); eed->v1->f1 = 1; } } // for all the entries if(eed->v1->f1 != 1) { BLI_linklist_append(&vertlist,eed->v1); eed->v1->f1 = 1; } else if(eed->v2->f1 != 1) { BLI_linklist_append(&vertlist,eed->v2); eed->v2->f1 = 1; } look = look->next; } // populate the SlideVerts vertgh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "EdgeSlide gh"); look = vertlist; while(look) { i=0; j=0; ev = look->link; tempsv = (struct SlideVert*)MEM_mallocN(sizeof(struct SlideVert),"SlideVert"); tempsv->up = NULL; tempsv->down = NULL; tempsv->origvert.co[0] = ev->co[0]; tempsv->origvert.co[1] = ev->co[1]; tempsv->origvert.co[2] = ev->co[2]; tempsv->origvert.no[0] = ev->no[0]; tempsv->origvert.no[1] = ev->no[1]; tempsv->origvert.no[2] = ev->no[2]; // i is total edges that vert is on // j is total selected edges that vert is on for(eed=em->edges.first;eed;eed=eed->next) { if(eed->v1 == ev || eed->v2 == ev) { i++; if(eed->f & SELECT) { j++; } } } // If the vert is in the middle of an edge loop, it touches 2 selected edges and 2 unselected edges if(i == 4 && j == 2) { for(eed=em->edges.first;eed;eed=eed->next) { if(editedge_containsVert(eed, ev)) { if(!(eed->f & SELECT)) { if(!tempsv->up) { tempsv->up = eed; } else if (!(tempsv->down)) { tempsv->down = eed; } } } } } // If it is on the end of the loop, it touches 1 selected and as least 2 more unselected if(i >= 3 && j == 1) { for(eed=em->edges.first;eed;eed=eed->next) { if(editedge_containsVert(eed, ev) && eed->f & SELECT) { for(efa = em->faces.first;efa;efa=efa->next) { if(editface_containsEdge(efa, eed)) { if(editedge_containsVert(efa->e1, ev) && efa->e1 != eed) { if(!tempsv->up) { tempsv->up = efa->e1; } else if (!(tempsv->down)) { tempsv->down = efa->e1; } } if(editedge_containsVert(efa->e2, ev) && efa->e2 != eed) { if(!tempsv->up) { tempsv->up = efa->e2; } else if (!(tempsv->down)) { tempsv->down = efa->e2; } } if(editedge_containsVert(efa->e3, ev) && efa->e3 != eed) { if(!tempsv->up) { tempsv->up = efa->e3; } else if (!(tempsv->down)) { tempsv->down = efa->e3; } } if(efa->e4) { if(editedge_containsVert(efa->e4, ev) && efa->e4 != eed) { if(!tempsv->up) { tempsv->up = efa->e4; } else if (!(tempsv->down)) { tempsv->down = efa->e4; } } } } } } } } if(i > 4 && j == 2) { BLI_ghash_free(vertgh, NULL, (GHashValFreeFP)MEM_freeN); BLI_linklist_free(vertlist,NULL); BLI_linklist_free(edgelist,NULL); return 0; } BLI_ghash_insert(vertgh,ev,tempsv); look = look->next; } // make sure the UPs nad DOWNs are 'faceloops' // Also find the nearest slidevert to the cursor // XXX getmouseco_areawin(mval); look = vertlist; nearest = NULL; vertdist = -1; while(look) { tempsv = BLI_ghash_lookup(vertgh,(EditVert*)look->link); if(!tempsv->up || !tempsv->down) { BKE_report(op->reports, RPT_ERROR, "Missing rails"); BLI_ghash_free(vertgh, NULL, (GHashValFreeFP)MEM_freeN); BLI_linklist_free(vertlist,NULL); BLI_linklist_free(edgelist,NULL); return 0; } if(me->drawflag & ME_DRAW_EDGELEN) { if(!(tempsv->up->f & SELECT)) { tempsv->up->f |= SELECT; tempsv->up->f2 |= 16; } else { tempsv->up->f2 |= ~16; } if(!(tempsv->down->f & SELECT)) { tempsv->down->f |= SELECT; tempsv->down->f2 |= 16; } else { tempsv->down->f2 |= ~16; } } if(look->next != NULL) { SlideVert *sv; sv = BLI_ghash_lookup(vertgh,(EditVert*)look->next->link); if(sv) { float tempdist, co[2]; if(!sharesFace(em, tempsv->up,sv->up)) { EditEdge *swap; swap = sv->up; sv->up = sv->down; sv->down = swap; } // view3d_project_float(curarea, tempsv->origvert.co, co, projectMat); tempdist = sqrt(pow(co[0] - mval[0],2)+pow(co[1] - mval[1],2)); if(vertdist < 0) { vertdist = tempdist; nearest = (EditVert*)look->link; } else if ( tempdist < vertdist ) { vertdist = tempdist; nearest = (EditVert*)look->link; } } } look = look->next; } if (uvlay_tot) { // XXX && (scene->toolsettings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) { int maxnum = 0; uvarray = MEM_callocN( uvlay_tot * sizeof(GHash *), "SlideUVs Array"); suv_last = slideuvs = MEM_callocN( uvlay_tot * (numadded+1) * sizeof(SlideUv), "SlideUVs"); /* uvLayers * verts */ suv = NULL; for (uvlay_idx=0; uvlay_idxverts.first;ev;ev=ev->next) { ev->tmp.l = 0; } look = vertlist; while(look) { float *uv_new; tempsv = BLI_ghash_lookup(vertgh,(EditVert*)look->link); ev = look->link; suv = NULL; for(efa = em->faces.first;efa;efa=efa->next) { if (ev->tmp.l != -1) { /* test for self, in this case its invalid */ int k=-1; /* face corner */ /* Is this vert in the faces corner? */ if (efa->v1==ev) k=0; else if (efa->v2==ev) k=1; else if (efa->v3==ev) k=2; else if (efa->v4 && efa->v4==ev) k=3; if (k != -1) { MTFace *tf = CustomData_em_get_n(&em->fdata, efa->data, CD_MTFACE, uvlay_idx); EditVert *ev_up, *ev_down; uv_new = tf->uv[k]; if (ev->tmp.l) { if (fabs(suv->origuv[0]-uv_new[0]) > 0.0001 || fabs(suv->origuv[1]-uv_new[1])) { ev->tmp.l = -1; /* Tag as invalid */ BLI_linklist_free(suv->fuv_list,NULL); suv->fuv_list = NULL; BLI_ghash_remove(uvarray[uvlay_idx],ev, NULL, NULL); suv = NULL; break; } } else { ev->tmp.l = 1; suv = suv_last; suv->fuv_list = NULL; suv->uv_up = suv->uv_down = NULL; suv->origuv[0] = uv_new[0]; suv->origuv[1] = uv_new[1]; BLI_linklist_prepend(&suv->fuv_list, uv_new); BLI_ghash_insert(uvarray[uvlay_idx],ev,suv); suv_last++; /* advance to next slide UV */ maxnum++; } /* Now get the uvs along the up or down edge if we can */ if (suv) { if (!suv->uv_up) { ev_up = editedge_getOtherVert(tempsv->up,ev); if (efa->v1==ev_up) suv->uv_up = tf->uv[0]; else if (efa->v2==ev_up) suv->uv_up = tf->uv[1]; else if (efa->v3==ev_up) suv->uv_up = tf->uv[2]; else if (efa->v4 && efa->v4==ev_up) suv->uv_up = tf->uv[3]; } if (!suv->uv_down) { /* if the first face was apart of the up edge, it cant be apart of the down edge */ ev_down = editedge_getOtherVert(tempsv->down,ev); if (efa->v1==ev_down) suv->uv_down = tf->uv[0]; else if (efa->v2==ev_down) suv->uv_down = tf->uv[1]; else if (efa->v3==ev_down) suv->uv_down = tf->uv[2]; else if (efa->v4 && efa->v4==ev_down) suv->uv_down = tf->uv[3]; } /* Copy the pointers to the face UV's */ BLI_linklist_prepend(&suv->fuv_list, uv_new); } } } } look = look->next; } } /* end uv layer loop */ } /* end uvlay_tot */ // we should have enough info now to slide len = 0.0f; percp = -1; while(draw) { /* For the % calculation */ short mval[2]; float rc[2]; float v2[2], v3[2]; EditVert *centerVert, *upVert, *downVert; // XXX getmouseco_areawin(mval); if (!immediate && (mval[0] == mvalo[0] && mval[1] == mvalo[1])) { PIL_sleep_ms(10); } else { char *p = str; int ctrl= 0, shift= 0; // XXX mvalo[0] = mval[0]; mvalo[1] = mval[1]; tempsv = BLI_ghash_lookup(vertgh,nearest); centerVert = editedge_getSharedVert(tempsv->up, tempsv->down); upVert = editedge_getOtherVert(tempsv->up, centerVert); downVert = editedge_getOtherVert(tempsv->down, centerVert); // view3d_project_float(curarea, upVert->co, v2, projectMat); // view3d_project_float(curarea, downVert->co, v3, projectMat); /* Determine the % on which the loop should be cut */ rc[0]= v3[0]-v2[0]; rc[1]= v3[1]-v2[1]; len= rc[0]*rc[0]+ rc[1]*rc[1]; if (len==0) {len = 0.0001;} if (shift) { wasshift = 0; labda= ( rc[0]*((mval[0]-v2[0])) + rc[1]*((mval[1]-v2[1])) )/len; } else { if (wasshift==0) { wasshift = 1; shiftlabda = labda; } labda= ( rc[0]*((mval[0]-v2[0])) + rc[1]*((mval[1]-v2[1])) )/len / 10.0 + shiftlabda; } if(labda<=0.0) labda=0.0; else if(labda>=1.0)labda=1.0; perc=((1-labda)*2)-1; if(shift == 0 && ctrl==0) { perc *= 100; perc = floor(perc); perc /= 100; } else if (ctrl) { perc *= 10; perc = floor(perc); perc /= 10; } if(prop == 0) { len = len_v3v3(upVert->co,downVert->co)*((perc+1)/2); if(flip == 1) { len = len_v3v3(upVert->co,downVert->co) - len; } } if (0) // XXX hasNumInput(&num)) { // XXX applyNumInput(&num, &perc); if (prop) { perc = MIN2(perc, 1); perc = MAX2(perc, -1); } else { len = MIN2(perc, len_v3v3(upVert->co,downVert->co)); len = MAX2(len, 0); } } //Adjust Edgeloop if(immediate) { perc = imperc; } percp = perc; if(prop) { look = vertlist; while(look) { EditVert *tempev; ev = look->link; tempsv = BLI_ghash_lookup(vertgh,ev); tempev = editedge_getOtherVert((perc>=0)?tempsv->up:tempsv->down, ev); interp_v3_v3v3(ev->co, tempsv->origvert.co, tempev->co, fabs(perc)); if (0) { // XXX scene->toolsettings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) { for (uvlay_idx=0; uvlay_idxfuv_list && suv->uv_up && suv->uv_down) { interp_v2_v2v2(uv_tmp, suv->origuv, (perc>=0)?suv->uv_up:suv->uv_down, fabs(perc)); fuv_link = suv->fuv_list; while (fuv_link) { VECCOPY2D(((float *)fuv_link->link), uv_tmp); fuv_link = fuv_link->next; } } } } look = look->next; } } else { //Non prop code look = vertlist; while(look) { float newlen; ev = look->link; tempsv = BLI_ghash_lookup(vertgh,ev); newlen = (len / len_v3v3(editedge_getOtherVert(tempsv->up,ev)->co,editedge_getOtherVert(tempsv->down,ev)->co)); if(newlen > 1.0) {newlen = 1.0;} if(newlen < 0.0) {newlen = 0.0;} if(flip == 0) { interp_v3_v3v3(ev->co, editedge_getOtherVert(tempsv->down,ev)->co, editedge_getOtherVert(tempsv->up,ev)->co, fabs(newlen)); if (0) { // XXX scene->toolsettings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) { /* dont do anything if no UVs */ for (uvlay_idx=0; uvlay_idxfuv_list && suv->uv_up && suv->uv_down) { interp_v2_v2v2(uv_tmp, suv->uv_down, suv->uv_up, fabs(newlen)); fuv_link = suv->fuv_list; while (fuv_link) { VECCOPY2D(((float *)fuv_link->link), uv_tmp); fuv_link = fuv_link->next; } } } } } else{ interp_v3_v3v3(ev->co, editedge_getOtherVert(tempsv->up,ev)->co, editedge_getOtherVert(tempsv->down,ev)->co, fabs(newlen)); if (0) { // XXX scene->toolsettings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT) { /* dont do anything if no UVs */ for (uvlay_idx=0; uvlay_idxfuv_list && suv->uv_up && suv->uv_down) { interp_v2_v2v2(uv_tmp, suv->uv_up, suv->uv_down, fabs(newlen)); fuv_link = suv->fuv_list; while (fuv_link) { VECCOPY2D(((float *)fuv_link->link), uv_tmp); fuv_link = fuv_link->next; } } } } } look = look->next; } } // Highlight the Control Edges // scrarea_do_windraw(curarea); // persp(PERSP_VIEW); // glPushMatrix(); // mymultmatrix(obedit->obmat); glColor3ub(0, 255, 0); glBegin(GL_LINES); glVertex3fv(upVert->co); glVertex3fv(downVert->co); glEnd(); if(prop == 0) { // draw start edge for non-prop glPointSize(5); glBegin(GL_POINTS); glColor3ub(255,0,255); if(flip) { glVertex3fv(upVert->co); } else { glVertex3fv(downVert->co); } glEnd(); } glPopMatrix(); if(prop) { p += sprintf(str, "(P)ercentage: "); } else { p += sprintf(str, "Non (P)rop Length: "); } if (0) // XXX hasNumInput(&num)) { char num_str[20]; // XX outputNumInput(&num, num_str); p += sprintf(p, "%s", num_str); } else { if (prop) { p += sprintf(p, "%f", perc); } else { p += sprintf(p, "%f", len); } } if (prop == 0) { p += sprintf(p, ", Press (F) to flip control side"); } // headerprint(str); // screen_swapbuffers(); } if(!immediate) { while(qtest()) { short val=0; event= extern_qread(&val); // extern_qread stores important events for the mainloop to handle /* val==0 on key-release event */ if (val) { if(ELEM(event, ESCKEY, RIGHTMOUSE)) { prop = 1; // Go back to prop mode imperc = 0; // This is the % that gets set for immediate immediate = 1; //Run through eval code 1 more time cancel = 1; // Return -1 mvalo[0] = -1; } else if(ELEM3(event, PADENTER, LEFTMOUSE, RETKEY)) { draw = 0; // End looping now } else if(event==MIDDLEMOUSE) { perc = 0; immediate = 1; } else if(event==PKEY) { // XXX initNumInput(&num); /* reset num input */ if (prop) { prop = 0; // XXX num.flag |= NUM_NO_NEGATIVE; } else { prop = 1; } mvalo[0] = -1; } else if(event==FKEY) { (flip == 1) ? (flip = 0):(flip = 1); mvalo[0] = -1; } else if(ELEM(event, RIGHTARROWKEY, WHEELUPMOUSE)) { // Scroll through Control Edges look = vertlist; while(look) { if(nearest == (EditVert*)look->link) { if(look->next == NULL) { nearest = (EditVert*)vertlist->link; } else { nearest = (EditVert*)look->next->link; } mvalo[0] = -1; break; } look = look->next; } } else if(ELEM(event, LEFTARROWKEY, WHEELDOWNMOUSE)) { // Scroll through Control Edges look = vertlist; while(look) { if(look->next) { if(look->next->link == nearest) { nearest = (EditVert*)look->link; mvalo[0] = -1; break; } } else { if((EditVert*)vertlist->link == nearest) { nearest = look->link; mvalo[0] = -1; break; } } look = look->next; } } // XXX if (handleNumInput(&num, event)) { mvalo[0] = -1; /* NEED A BETTER WAY TO TRIGGER REDRAW */ } } } } else { draw = 0; } // DAG_id_flush_update(obedit->data, OB_RECALC_DATA); } if(me->drawflag & ME_DRAW_EDGELEN) { look = vertlist; while(look) { tempsv = BLI_ghash_lookup(vertgh,(EditVert*)look->link); if(tempsv != NULL) { tempsv->up->f &= !SELECT; tempsv->down->f &= !SELECT; } look = look->next; } } // force_draw(0); if(!immediate) EM_automerge(0); // DAG_id_flush_update(obedit->data, OB_RECALC_DATA); // scrarea_queue_winredraw(curarea); //BLI_ghash_free(edgesgh, freeGHash, NULL); BLI_ghash_free(vertgh, NULL, (GHashValFreeFP)MEM_freeN); BLI_linklist_free(vertlist,NULL); BLI_linklist_free(edgelist,NULL); if (uvlay_tot) { // XXX && (scene->toolsettings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) { for (uvlay_idx=0; uvlay_idx= slideuvs) { if (suv->fuv_list) { BLI_linklist_free(suv->fuv_list,NULL); } suv--; } } if(cancel == 1) { return -1; } return 1; } #endif // END OF XXX int EdgeLoopDelete(EditMesh *em, wmOperator *op) { #if 0 //XXX won't work with new edgeslide /* temporal flag setting so we keep UVs when deleting edge loops, * this is a bit of a hack but it works how you would want in almost all cases */ // short uvcalc_flag_orig = 0; // XXX scene->toolsettings->uvcalc_flag; // scene->toolsettings->uvcalc_flag |= UVCALC_TRANSFORM_CORRECT; if(!EdgeSlide(em, op, 1, 1)) { return 0; } /* restore uvcalc flag */ // scene->toolsettings->uvcalc_flag = uvcalc_flag_orig; EM_select_more(em); removedoublesflag(em, 1,0, 0.001); EM_select_flush(em); // DAG_id_flush_update(obedit->data, OB_RECALC_DATA); return 1; #endif return 0; } /* -------------------- More tools ------------------ */ #if 0 void mesh_set_face_flags(EditMesh *em, short mode) { EditFace *efa; MTFace *tface; short m_tex=0, m_shared=0, m_light=0, m_invis=0, m_collision=0, m_twoside=0, m_obcolor=0, m_halo=0, m_billboard=0, m_shadow=0, m_text=0, m_sort=0; short flag = 0, change = 0; // XXX if (!EM_texFaceCheck()) { // error("not a mesh with uv/image layers"); // return; // } add_numbut(0, TOG|SHO, "Texture", 0, 0, &m_tex, NULL); add_numbut(2, TOG|SHO, "Light", 0, 0, &m_light, NULL); add_numbut(3, TOG|SHO, "Invisible", 0, 0, &m_invis, NULL); add_numbut(4, TOG|SHO, "Collision", 0, 0, &m_collision, NULL); add_numbut(5, TOG|SHO, "Shared", 0, 0, &m_shared, NULL); add_numbut(6, TOG|SHO, "Twoside", 0, 0, &m_twoside, NULL); add_numbut(7, TOG|SHO, "ObColor", 0, 0, &m_obcolor, NULL); add_numbut(8, TOG|SHO, "Halo", 0, 0, &m_halo, NULL); add_numbut(9, TOG|SHO, "Billboard", 0, 0, &m_billboard, NULL); add_numbut(10, TOG|SHO, "Shadow", 0, 0, &m_shadow, NULL); add_numbut(11, TOG|SHO, "Text", 0, 0, &m_text, NULL); add_numbut(12, TOG|SHO, "Sort", 0, 0, &m_sort, NULL); if (!do_clever_numbuts((mode ? "Set Flags" : "Clear Flags"), 13, REDRAW)) return; /* these 2 cant both be on */ if (mode) /* are we seeting*/ if (m_halo) m_billboard = 0; if (m_tex) flag |= TF_TEX; if (m_shared) flag |= TF_SHAREDCOL; if (m_light) flag |= TF_LIGHT; if (m_invis) flag |= TF_INVISIBLE; if (m_collision) flag |= TF_DYNAMIC; if (m_twoside) flag |= TF_TWOSIDE; if (m_obcolor) flag |= TF_OBCOL; if (m_halo) flag |= TF_BILLBOARD; if (m_billboard) flag |= TF_BILLBOARD2; if (m_shadow) flag |= TF_SHADOW; if (m_text) flag |= TF_BMFONT; if (m_sort) flag |= TF_ALPHASORT; if (flag==0) return; efa= em->faces.first; while(efa) { if(efa->f & SELECT) { tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); if (mode) tface->mode |= flag; else tface->mode &= ~flag; change = 1; } efa= efa->next; } } #endif /********************** Rip Operator *************************/ /* helper to find edge for edge_rip */ static float mesh_rip_edgedist(ARegion *ar, float mat[][4], float *co1, float *co2, short *mval) { float vec1[3], vec2[3], mvalf[2]; view3d_project_float(ar, co1, vec1, mat); view3d_project_float(ar, co2, vec2, mat); mvalf[0]= (float)mval[0]; mvalf[1]= (float)mval[1]; return dist_to_line_segment_v2(mvalf, vec1, vec2); } /* helper for below */ static void mesh_rip_setface(EditMesh *em, EditFace *sefa) { /* put new vertices & edges in best face */ if(sefa->v1->tmp.v) sefa->v1= sefa->v1->tmp.v; if(sefa->v2->tmp.v) sefa->v2= sefa->v2->tmp.v; if(sefa->v3->tmp.v) sefa->v3= sefa->v3->tmp.v; if(sefa->v4 && sefa->v4->tmp.v) sefa->v4= sefa->v4->tmp.v; sefa->e1= addedgelist(em, sefa->v1, sefa->v2, sefa->e1); sefa->e2= addedgelist(em, sefa->v2, sefa->v3, sefa->e2); if(sefa->v4) { sefa->e3= addedgelist(em, sefa->v3, sefa->v4, sefa->e3); sefa->e4= addedgelist(em, sefa->v4, sefa->v1, sefa->e4); } else sefa->e3= addedgelist(em, sefa->v3, sefa->v1, sefa->e3); } /* based on mouse cursor position, it defines how is being ripped */ static int mesh_rip_invoke(bContext *C, wmOperator *op, wmEvent *event) { ARegion *ar= CTX_wm_region(C); RegionView3D *rv3d= ar->regiondata; Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data); EditVert *eve, *nextve; EditEdge *eed, *seed= NULL; EditFace *efa, *sefa= NULL; float projectMat[4][4], vec[3], dist, mindist; short doit= 1, *mval= event->mval; /* select flush... vertices are important */ EM_selectmode_set(em); view3d_get_object_project_mat(rv3d, obedit, projectMat); /* find best face, exclude triangles and break on face select or faces with 2 edges select */ mindist= 1000000.0f; for(efa= em->faces.first; efa; efa=efa->next) { if( efa->f & 1) break; if(efa->v4 && faceselectedOR(efa, SELECT) ) { int totsel=0; if(efa->e1->f & SELECT) totsel++; if(efa->e2->f & SELECT) totsel++; if(efa->e3->f & SELECT) totsel++; if(efa->e4->f & SELECT) totsel++; if(totsel>1) break; view3d_project_float(ar, efa->cent, vec, projectMat); dist= sqrt( (vec[0]-mval[0])*(vec[0]-mval[0]) + (vec[1]-mval[1])*(vec[1]-mval[1]) ); if(distreports, RPT_ERROR, "Can't perform ripping with faces selected this way"); BKE_mesh_end_editmesh(obedit->data, em); return OPERATOR_CANCELLED; } if(sefa==NULL) { BKE_report(op->reports, RPT_ERROR, "No proper selection or faces included"); BKE_mesh_end_editmesh(obedit->data, em); return OPERATOR_CANCELLED; } /* duplicate vertices, new vertices get selected */ for(eve = em->verts.last; eve; eve= eve->prev) { eve->tmp.v = NULL; if(eve->f & SELECT) { eve->tmp.v = addvertlist(em, eve->co, eve); eve->f &= ~SELECT; eve->tmp.v->f |= SELECT; } } /* find the best candidate edge */ /* or one of sefa edges is selected... */ if(sefa->e1->f & SELECT) seed= sefa->e2; if(sefa->e2->f & SELECT) seed= sefa->e1; if(sefa->e3->f & SELECT) seed= sefa->e2; if(sefa->e4 && sefa->e4->f & SELECT) seed= sefa->e3; /* or we do the distance trick */ if(seed==NULL) { mindist= 1000000.0f; if(sefa->e1->v1->tmp.v || sefa->e1->v2->tmp.v) { dist = mesh_rip_edgedist(ar, projectMat, sefa->e1->v1->co, sefa->e1->v2->co, mval); if(diste1; mindist= dist; } } if(sefa->e2->v1->tmp.v || sefa->e2->v2->tmp.v) { dist = mesh_rip_edgedist(ar, projectMat, sefa->e2->v1->co, sefa->e2->v2->co, mval); if(diste2; mindist= dist; } } if(sefa->e3->v1->tmp.v || sefa->e3->v2->tmp.v) { dist= mesh_rip_edgedist(ar, projectMat, sefa->e3->v1->co, sefa->e3->v2->co, mval); if(diste3; mindist= dist; } } if(sefa->e4 && (sefa->e4->v1->tmp.v || sefa->e4->v2->tmp.v)) { dist= mesh_rip_edgedist(ar, projectMat, sefa->e4->v1->co, sefa->e4->v2->co, mval); if(diste4; mindist= dist; } } } if(seed==NULL) { // never happens? BKE_report(op->reports, RPT_ERROR, "No proper edge found to start"); BKE_mesh_end_editmesh(obedit->data, em); return OPERATOR_CANCELLED; } faceloop_select(em, seed, 2); // tmp abuse for finding all edges that need duplicated, returns OK faces with f1 /* duplicate edges in the loop, with at least 1 vertex selected, needed for selection flip */ for(eed = em->edges.last; eed; eed= eed->prev) { eed->tmp.v = NULL; if((eed->v1->tmp.v) || (eed->v2->tmp.v)) { EditEdge *newed; newed= addedgelist(em, eed->v1->tmp.v?eed->v1->tmp.v:eed->v1, eed->v2->tmp.v?eed->v2->tmp.v:eed->v2, eed); if(eed->f & SELECT) { EM_select_edge(eed, 0); EM_remove_selection(em, eed, EDITEDGE); EM_select_edge(newed, 1); } eed->tmp.v = (EditVert *)newed; } } /* first clear edges to help finding neighbours */ for(eed = em->edges.last; eed; eed= eed->prev) eed->f1= 0; /* put new vertices & edges && flag in best face */ mesh_rip_setface(em, sefa); /* starting with neighbours of best face, we loop over the seam */ sefa->f1= 2; doit= 1; while(doit) { doit= 0; for(efa= em->faces.first; efa; efa=efa->next) { /* new vert in face */ if (efa->v1->tmp.v || efa->v2->tmp.v || efa->v3->tmp.v || (efa->v4 && efa->v4->tmp.v)) { /* face is tagged with loop */ if(efa->f1==1) { mesh_rip_setface(em, efa); efa->f1= 2; doit= 1; } } } } /* remove loose edges, that were part of a ripped face */ for(eve = em->verts.first; eve; eve= eve->next) eve->f1= 0; for(eed = em->edges.last; eed; eed= eed->prev) eed->f1= 0; for(efa= em->faces.first; efa; efa=efa->next) { efa->e1->f1= 1; efa->e2->f1= 1; efa->e3->f1= 1; if(efa->e4) efa->e4->f1= 1; } for(eed = em->edges.last; eed; eed= seed) { seed= eed->prev; if(eed->f1==0) { if(eed->v1->tmp.v || eed->v2->tmp.v || (eed->v1->f & SELECT) || (eed->v2->f & SELECT)) { remedge(em, eed); free_editedge(em, eed); eed= NULL; } } if(eed) { eed->v1->f1= 1; eed->v2->f1= 1; } } /* and remove loose selected vertices, that got duplicated accidentally */ for(eve = em->verts.first; eve; eve= nextve) { nextve= eve->next; if(eve->f1==0 && (eve->tmp.v || (eve->f & SELECT))) { BLI_remlink(&em->verts,eve); free_editvert(em, eve); } } DAG_id_flush_update(obedit->data, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); BKE_mesh_end_editmesh(obedit->data, em); // RNA_enum_set(op->ptr, "proportional", 0); // RNA_boolean_set(op->ptr, "mirror", 0); // WM_operator_name_call(C, "TRANSFORM_OT_translate", WM_OP_INVOKE_REGION_WIN, op->ptr); return OPERATOR_FINISHED; } void MESH_OT_rip(wmOperatorType *ot) { /* identifiers */ ot->name= "Rip"; ot->description= "Rip selection from mesh (quads only)"; ot->idname= "MESH_OT_rip"; /* api callbacks */ ot->invoke= mesh_rip_invoke; ot->poll= EM_view3d_poll; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; /* to give to transform */ /* XXX Transform this in a macro */ Transform_Properties(ot, P_CONSTRAINT|P_MIRROR); } /************************ Shape Operators *************************/ static void shape_propagate(Object *obedit, EditMesh *em, wmOperator *op) { EditVert *ev = NULL; Mesh* me = (Mesh*)obedit->data; Key* ky = NULL; KeyBlock* kb = NULL; if(me->key){ ky = me->key; } else { BKE_report(op->reports, RPT_ERROR, "Object Has No Key"); return; } if(ky->block.first){ for(ev = em->verts.first; ev ; ev = ev->next){ if(ev->f & SELECT){ for(kb=ky->block.first;kb;kb = kb->next){ float *data; data = kb->data; VECCOPY(data+(ev->keyindex*3),ev->co); } } } } else { BKE_report(op->reports, RPT_ERROR, "Object Has No Blendshapes"); return; } #if 0 //TAG Mesh Objects that share this data for(base = scene->base.first; base; base = base->next){ if(base->object && base->object->data == me){ base->object->recalc = OB_RECALC_DATA; } } #endif DAG_id_flush_update(obedit->data, OB_RECALC_DATA); return; } static int shape_propagate_to_all_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); Mesh *me= obedit->data; EditMesh *em= BKE_mesh_get_editmesh(me); shape_propagate(obedit, em, op); DAG_id_flush_update(&me->id, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, me); return OPERATOR_FINISHED; } void MESH_OT_shape_propagate_to_all(wmOperatorType *ot) { /* identifiers */ ot->name= "Shape Propagate"; ot->description= "Apply selected vertex locations to all other shape keys"; ot->idname= "MESH_OT_shape_propagate_to_all"; /* api callbacks */ ot->exec= shape_propagate_to_all_exec; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; } static int blend_from_shape_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); Mesh *me= obedit->data; Key *key= me->key; EditMesh *em= BKE_mesh_get_editmesh(me); EditVert *eve; KeyBlock *kb; float *data, co[3]; float blend= RNA_float_get(op->ptr, "blend"); int shape= RNA_enum_get(op->ptr, "shape"); int add= RNA_int_get(op->ptr, "add"); int blended= 0; if(key && (kb= BLI_findlink(&key->block, shape))) { data= kb->data; for(eve=em->verts.first; eve; eve=eve->next){ if(eve->f & SELECT) { if(eve->keyindex >= 0 && eve->keyindex < kb->totelem) { VECCOPY(co, data + eve->keyindex*3); if(add) { mul_v3_fl(co, blend); add_v3_v3(eve->co, co); } else interp_v3_v3v3(eve->co, eve->co, co, blend); blended= 1; } } } } BKE_mesh_end_editmesh(me, em); if(!blended) return OPERATOR_CANCELLED; DAG_id_flush_update(&me->id, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, me); return OPERATOR_FINISHED; } static EnumPropertyItem *shape_itemf(bContext *C, PointerRNA *ptr, int *free) { Object *obedit= CTX_data_edit_object(C); Mesh *me= (obedit) ? obedit->data : NULL; Key *key; KeyBlock *kb, *actkb; EnumPropertyItem tmp= {0, "", 0, "", ""}, *item= NULL; int totitem= 0, a; if(obedit && obedit->type == OB_MESH) { key= me->key; actkb= ob_get_keyblock(obedit); if(key && actkb) { for(kb=key->block.first, a=0; kb; kb=kb->next, a++) { if(kb != actkb) { tmp.value= a; tmp.identifier= kb->name; tmp.name= kb->name; RNA_enum_item_add(&item, &totitem, &tmp); } } } } RNA_enum_item_end(&item, &totitem); *free= 1; return item; } void MESH_OT_blend_from_shape(wmOperatorType *ot) { PropertyRNA *prop; static EnumPropertyItem shape_items[]= {{0, NULL, 0, NULL, NULL}}; /* identifiers */ ot->name= "Blend From Shape"; ot->description= "Blend in shape from a shape key"; ot->idname= "MESH_OT_blend_from_shape"; /* api callbacks */ ot->exec= blend_from_shape_exec; ot->invoke= WM_operator_props_popup; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; /* properties */ prop= RNA_def_enum(ot->srna, "shape", shape_items, 0, "Shape", "Shape key to use for blending."); RNA_def_enum_funcs(prop, shape_itemf); RNA_def_float(ot->srna, "blend", 1.0f, -FLT_MAX, FLT_MAX, "Blend", "Blending factor.", -2.0f, 2.0f); RNA_def_boolean(ot->srna, "add", 1, "Add", "Add rather then blend between shapes."); } /************************ Merge Operator *************************/ /* Collection Routines|Currently used by the improved merge code*/ /* buildEdge_collection() creates a list of lists*/ /* these lists are filled with edges that are topologically connected.*/ /* This whole tool needs to be redone, its rather poorly implemented...*/ typedef struct Collection{ struct Collection *next, *prev; int index; ListBase collectionbase; } Collection; typedef struct CollectedEdge{ struct CollectedEdge *next, *prev; EditEdge *eed; } CollectedEdge; #define MERGELIMIT 0.000001 static void build_edgecollection(EditMesh *em, ListBase *allcollections) { EditEdge *eed; Collection *edgecollection, *newcollection; CollectedEdge *newedge; int currtag = 1; short ebalanced = 0; short collectionfound = 0; for (eed=em->edges.first; eed; eed = eed->next){ eed->tmp.l = 0; eed->v1->tmp.l = 0; eed->v2->tmp.l = 0; } /*1st pass*/ for(eed=em->edges.first; eed; eed=eed->next){ if(eed->f&SELECT){ eed->v1->tmp.l = currtag; eed->v2->tmp.l = currtag; currtag +=1; } } /*2nd pass - Brute force. Loop through selected faces until there are no 'unbalanced' edges left (those with both vertices 'tmp.l' tag matching */ while(ebalanced == 0){ ebalanced = 1; for(eed=em->edges.first; eed; eed = eed->next){ if(eed->f&SELECT){ if(eed->v1->tmp.l != eed->v2->tmp.l) /*unbalanced*/{ if(eed->v1->tmp.l > eed->v2->tmp.l && eed->v2->tmp.l !=0) eed->v1->tmp.l = eed->v2->tmp.l; else if(eed->v1 != 0) eed->v2->tmp.l = eed->v1->tmp.l; ebalanced = 0; } } } } /*3rd pass, set all the edge flags (unnessecary?)*/ for(eed=em->edges.first; eed; eed = eed->next){ if(eed->f&SELECT) eed->tmp.l = eed->v1->tmp.l; } for(eed=em->edges.first; eed; eed=eed->next){ if(eed->f&SELECT){ if(allcollections->first){ for(edgecollection = allcollections->first; edgecollection; edgecollection=edgecollection->next){ if(edgecollection->index == eed->tmp.l){ newedge = MEM_mallocN(sizeof(CollectedEdge), "collected edge"); newedge->eed = eed; BLI_addtail(&(edgecollection->collectionbase), newedge); collectionfound = 1; break; } else collectionfound = 0; } } if(allcollections->first == NULL || collectionfound == 0){ newcollection = MEM_mallocN(sizeof(Collection), "element collection"); newcollection->index = eed->tmp.l; newcollection->collectionbase.first = 0; newcollection->collectionbase.last = 0; newedge = MEM_mallocN(sizeof(CollectedEdge), "collected edge"); newedge->eed = eed; BLI_addtail(&(newcollection->collectionbase), newedge); BLI_addtail(allcollections, newcollection); } } } } static void freecollections(ListBase *allcollections) { struct Collection *curcollection; for(curcollection = allcollections->first; curcollection; curcollection = curcollection->next) BLI_freelistN(&(curcollection->collectionbase)); BLI_freelistN(allcollections); } /*Begin UV Edge Collapse Code Like Edge subdivide, Edge Collapse should handle UV's intelligently, but since UV's are a per-face attribute, normal edge collapse will fail in areas such as the boundries of 'UV islands'. So for each edge collection we need to build a set of 'welded' UV vertices and edges for it. The welded UV edges can then be sorted and collapsed. */ typedef struct wUV{ struct wUV *next, *prev; ListBase nodes; float u, v; /*cached copy of UV coordinates pointed to by nodes*/ EditVert *eve; int f; } wUV; typedef struct wUVNode{ struct wUVNode *next, *prev; float *u; /*pointer to original tface data*/ float *v; /*pointer to original tface data*/ } wUVNode; typedef struct wUVEdge{ struct wUVEdge *next, *prev; float v1uv[2], v2uv[2]; /*nasty.*/ struct wUV *v1, *v2; /*oriented same as editedge*/ EditEdge *eed; int f; } wUVEdge; typedef struct wUVEdgeCollect{ /*used for grouping*/ struct wUVEdgeCollect *next, *prev; wUVEdge *uved; int id; } wUVEdgeCollect; static void append_weldedUV(EditMesh *em, EditFace *efa, EditVert *eve, int tfindex, ListBase *uvverts) { wUV *curwvert, *newwvert; wUVNode *newnode; int found; MTFace *tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); found = 0; for(curwvert=uvverts->first; curwvert; curwvert=curwvert->next){ if(curwvert->eve == eve && curwvert->u == tf->uv[tfindex][0] && curwvert->v == tf->uv[tfindex][1]){ newnode = MEM_callocN(sizeof(wUVNode), "Welded UV Vert Node"); newnode->u = &(tf->uv[tfindex][0]); newnode->v = &(tf->uv[tfindex][1]); BLI_addtail(&(curwvert->nodes), newnode); found = 1; break; } } if(!found){ newnode = MEM_callocN(sizeof(wUVNode), "Welded UV Vert Node"); newnode->u = &(tf->uv[tfindex][0]); newnode->v = &(tf->uv[tfindex][1]); newwvert = MEM_callocN(sizeof(wUV), "Welded UV Vert"); newwvert->u = *(newnode->u); newwvert->v = *(newnode->v); newwvert->eve = eve; BLI_addtail(&(newwvert->nodes), newnode); BLI_addtail(uvverts, newwvert); } } static void build_weldedUVs(EditMesh *em, ListBase *uvverts) { EditFace *efa; for(efa=em->faces.first; efa; efa=efa->next){ if(efa->v1->f1) append_weldedUV(em, efa, efa->v1, 0, uvverts); if(efa->v2->f1) append_weldedUV(em, efa, efa->v2, 1, uvverts); if(efa->v3->f1) append_weldedUV(em, efa, efa->v3, 2, uvverts); if(efa->v4 && efa->v4->f1) append_weldedUV(em, efa, efa->v4, 3, uvverts); } } static void append_weldedUVEdge(EditMesh *em, EditFace *efa, EditEdge *eed, ListBase *uvedges) { wUVEdge *curwedge, *newwedge; int v1tfindex, v2tfindex, found; MTFace *tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); found = 0; if(eed->v1 == efa->v1) v1tfindex = 0; else if(eed->v1 == efa->v2) v1tfindex = 1; else if(eed->v1 == efa->v3) v1tfindex = 2; else /* if(eed->v1 == efa->v4) */ v1tfindex = 3; if(eed->v2 == efa->v1) v2tfindex = 0; else if(eed->v2 == efa->v2) v2tfindex = 1; else if(eed->v2 == efa->v3) v2tfindex = 2; else /* if(eed->v2 == efa->v4) */ v2tfindex = 3; for(curwedge=uvedges->first; curwedge; curwedge=curwedge->next){ if(curwedge->eed == eed && curwedge->v1uv[0] == tf->uv[v1tfindex][0] && curwedge->v1uv[1] == tf->uv[v1tfindex][1] && curwedge->v2uv[0] == tf->uv[v2tfindex][0] && curwedge->v2uv[1] == tf->uv[v2tfindex][1]){ found = 1; break; //do nothing, we don't need another welded uv edge } } if(!found){ newwedge = MEM_callocN(sizeof(wUVEdge), "Welded UV Edge"); newwedge->v1uv[0] = tf->uv[v1tfindex][0]; newwedge->v1uv[1] = tf->uv[v1tfindex][1]; newwedge->v2uv[0] = tf->uv[v2tfindex][0]; newwedge->v2uv[1] = tf->uv[v2tfindex][1]; newwedge->eed = eed; BLI_addtail(uvedges, newwedge); } } static void build_weldedUVEdges(EditMesh *em, ListBase *uvedges, ListBase *uvverts) { wUV *curwvert; wUVEdge *curwedge; EditFace *efa; for(efa=em->faces.first; efa; efa=efa->next){ if(efa->e1->f1) append_weldedUVEdge(em, efa, efa->e1, uvedges); if(efa->e2->f1) append_weldedUVEdge(em, efa, efa->e2, uvedges); if(efa->e3->f1) append_weldedUVEdge(em, efa, efa->e3, uvedges); if(efa->e4 && efa->e4->f1) append_weldedUVEdge(em, efa, efa->e4, uvedges); } //link vertices: for each uvedge, search uvverts to populate v1 and v2 pointers for(curwedge=uvedges->first; curwedge; curwedge=curwedge->next){ for(curwvert=uvverts->first; curwvert; curwvert=curwvert->next){ if(curwedge->eed->v1 == curwvert->eve && curwedge->v1uv[0] == curwvert->u && curwedge->v1uv[1] == curwvert->v){ curwedge->v1 = curwvert; break; } } for(curwvert=uvverts->first; curwvert; curwvert=curwvert->next){ if(curwedge->eed->v2 == curwvert->eve && curwedge->v2uv[0] == curwvert->u && curwedge->v2uv[1] == curwvert->v){ curwedge->v2 = curwvert; break; } } } } static void free_weldedUVs(ListBase *uvverts) { wUV *curwvert; for(curwvert = uvverts->first; curwvert; curwvert=curwvert->next) BLI_freelistN(&(curwvert->nodes)); BLI_freelistN(uvverts); } static void collapse_edgeuvs(EditMesh *em) { ListBase uvedges, uvverts, allcollections; wUVEdge *curwedge; wUVNode *curwnode; wUVEdgeCollect *collectedwuve, *newcollectedwuve; Collection *wuvecollection, *newcollection; int curtag, balanced, collectionfound= 0, vcount; float avg[2]; if (!EM_texFaceCheck(em)) return; uvverts.first = uvverts.last = uvedges.first = uvedges.last = allcollections.first = allcollections.last = NULL; build_weldedUVs(em, &uvverts); build_weldedUVEdges(em, &uvedges, &uvverts); curtag = 0; for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next){ curwedge->v1->f = curtag; curwedge->v2->f = curtag; curtag +=1; } balanced = 0; while(!balanced){ balanced = 1; for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next){ if(curwedge->v1->f != curwedge->v2->f){ if(curwedge->v1->f > curwedge->v2->f) curwedge->v1->f = curwedge->v2->f; else curwedge->v2->f = curwedge->v1->f; balanced = 0; } } } for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next) curwedge->f = curwedge->v1->f; for(curwedge=uvedges.first; curwedge; curwedge=curwedge->next){ if(allcollections.first){ for(wuvecollection = allcollections.first; wuvecollection; wuvecollection=wuvecollection->next){ if(wuvecollection->index == curwedge->f){ newcollectedwuve = MEM_callocN(sizeof(wUVEdgeCollect), "Collected Welded UV Edge"); newcollectedwuve->uved = curwedge; BLI_addtail(&(wuvecollection->collectionbase), newcollectedwuve); collectionfound = 1; break; } else collectionfound = 0; } } if(allcollections.first == NULL || collectionfound == 0){ newcollection = MEM_callocN(sizeof(Collection), "element collection"); newcollection->index = curwedge->f; newcollection->collectionbase.first = 0; newcollection->collectionbase.last = 0; newcollectedwuve = MEM_callocN(sizeof(wUVEdgeCollect), "Collected Welded UV Edge"); newcollectedwuve->uved = curwedge; BLI_addtail(&(newcollection->collectionbase), newcollectedwuve); BLI_addtail(&allcollections, newcollection); } } for(wuvecollection=allcollections.first; wuvecollection; wuvecollection=wuvecollection->next){ vcount = avg[0] = avg[1] = 0; for(collectedwuve= wuvecollection->collectionbase.first; collectedwuve; collectedwuve = collectedwuve->next){ avg[0] += collectedwuve->uved->v1uv[0]; avg[1] += collectedwuve->uved->v1uv[1]; avg[0] += collectedwuve->uved->v2uv[0]; avg[1] += collectedwuve->uved->v2uv[1]; vcount +=2; } avg[0] /= vcount; avg[1] /= vcount; for(collectedwuve= wuvecollection->collectionbase.first; collectedwuve; collectedwuve = collectedwuve->next){ for(curwnode=collectedwuve->uved->v1->nodes.first; curwnode; curwnode=curwnode->next){ *(curwnode->u) = avg[0]; *(curwnode->v) = avg[1]; } for(curwnode=collectedwuve->uved->v2->nodes.first; curwnode; curwnode=curwnode->next){ *(curwnode->u) = avg[0]; *(curwnode->v) = avg[1]; } } } free_weldedUVs(&uvverts); BLI_freelistN(&uvedges); freecollections(&allcollections); } /*End UV Edge collapse code*/ static void collapseuvs(EditMesh *em, EditVert *mergevert) { EditFace *efa; MTFace *tf; int uvcount; float uvav[2]; if (!EM_texFaceCheck(em)) return; uvcount = 0; uvav[0] = 0; uvav[1] = 0; for(efa = em->faces.first; efa; efa=efa->next){ tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); if(efa->v1->f1 && ELEM(mergevert, NULL, efa->v1)) { uvav[0] += tf->uv[0][0]; uvav[1] += tf->uv[0][1]; uvcount += 1; } if(efa->v2->f1 && ELEM(mergevert, NULL, efa->v2)){ uvav[0] += tf->uv[1][0]; uvav[1] += tf->uv[1][1]; uvcount += 1; } if(efa->v3->f1 && ELEM(mergevert, NULL, efa->v3)){ uvav[0] += tf->uv[2][0]; uvav[1] += tf->uv[2][1]; uvcount += 1; } if(efa->v4 && efa->v4->f1 && ELEM(mergevert, NULL, efa->v4)){ uvav[0] += tf->uv[3][0]; uvav[1] += tf->uv[3][1]; uvcount += 1; } } if(uvcount > 0) { uvav[0] /= uvcount; uvav[1] /= uvcount; for(efa = em->faces.first; efa; efa=efa->next){ tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); if(efa->v1->f1){ tf->uv[0][0] = uvav[0]; tf->uv[0][1] = uvav[1]; } if(efa->v2->f1){ tf->uv[1][0] = uvav[0]; tf->uv[1][1] = uvav[1]; } if(efa->v3->f1){ tf->uv[2][0] = uvav[0]; tf->uv[2][1] = uvav[1]; } if(efa->v4 && efa->v4->f1){ tf->uv[3][0] = uvav[0]; tf->uv[3][1] = uvav[1]; } } } } static int collapseEdges(EditMesh *em) { EditVert *eve; EditEdge *eed; ListBase allcollections; CollectedEdge *curredge; Collection *edgecollection; int totedges, groupcount, mergecount,vcount; float avgcount[3]; allcollections.first = 0; allcollections.last = 0; mergecount = 0; build_edgecollection(em, &allcollections); groupcount = BLI_countlist(&allcollections); for(edgecollection = allcollections.first; edgecollection; edgecollection = edgecollection->next){ totedges = BLI_countlist(&(edgecollection->collectionbase)); mergecount += totedges; avgcount[0] = 0; avgcount[1] = 0; avgcount[2] = 0; vcount = 0; for(curredge = edgecollection->collectionbase.first; curredge; curredge = curredge->next){ avgcount[0] += ((EditEdge*)curredge->eed)->v1->co[0]; avgcount[1] += ((EditEdge*)curredge->eed)->v1->co[1]; avgcount[2] += ((EditEdge*)curredge->eed)->v1->co[2]; avgcount[0] += ((EditEdge*)curredge->eed)->v2->co[0]; avgcount[1] += ((EditEdge*)curredge->eed)->v2->co[1]; avgcount[2] += ((EditEdge*)curredge->eed)->v2->co[2]; vcount +=2; } avgcount[0] /= vcount; avgcount[1] /=vcount; avgcount[2] /= vcount; for(curredge = edgecollection->collectionbase.first; curredge; curredge = curredge->next){ VECCOPY(((EditEdge*)curredge->eed)->v1->co,avgcount); VECCOPY(((EditEdge*)curredge->eed)->v2->co,avgcount); } if (EM_texFaceCheck(em)) { /*uv collapse*/ for(eve=em->verts.first; eve; eve=eve->next) eve->f1 = 0; for(eed=em->edges.first; eed; eed=eed->next) eed->f1 = 0; for(curredge = edgecollection->collectionbase.first; curredge; curredge = curredge->next){ curredge->eed->v1->f1 = 1; curredge->eed->v2->f1 = 1; curredge->eed->f1 = 1; } collapse_edgeuvs(em); } } freecollections(&allcollections); removedoublesflag(em, 1, 0, MERGELIMIT); return mergecount; } static int merge_firstlast(EditMesh *em, int first, int uvmerge) { EditVert *eve,*mergevert; EditSelection *ese; /* do sanity check in mergemenu in edit.c ?*/ if(first == 0){ ese = em->selected.last; mergevert= (EditVert*)ese->data; } else{ ese = em->selected.first; mergevert = (EditVert*)ese->data; } if(mergevert->f&SELECT){ for (eve=em->verts.first; eve; eve=eve->next){ if (eve->f&SELECT) VECCOPY(eve->co,mergevert->co); } } if(uvmerge && CustomData_has_layer(&em->fdata, CD_MTFACE)){ for(eve=em->verts.first; eve; eve=eve->next) eve->f1 = 0; for(eve=em->verts.first; eve; eve=eve->next){ if(eve->f&SELECT) eve->f1 = 1; } collapseuvs(em, mergevert); } return removedoublesflag(em, 1, 0, MERGELIMIT); } static void em_snap_to_center(EditMesh *em) { EditVert *eve; float cent[3] = {0.0f, 0.0f, 0.0f}; int i=0; for (eve=em->verts.first; eve; eve=eve->next) { if (eve->f & SELECT) { add_v3_v3(cent, eve->co); i++; } } if (!i) return; mul_v3_fl(cent, 1.0f / (float)i); for (eve=em->verts.first; eve; eve=eve->next) { if (eve->f & SELECT) { VECCOPY(eve->co, cent); } } } static void em_snap_to_cursor(EditMesh *em, bContext *C) { Scene *scene = CTX_data_scene(C); Object *ob= CTX_data_edit_object(C); View3D *v3d = CTX_wm_view3d(C); EditVert *eve; float co[3], *vco, invmat[4][4]; invert_m4_m4(invmat, ob->obmat); vco = give_cursor(scene, v3d); VECCOPY(co, vco); mul_m4_v3(invmat, co); for (eve=em->verts.first; eve; eve=eve->next) { if (eve->f & SELECT) { VECCOPY(eve->co, co); } } } static int merge_target(bContext *C, EditMesh *em, int target, int uvmerge) { EditVert *eve; // XXX not working if(target) em_snap_to_cursor(em, C); else em_snap_to_center(em); if(uvmerge && CustomData_has_layer(&em->fdata, CD_MTFACE)){ for(eve=em->verts.first; eve; eve=eve->next) eve->f1 = 0; for(eve=em->verts.first; eve; eve=eve->next){ if(eve->f&SELECT) eve->f1 = 1; } collapseuvs(em, NULL); } return removedoublesflag(em, 1, 0, MERGELIMIT); } #undef MERGELIMIT static int merge_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data); int count= 0, uvs= RNA_boolean_get(op->ptr, "uvs"); switch(RNA_enum_get(op->ptr, "type")) { case 3: count = merge_target(C, em, 0, uvs); break; case 4: count = merge_target(C, em, 1, uvs); break; case 1: count = merge_firstlast(em, 0, uvs); break; case 6: count = merge_firstlast(em, 1, uvs); break; case 5: count = collapseEdges(em); break; } if(!count) return OPERATOR_CANCELLED; recalc_editnormals(em); BKE_reportf(op->reports, RPT_INFO, "Removed %d vert%s.", count, (count==1)?"ex":"ices"); BKE_mesh_end_editmesh(obedit->data, em); DAG_id_flush_update(obedit->data, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); return OPERATOR_FINISHED; } static EnumPropertyItem merge_type_items[]= { {6, "FIRST", 0, "At First", ""}, {1, "LAST", 0, "At Last", ""}, {3, "CENTER", 0, "At Center", ""}, {4, "CURSOR", 0, "At Cursor", ""}, {5, "COLLAPSE", 0, "Collapse", ""}, {0, NULL, 0, NULL, NULL}}; static EnumPropertyItem *merge_type_itemf(bContext *C, PointerRNA *ptr, int *free) { Object *obedit= CTX_data_edit_object(C); EnumPropertyItem *item= NULL; int totitem= 0; if (C==NULL) { return merge_type_items; } if(obedit && obedit->type == OB_MESH) { EditMesh *em= BKE_mesh_get_editmesh(obedit->data); if(em->selectmode & SCE_SELECT_VERTEX) { if(em->selected.first && em->selected.last && ((EditSelection*)em->selected.first)->type == EDITVERT && ((EditSelection*)em->selected.last)->type == EDITVERT) { RNA_enum_items_add_value(&item, &totitem, merge_type_items, 6); RNA_enum_items_add_value(&item, &totitem, merge_type_items, 1); } else if(em->selected.first && ((EditSelection*)em->selected.first)->type == EDITVERT) RNA_enum_items_add_value(&item, &totitem, merge_type_items, 1); else if(em->selected.last && ((EditSelection*)em->selected.last)->type == EDITVERT) RNA_enum_items_add_value(&item, &totitem, merge_type_items, 6); } RNA_enum_items_add_value(&item, &totitem, merge_type_items, 3); RNA_enum_items_add_value(&item, &totitem, merge_type_items, 4); RNA_enum_items_add_value(&item, &totitem, merge_type_items, 5); } RNA_enum_item_end(&item, &totitem); *free= 1; return item; } void MESH_OT_merge(wmOperatorType *ot) { PropertyRNA *prop; /* identifiers */ ot->name= "Merge"; ot->description= "Merge selected vertices"; ot->idname= "MESH_OT_merge"; /* api callbacks */ ot->exec= merge_exec; ot->invoke= WM_menu_invoke; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; /* properties */ prop= RNA_def_enum(ot->srna, "type", merge_type_items, 3, "Type", "Merge method to use."); RNA_def_enum_funcs(prop, merge_type_itemf); ot->prop= prop; RNA_def_boolean(ot->srna, "uvs", 0, "UVs", "Move UVs according to merge."); } /************************ Vertex Path Operator *************************/ typedef struct PathNode { int u; int visited; ListBase edges; } PathNode; typedef struct PathEdge { struct PathEdge *next, *prev; int v; float w; } PathEdge; #define PATH_SELECT_EDGE_LENGTH 0 #define PATH_SELECT_TOPOLOGICAL 1 static int select_vertex_path_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data); EditVert *eve, *s, *t; EditEdge *eed; EditSelection *ese; PathEdge *newpe, *currpe; PathNode *currpn; PathNode *Q; int v, *previous, pathvert, pnindex; /*pnindex redundant?*/ int unbalanced, totnodes; float *cost; int type= RNA_enum_get(op->ptr, "type"); Heap *heap; /*binary heap for sorting pointers to PathNodes based upon a 'cost'*/ s = t = NULL; ese = ((EditSelection*)em->selected.last); if(ese && ese->type == EDITVERT && ese->prev && ese->prev->type == EDITVERT) { t = (EditVert*)ese->data; s = (EditVert*)ese->prev->data; /*need to find out if t is actually reachable by s....*/ for(eve=em->verts.first; eve; eve=eve->next){ eve->f1 = 0; } s->f1 = 1; unbalanced = 1; totnodes = 1; while(unbalanced){ unbalanced = 0; for(eed=em->edges.first; eed; eed=eed->next){ if(!eed->h){ if(eed->v1->f1 && !eed->v2->f1){ eed->v2->f1 = 1; totnodes++; unbalanced = 1; } else if(eed->v2->f1 && !eed->v1->f1){ eed->v1->f1 = 1; totnodes++; unbalanced = 1; } } } } if(s->f1 && t->f1){ /* t can be reached by s */ Q = MEM_callocN(sizeof(PathNode)*totnodes, "Path Select Nodes"); totnodes = 0; for(eve=em->verts.first; eve; eve=eve->next){ if(eve->f1){ Q[totnodes].u = totnodes; Q[totnodes].edges.first = 0; Q[totnodes].edges.last = 0; Q[totnodes].visited = 0; eve->tmp.p = &(Q[totnodes]); totnodes++; } else eve->tmp.p = NULL; } for(eed=em->edges.first; eed; eed=eed->next){ if(!eed->h){ if(eed->v1->f1){ currpn = ((PathNode*)eed->v1->tmp.p); newpe = MEM_mallocN(sizeof(PathEdge), "Path Edge"); newpe->v = ((PathNode*)eed->v2->tmp.p)->u; if (type == PATH_SELECT_EDGE_LENGTH) { newpe->w = len_v3v3(eed->v1->co, eed->v2->co); } else newpe->w = 1; newpe->next = 0; newpe->prev = 0; BLI_addtail(&(currpn->edges), newpe); } if(eed->v2->f1){ currpn = ((PathNode*)eed->v2->tmp.p); newpe = MEM_mallocN(sizeof(PathEdge), "Path Edge"); newpe->v = ((PathNode*)eed->v1->tmp.p)->u; if (type == PATH_SELECT_EDGE_LENGTH) { newpe->w = len_v3v3(eed->v1->co, eed->v2->co); } else newpe->w = 1; newpe->next = 0; newpe->prev = 0; BLI_addtail(&(currpn->edges), newpe); } } } heap = BLI_heap_new(); cost = MEM_callocN(sizeof(float)*totnodes, "Path Select Costs"); previous = MEM_callocN(sizeof(int)*totnodes, "PathNode indices"); for(v=0; v < totnodes; v++){ cost[v] = 1000000; previous[v] = -1; /*array of indices*/ } pnindex = ((PathNode*)s->tmp.p)->u; cost[pnindex] = 0; BLI_heap_insert(heap, 0.0f, SET_INT_IN_POINTER(pnindex)); while( !BLI_heap_empty(heap) ){ pnindex = GET_INT_FROM_POINTER(BLI_heap_popmin(heap)); currpn = &(Q[pnindex]); if(currpn == (PathNode*)t->tmp.p) /*target has been reached....*/ break; for(currpe=currpn->edges.first; currpe; currpe=currpe->next){ if(!Q[currpe->v].visited){ if( cost[currpe->v] > (cost[currpn->u ] + currpe->w) ){ cost[currpe->v] = cost[currpn->u] + currpe->w; previous[currpe->v] = currpn->u; Q[currpe->v].visited = 1; BLI_heap_insert(heap, cost[currpe->v], SET_INT_IN_POINTER(currpe->v)); } } } } pathvert = ((PathNode*)t->tmp.p)->u; while(pathvert != -1){ for(eve=em->verts.first; eve; eve=eve->next){ if(eve->f1){ if( ((PathNode*)eve->tmp.p)->u == pathvert) eve->f |= SELECT; } } pathvert = previous[pathvert]; } for(v=0; v < totnodes; v++) BLI_freelistN(&(Q[v].edges)); MEM_freeN(Q); MEM_freeN(cost); MEM_freeN(previous); BLI_heap_free(heap, NULL); EM_select_flush(em); } } else { BKE_mesh_end_editmesh(obedit->data, em); BKE_report(op->reports, RPT_ERROR, "Path Selection requires that exactly two vertices be selected"); return OPERATOR_CANCELLED; } WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); BKE_mesh_end_editmesh(obedit->data, em); return OPERATOR_FINISHED; } void MESH_OT_select_vertex_path(wmOperatorType *ot) { static const EnumPropertyItem type_items[] = { {PATH_SELECT_EDGE_LENGTH, "EDGE_LENGTH", 0, "Edge Length", NULL}, {PATH_SELECT_TOPOLOGICAL, "TOPOLOGICAL", 0, "Topological", NULL}, {0, NULL, 0, NULL, NULL}}; /* identifiers */ ot->name= "Select Vertex Path"; ot->description= "Select shortest path between two vertices by distance type"; ot->idname= "MESH_OT_select_vertex_path"; /* api callbacks */ ot->exec= select_vertex_path_exec; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; /* properties */ ot->prop= RNA_def_enum(ot->srna, "type", type_items, PATH_SELECT_EDGE_LENGTH, "Type", "Method to compute distance."); } /********************** Region/Loop Operators *************************/ static int region_to_loop(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data); EditEdge *eed; EditFace *efa; int selected= 0; for(eed=em->edges.first; eed; eed=eed->next) eed->f1 = 0; for(efa=em->faces.first; efa; efa=efa->next){ if(efa->f&SELECT){ efa->e1->f1++; efa->e2->f1++; efa->e3->f1++; if(efa->e4) efa->e4->f1++; selected= 1; } } if(!selected) return OPERATOR_CANCELLED; EM_clear_flag_all(em, SELECT); for(eed=em->edges.first; eed; eed=eed->next){ if(eed->f1 == 1) EM_select_edge(eed, 1); } em->selectmode = SCE_SELECT_EDGE; CTX_data_tool_settings(C)->selectmode= em->selectmode; EM_selectmode_set(em); BKE_mesh_end_editmesh(obedit->data, em); WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); return OPERATOR_FINISHED; } void MESH_OT_region_to_loop(wmOperatorType *ot) { /* identifiers */ ot->name= "Region to Loop"; ot->description= "Select a region as a loop of connected edges"; ot->idname= "MESH_OT_region_to_loop"; /* api callbacks */ ot->exec= region_to_loop; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; } static int validate_loop(EditMesh *em, Collection *edgecollection) { EditEdge *eed; EditFace *efa; CollectedEdge *curredge; /*1st test*/ for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){ curredge->eed->v1->f1 = 0; curredge->eed->v2->f1 = 0; } for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){ curredge->eed->v1->f1++; curredge->eed->v2->f1++; } for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){ if(curredge->eed->v1->f1 > 2) return(0); else if(curredge->eed->v2->f1 > 2) return(0); } /*2nd test*/ for(eed = em->edges.first; eed; eed=eed->next) eed->f1 = 0; for(efa=em->faces.first; efa; efa=efa->next){ efa->e1->f1++; efa->e2->f1++; efa->e3->f1++; if(efa->e4) efa->e4->f1++; } for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next){ if(curredge->eed->f1 > 2) return(0); } return(1); } static int loop_bisect(EditMesh *em, Collection *edgecollection){ EditFace *efa, *sf1, *sf2; EditEdge *eed, *sed; CollectedEdge *curredge; int totsf1, totsf2, unbalanced,balancededges; for(eed=em->edges.first; eed; eed=eed->next) eed->f1 = eed->f2 = 0; for(efa=em->faces.first; efa; efa=efa->next) efa->f1 = 0; for(curredge = (CollectedEdge*)edgecollection->collectionbase.first; curredge; curredge=curredge->next) curredge->eed->f1 = 1; sf1 = sf2 = NULL; sed = ((CollectedEdge*)edgecollection->collectionbase.first)->eed; for(efa=em->faces.first; efa; efa=efa->next){ if(sf2) break; else if(sf1){ if(efa->e1 == sed || efa->e2 == sed || efa->e3 == sed || ( (efa->e4) ? efa->e4 == sed : 0) ) sf2 = efa; } else{ if(efa->e1 == sed || efa->e2 == sed || efa->e3 == sed || ( (efa->e4) ? efa->e4 == sed : 0) ) sf1 = efa; } } if(sf1==NULL || sf2==NULL) return(-1); if(!(sf1->e1->f1)) sf1->e1->f2 = 1; if(!(sf1->e2->f1)) sf1->e2->f2 = 1; if(!(sf1->e3->f1)) sf1->e3->f2 = 1; if(sf1->e4 && !(sf1->e4->f1)) sf1->e4->f2 = 1; sf1->f1 = 1; totsf1 = 1; if(!(sf2->e1->f1)) sf2->e1->f2 = 2; if(!(sf2->e2->f1)) sf2->e2->f2 = 2; if(!(sf2->e3->f1)) sf2->e3->f2 = 2; if(sf2->e4 && !(sf2->e4->f1)) sf2->e4->f2 = 2; sf2->f1 = 2; totsf2 = 1; /*do sf1*/ unbalanced = 1; while(unbalanced){ unbalanced = 0; for(efa=em->faces.first; efa; efa=efa->next){ balancededges = 0; if(efa->f1 == 0){ if(efa->e1->f2 == 1 || efa->e2->f2 == 1 || efa->e3->f2 == 1 || ( (efa->e4) ? efa->e4->f2 == 1 : 0) ){ balancededges += efa->e1->f2 = (efa->e1->f1) ? 0 : 1; balancededges += efa->e2->f2 = (efa->e2->f1) ? 0 : 1; balancededges += efa->e3->f2 = (efa->e3->f1) ? 0 : 1; if(efa->e4) balancededges += efa->e4->f2 = (efa->e4->f1) ? 0 : 1; if(balancededges){ unbalanced = 1; efa->f1 = 1; totsf1++; } } } } } /*do sf2*/ unbalanced = 1; while(unbalanced){ unbalanced = 0; for(efa=em->faces.first; efa; efa=efa->next){ balancededges = 0; if(efa->f1 == 0){ if(efa->e1->f2 == 2 || efa->e2->f2 == 2 || efa->e3->f2 == 2 || ( (efa->e4) ? efa->e4->f2 == 2 : 0) ){ balancededges += efa->e1->f2 = (efa->e1->f1) ? 0 : 2; balancededges += efa->e2->f2 = (efa->e2->f1) ? 0 : 2; balancededges += efa->e3->f2 = (efa->e3->f1) ? 0 : 2; if(efa->e4) balancededges += efa->e4->f2 = (efa->e4->f1) ? 0 : 2; if(balancededges){ unbalanced = 1; efa->f1 = 2; totsf2++; } } } } } if(totsf1 < totsf2) return(1); else return(2); } static int loop_to_region(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data); EditFace *efa; ListBase allcollections={NULL,NULL}; Collection *edgecollection; int testflag; build_edgecollection(em, &allcollections); for(edgecollection = (Collection *)allcollections.first; edgecollection; edgecollection=edgecollection->next){ if(validate_loop(em, edgecollection)){ testflag = loop_bisect(em, edgecollection); for(efa=em->faces.first; efa; efa=efa->next){ if(efa->f1 == testflag){ if(efa->f&SELECT) EM_select_face(efa, 0); else EM_select_face(efa,1); } } } } for(efa=em->faces.first; efa; efa=efa->next){ /*fix this*/ if(efa->f&SELECT) EM_select_face(efa,1); } freecollections(&allcollections); BKE_mesh_end_editmesh(obedit->data, em); WM_event_add_notifier(C, NC_GEOM|ND_SELECT, obedit->data); return OPERATOR_FINISHED; } void MESH_OT_loop_to_region(wmOperatorType *ot) { /* identifiers */ ot->name= "Loop to Region"; ot->description= "Select a loop of connected edges as a region"; ot->idname= "MESH_OT_loop_to_region"; /* api callbacks */ ot->exec= loop_to_region; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; } /********************** UV/Color Operators *************************/ // XXX please check if these functions do what you want them to /* texface and vertex color editmode tools for the face menu */ static int mesh_rotate_uvs(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data); EditFace *efa; short change = 0; MTFace *tf; float u1, v1; int dir= RNA_enum_get(op->ptr, "direction"); if (!EM_texFaceCheck(em)) { BKE_report(op->reports, RPT_ERROR, "Mesh has no uv/image layers."); BKE_mesh_end_editmesh(obedit->data, em); return OPERATOR_CANCELLED; } for(efa=em->faces.first; efa; efa=efa->next) { if (efa->f & SELECT) { tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); u1= tf->uv[0][0]; v1= tf->uv[0][1]; if (dir == DIRECTION_CCW) { if(efa->v4) { tf->uv[0][0]= tf->uv[3][0]; tf->uv[0][1]= tf->uv[3][1]; tf->uv[3][0]= tf->uv[2][0]; tf->uv[3][1]= tf->uv[2][1]; } else { tf->uv[0][0]= tf->uv[2][0]; tf->uv[0][1]= tf->uv[2][1]; } tf->uv[2][0]= tf->uv[1][0]; tf->uv[2][1]= tf->uv[1][1]; tf->uv[1][0]= u1; tf->uv[1][1]= v1; } else { tf->uv[0][0]= tf->uv[1][0]; tf->uv[0][1]= tf->uv[1][1]; tf->uv[1][0]= tf->uv[2][0]; tf->uv[1][1]= tf->uv[2][1]; if(efa->v4) { tf->uv[2][0]= tf->uv[3][0]; tf->uv[2][1]= tf->uv[3][1]; tf->uv[3][0]= u1; tf->uv[3][1]= v1; } else { tf->uv[2][0]= u1; tf->uv[2][1]= v1; } } change = 1; } } BKE_mesh_end_editmesh(obedit->data, em); if(!change) return OPERATOR_CANCELLED; DAG_id_flush_update(obedit->data, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); return OPERATOR_FINISHED; } static int mesh_mirror_uvs(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data); EditFace *efa; short change = 0; MTFace *tf; float u1, v1; int axis= RNA_enum_get(op->ptr, "axis"); if (!EM_texFaceCheck(em)) { BKE_report(op->reports, RPT_ERROR, "Mesh has no uv/image layers."); BKE_mesh_end_editmesh(obedit->data, em); return OPERATOR_CANCELLED; } for(efa=em->faces.first; efa; efa=efa->next) { if (efa->f & SELECT) { tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); if (axis == AXIS_Y) { u1= tf->uv[1][0]; v1= tf->uv[1][1]; if(efa->v4) { tf->uv[1][0]= tf->uv[2][0]; tf->uv[1][1]= tf->uv[2][1]; tf->uv[2][0]= u1; tf->uv[2][1]= v1; u1= tf->uv[3][0]; v1= tf->uv[3][1]; tf->uv[3][0]= tf->uv[0][0]; tf->uv[3][1]= tf->uv[0][1]; tf->uv[0][0]= u1; tf->uv[0][1]= v1; } else { tf->uv[1][0]= tf->uv[2][0]; tf->uv[1][1]= tf->uv[2][1]; tf->uv[2][0]= u1; tf->uv[2][1]= v1; } } else { u1= tf->uv[0][0]; v1= tf->uv[0][1]; if(efa->v4) { tf->uv[0][0]= tf->uv[1][0]; tf->uv[0][1]= tf->uv[1][1]; tf->uv[1][0]= u1; tf->uv[1][1]= v1; u1= tf->uv[3][0]; v1= tf->uv[3][1]; tf->uv[3][0]= tf->uv[2][0]; tf->uv[3][1]= tf->uv[2][1]; tf->uv[2][0]= u1; tf->uv[2][1]= v1; } else { tf->uv[0][0]= tf->uv[1][0]; tf->uv[0][1]= tf->uv[1][1]; tf->uv[1][0]= u1; tf->uv[1][1]= v1; } } change = 1; } } BKE_mesh_end_editmesh(obedit->data, em); if(!change) return OPERATOR_CANCELLED; DAG_id_flush_update(obedit->data, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); return OPERATOR_FINISHED; } static int mesh_rotate_colors(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data); EditFace *efa; short change = 0; MCol tmpcol, *mcol; int dir= RNA_enum_get(op->ptr, "direction"); if (!EM_vertColorCheck(em)) { BKE_report(op->reports, RPT_ERROR, "Mesh has no color layers."); BKE_mesh_end_editmesh(obedit->data, em); return OPERATOR_CANCELLED; } for(efa=em->faces.first; efa; efa=efa->next) { if (efa->f & SELECT) { mcol = CustomData_em_get(&em->fdata, efa->data, CD_MCOL); tmpcol= mcol[0]; if (dir == DIRECTION_CCW) { if(efa->v4) { mcol[0]= mcol[3]; mcol[3]= mcol[2]; } else { mcol[0]= mcol[2]; } mcol[2]= mcol[1]; mcol[1]= tmpcol; } else { mcol[0]= mcol[1]; mcol[1]= mcol[2]; if(efa->v4) { mcol[2]= mcol[3]; mcol[3]= tmpcol; } else mcol[2]= tmpcol; } change = 1; } } BKE_mesh_end_editmesh(obedit->data, em); if(!change) return OPERATOR_CANCELLED; DAG_id_flush_update(obedit->data, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); return OPERATOR_FINISHED; } static int mesh_mirror_colors(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data); EditFace *efa; short change = 0; MCol tmpcol, *mcol; int axis= RNA_enum_get(op->ptr, "axis"); if (!EM_vertColorCheck(em)) { BKE_report(op->reports, RPT_ERROR, "Mesh has no color layers"); BKE_mesh_end_editmesh(obedit->data, em); return OPERATOR_CANCELLED; } for(efa=em->faces.first; efa; efa=efa->next) { if (efa->f & SELECT) { mcol = CustomData_em_get(&em->fdata, efa->data, CD_MCOL); if (axis == AXIS_Y) { tmpcol= mcol[1]; mcol[1]= mcol[2]; mcol[2]= tmpcol; if(efa->v4) { tmpcol= mcol[0]; mcol[0]= mcol[3]; mcol[3]= tmpcol; } } else { tmpcol= mcol[0]; mcol[0]= mcol[1]; mcol[1]= tmpcol; if(efa->v4) { tmpcol= mcol[2]; mcol[2]= mcol[3]; mcol[3]= tmpcol; } } change = 1; } } BKE_mesh_end_editmesh(obedit->data, em); if(!change) return OPERATOR_CANCELLED; DAG_id_flush_update(obedit->data, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); return OPERATOR_FINISHED; } void MESH_OT_uvs_rotate(wmOperatorType *ot) { /* identifiers */ ot->name= "Rotate UVs"; ot->description= "Rotate selected UVs"; ot->idname= "MESH_OT_uvs_rotate"; /* api callbacks */ ot->exec= mesh_rotate_uvs; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; /* props */ RNA_def_enum(ot->srna, "direction", direction_items, DIRECTION_CW, "Direction", "Direction to rotate UVs around."); } void MESH_OT_uvs_mirror(wmOperatorType *ot) { /* identifiers */ ot->name= "Mirror UVs"; ot->description= "Mirror selected UVs"; ot->idname= "MESH_OT_uvs_mirror"; /* api callbacks */ ot->exec= mesh_mirror_uvs; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; /* props */ RNA_def_enum(ot->srna, "axis", axis_items, DIRECTION_CW, "Axis", "Axis to mirror UVs around."); } void MESH_OT_colors_rotate(wmOperatorType *ot) { /* identifiers */ ot->name= "Rotate Colors"; ot->description= "Rotate UV/image color layer"; ot->idname= "MESH_OT_colors_rotate"; /* api callbacks */ ot->exec= mesh_rotate_colors; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; /* props */ RNA_def_enum(ot->srna, "direction", direction_items, DIRECTION_CW, "Direction", "Direction to rotate edge around."); } void MESH_OT_colors_mirror(wmOperatorType *ot) { /* identifiers */ ot->name= "Mirror Colors"; ot->description= "Mirror UV/image color layer"; ot->idname= "MESH_OT_colors_mirror"; /* api callbacks */ ot->exec= mesh_mirror_colors; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; /* props */ RNA_def_enum(ot->srna, "axis", axis_items, DIRECTION_CW, "Axis", "Axis to mirror colors around."); } /********************** Subdivide Operator *************************/ static int subdivide_exec(bContext *C, wmOperator *op) { ToolSettings *ts= CTX_data_tool_settings(C); Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data); int cuts= RNA_int_get(op->ptr,"number_cuts"); float smooth= 0.292f*RNA_float_get(op->ptr, "smoothness"); float fractal= RNA_float_get(op->ptr, "fractal")/100; int corner_cut_pattern= RNA_enum_get(op->ptr,"corner_cut_pattern"); int flag= 0; if(smooth != 0.0f) flag |= B_SMOOTH; if(fractal != 0.0f) flag |= B_FRACTAL; esubdivideflag(obedit, em, 1, smooth, fractal, ts->editbutflag|flag, cuts, corner_cut_pattern, 0); DAG_id_flush_update(obedit->data, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); return OPERATOR_FINISHED; } void MESH_OT_subdivide(wmOperatorType *ot) { /* identifiers */ ot->name= "Subdivide"; ot->description= "Subdivide selected edges"; ot->idname= "MESH_OT_subdivide"; /* api callbacks */ ot->exec= subdivide_exec; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; /* properties */ RNA_def_int(ot->srna, "number_cuts", 1, 1, INT_MAX, "Number of Cuts", "", 1, 10); RNA_def_float(ot->srna, "smoothness", 0.0f, 0.0f, FLT_MAX, "Smoothness", "Smoothness factor.", 0.0f, 1000.0f); RNA_def_float(ot->srna, "fractal", 0.0, 0.0f, FLT_MAX, "Fractal", "Fractal randomness factor.", 0.0f, 1000.0f); RNA_def_enum(ot->srna, "corner_cut_pattern", corner_type_items, SUBDIV_CORNER_INNERVERT, "Corner Cut Pattern", "Topology pattern to use to fill a face after cutting across its corner"); } /********************** Fill Operators *************************/ /* note; the EM_selectmode_set() calls here illustrate how badly constructed it all is... from before the edge/face flags, with very mixed results.... */ static void beautify_fill(EditMesh *em) { EditVert *v1, *v2, *v3, *v4; EditEdge *eed, *nexted; EditEdge dia1, dia2; EditFace *efa, *w; // void **efaar, **efaa; EVPTuple *efaar; EVPtr *efaa; float len1, len2, len3, len4, len5, len6, opp1, opp2, fac1, fac2; int totedge, ok, notbeauty=8, onedone, vindex[4]; /* - all selected edges with two faces * - find the faces: store them in edges (using datablock) * - per edge: - test convex * - test edge: flip? * - if true: remedge, addedge, all edges at the edge get new face pointers */ EM_selectmode_set(em); // makes sure in selectmode 'face' the edges of selected faces are selected too totedge = count_selected_edges(em->edges.first); if(totedge==0) return; /* temp block with face pointers */ efaar= (EVPTuple *) MEM_callocN(totedge * sizeof(EVPTuple), "beautyfill"); while (notbeauty) { notbeauty--; ok = collect_quadedges(efaar, em->edges.first, em->faces.first); /* there we go */ onedone= 0; eed= em->edges.first; while(eed) { nexted= eed->next; /* f2 is set in collect_quadedges() */ if(eed->f2==2 && eed->h==0) { efaa = (EVPtr *) eed->tmp.p; /* none of the faces should be treated before, nor be part of fgon */ ok= 1; efa= efaa[0]; if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0; if(efa->fgonf) ok= 0; efa= efaa[1]; if(efa->e1->f1 || efa->e2->f1 || efa->e3->f1) ok= 0; if(efa->fgonf) ok= 0; if(ok) { /* test convex */ givequadverts(efaa[0], efaa[1], &v1, &v2, &v3, &v4, vindex); if(v1 && v2 && v3 && v4) { if( convex(v1->co, v2->co, v3->co, v4->co) ) { /* test edges */ if( (v1) > (v3) ) { dia1.v1= v3; dia1.v2= v1; } else { dia1.v1= v1; dia1.v2= v3; } if( (v2) > (v4) ) { dia2.v1= v4; dia2.v2= v2; } else { dia2.v1= v2; dia2.v2= v4; } /* testing rule: * the area divided by the total edge lengths */ len1= len_v3v3(v1->co, v2->co); len2= len_v3v3(v2->co, v3->co); len3= len_v3v3(v3->co, v4->co); len4= len_v3v3(v4->co, v1->co); len5= len_v3v3(v1->co, v3->co); len6= len_v3v3(v2->co, v4->co); opp1= area_tri_v3(v1->co, v2->co, v3->co); opp2= area_tri_v3(v1->co, v3->co, v4->co); fac1= opp1/(len1+len2+len5) + opp2/(len3+len4+len5); opp1= area_tri_v3(v2->co, v3->co, v4->co); opp2= area_tri_v3(v2->co, v4->co, v1->co); fac2= opp1/(len2+len3+len6) + opp2/(len4+len1+len6); ok= 0; if(fac1 > fac2) { if(dia2.v1==eed->v1 && dia2.v2==eed->v2) { eed->f1= 1; efa= efaa[0]; efa->f1= 1; efa= efaa[1]; efa->f1= 1; w= EM_face_from_faces(em, efaa[0], efaa[1], vindex[0], vindex[1], 4+vindex[2], -1); w->f |= SELECT; w= EM_face_from_faces(em, efaa[0], efaa[1], vindex[0], 4+vindex[2], 4+vindex[3], -1); w->f |= SELECT; onedone= 1; } } else if(fac1 < fac2) { if(dia1.v1==eed->v1 && dia1.v2==eed->v2) { eed->f1= 1; efa= efaa[0]; efa->f1= 1; efa= efaa[1]; efa->f1= 1; w= EM_face_from_faces(em, efaa[0], efaa[1], vindex[1], 4+vindex[2], 4+vindex[3], -1); w->f |= SELECT; w= EM_face_from_faces(em, efaa[0], efaa[1], vindex[0], 4+vindex[1], 4+vindex[3], -1); w->f |= SELECT; onedone= 1; } } } } } } eed= nexted; } free_tagged_edges_faces(em, em->edges.first, em->faces.first); if(onedone==0) break; EM_selectmode_set(em); // new edges/faces were added } MEM_freeN(efaar); EM_select_flush(em); } /* Got this from scanfill.c. You will need to juggle around the * callbacks for the scanfill.c code a bit for this to work. */ static void fill_mesh(EditMesh *em) { EditVert *eve,*v1; EditEdge *eed,*e1,*nexted; EditFace *efa,*nextvl, *efan; short ok; if(em==NULL) return; waitcursor(1); /* copy all selected vertices */ eve= em->verts.first; while(eve) { if(eve->f & SELECT) { v1= BLI_addfillvert(eve->co); eve->tmp.v= v1; v1->tmp.v= eve; v1->xs= 0; // used for counting edges } eve= eve->next; } /* copy all selected edges */ eed= em->edges.first; while(eed) { if( (eed->v1->f & SELECT) && (eed->v2->f & SELECT) ) { e1= BLI_addfilledge(eed->v1->tmp.v, eed->v2->tmp.v); e1->v1->xs++; e1->v2->xs++; } eed= eed->next; } /* from all selected faces: remove vertices and edges to prevent doubles */ /* all edges add values, faces subtract, then remove edges with vertices ->xs<2 */ efa= em->faces.first; ok= 0; while(efa) { nextvl= efa->next; if( faceselectedAND(efa, 1) ) { efa->v1->tmp.v->xs--; efa->v2->tmp.v->xs--; efa->v3->tmp.v->xs--; if(efa->v4) efa->v4->tmp.v->xs--; ok= 1; } efa= nextvl; } if(ok) { /* there are faces selected */ eed= filledgebase.first; while(eed) { nexted= eed->next; if(eed->v1->xs<2 || eed->v2->xs<2) { BLI_remlink(&filledgebase,eed); } eed= nexted; } } if(BLI_edgefill(0, em->mat_nr)) { efa= fillfacebase.first; while(efa) { /* normals default pointing up */ efan= addfacelist(em, efa->v3->tmp.v, efa->v2->tmp.v, efa->v1->tmp.v, 0, NULL, NULL); if(efan) EM_select_face(efan, 1); efa= efa->next; } } BLI_end_edgefill(); beautify_fill(em); WM_cursor_wait(0); EM_select_flush(em); } static int fill_mesh_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data); fill_mesh(em); BKE_mesh_end_editmesh(obedit->data, em); DAG_id_flush_update(obedit->data, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); return OPERATOR_FINISHED; } void MESH_OT_fill(wmOperatorType *ot) { /* identifiers */ ot->name= "Fill"; ot->description= "Create a segment, edge or face"; ot->idname= "MESH_OT_fill"; /* api callbacks */ ot->exec= fill_mesh_exec; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; } static int beautify_fill_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data); beautify_fill(em); BKE_mesh_end_editmesh(obedit->data, em); DAG_id_flush_update(obedit->data, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); return OPERATOR_FINISHED; } void MESH_OT_beautify_fill(wmOperatorType *ot) { /* identifiers */ ot->name= "Beautify Fill"; ot->description= "Rearrange geometry on a selected surface to avoid skinny faces"; ot->idname= "MESH_OT_beautify_fill"; /* api callbacks */ ot->exec= beautify_fill_exec; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; } /* ********************** SORT FACES ******************* */ static void permutate(void *list, int num, int size, int *index) { void *buf; int len; int i; len = num * size; buf = MEM_mallocN(len, "permutate"); memcpy(buf, list, len); for (i = 0; i < num; i++) { memcpy((char *)list + (i * size), (char *)buf + (index[i] * size), size); } MEM_freeN(buf); } /* sort faces on view axis */ static float *face_sort_floats; static int float_sort(const void *v1, const void *v2) { float x1, x2; x1 = face_sort_floats[((int *) v1)[0]]; x2 = face_sort_floats[((int *) v2)[0]]; if( x1 > x2 ) return 1; else if( x1 < x2 ) return -1; return 0; } static int sort_faces_exec(bContext *C, wmOperator *op) { RegionView3D *rv3d= ED_view3d_context_rv3d(C); View3D *v3d= CTX_wm_view3d(C); Object *ob= CTX_data_edit_object(C); Scene *scene= CTX_data_scene(C); Mesh *me; CustomDataLayer *layer; int i, *index; int event; float reverse = 1; // XXX int ctrl= 0; if (!v3d) return OPERATOR_CANCELLED; /* This operator work in Object Mode, not in edit mode. * After talk with Cambell we agree that there is no point to port this to EditMesh right now. * so for now, we just exit_editmode and enter_editmode at the end of this function. */ ED_object_exit_editmode(C, EM_FREEDATA); me= ob->data; if(me->totface==0) { ED_object_enter_editmode(C, 0); return OPERATOR_FINISHED; } event= RNA_enum_get(op->ptr, "type"); // XXX //if(ctrl) // reverse = -1; /* create index list */ index= (int *)MEM_mallocN(sizeof(int) * me->totface, "sort faces"); for (i = 0; i < me->totface; i++) { index[i] = i; } face_sort_floats = (float *) MEM_mallocN(sizeof(float) * me->totface, "sort faces float"); /* sort index list instead of faces itself * and apply this permutation to all face layers */ if (event == 5) { /* Random */ for(i=0; itotface; i++) { face_sort_floats[i] = BLI_frand(); } qsort(index, me->totface, sizeof(int), float_sort); } else { MFace *mf; float vec[3]; float mat[4][4]; float cur[3]; if (event == 1) mul_m4_m4m4(mat, OBACT->obmat, rv3d->viewmat); /* apply the view matrix to the object matrix */ else if (event == 2) { /* sort from cursor */ if( v3d && v3d->localvd ) { VECCOPY(cur, v3d->cursor); } else { VECCOPY(cur, scene->cursor); } invert_m4_m4(mat, OBACT->obmat); mul_m4_v3(mat, cur); } mf= me->mface; for(i=0; itotface; i++, mf++) { if (event==3) { face_sort_floats[i] = ((float)mf->mat_nr)*reverse; } else if (event==4) { /*selected first*/ if (mf->flag & ME_FACE_SEL) face_sort_floats[i] = 0.0; else face_sort_floats[i] = reverse; } else { /* find the faces center */ add_v3_v3v3(vec, (me->mvert+mf->v1)->co, (me->mvert+mf->v2)->co); if (mf->v4) { add_v3_v3(vec, (me->mvert+mf->v3)->co); add_v3_v3(vec, (me->mvert+mf->v4)->co); mul_v3_fl(vec, 0.25f); } else { add_v3_v3(vec, (me->mvert+mf->v3)->co); mul_v3_fl(vec, 1.0f/3.0f); } /* done */ if (event == 1) { /* sort on view axis */ mul_m4_v3(mat, vec); face_sort_floats[i] = vec[2] * reverse; } else if(event == 2) { /* distance from cursor*/ face_sort_floats[i] = len_v3v3(cur, vec) * reverse; /* back to front */ } } } qsort(index, me->totface, sizeof(int), float_sort); } MEM_freeN(face_sort_floats); for(i = 0; i < me->fdata.totlayer; i++) { layer = &me->fdata.layers[i]; permutate(layer->data, me->totface, CustomData_sizeof(layer->type), index); } MEM_freeN(index); DAG_id_flush_update(ob->data, OB_RECALC_DATA); /* Return to editmode. */ ED_object_enter_editmode(C, 0); return OPERATOR_FINISHED; } void MESH_OT_sort_faces(wmOperatorType *ot) { static EnumPropertyItem type_items[]= { { 1, "VIEW_AXIS", 0, "View Axis", "" }, { 2, "CURSOR_DISTANCE", 0, "Cursor Distance", "" }, { 3, "MATERIAL", 0, "Material", "" }, { 4, "SELECTION", 0, "Selection", "" }, { 5, "RANDOMIZE", 0, "Randomize", "" }, { 0, NULL, 0, NULL, NULL }}; /* identifiers */ ot->name= "Sort Faces"; // XXX (Ctrl to reverse)%t| ot->description= "The faces of the active Mesh Object are sorted, based on the current view."; ot->idname= "MESH_OT_sort_faces"; /* api callbacks */ ot->invoke= WM_menu_invoke; ot->exec= sort_faces_exec; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; /* properties */ ot->prop= RNA_def_enum(ot->srna, "type", type_items, 0, "Type", ""); } /********************** Quad/Tri Operators *************************/ static int quads_convert_to_tris_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data); convert_to_triface(em,0); DAG_id_flush_update(obedit->data, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); BKE_mesh_end_editmesh(obedit->data, em); return OPERATOR_FINISHED; } void MESH_OT_quads_convert_to_tris(wmOperatorType *ot) { /* identifiers */ ot->name= "Quads to Tris"; ot->description= "Convert selected quads to triangles"; ot->idname= "MESH_OT_quads_convert_to_tris"; /* api callbacks */ ot->exec= quads_convert_to_tris_exec; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; } static int tris_convert_to_quads_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data); join_triangles(em); DAG_id_flush_update(obedit->data, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); BKE_mesh_end_editmesh(obedit->data, em); return OPERATOR_FINISHED; } void MESH_OT_tris_convert_to_quads(wmOperatorType *ot) { /* identifiers */ ot->name= "Tris to Quads"; ot->description= "Convert selected triangles to quads"; ot->idname= "MESH_OT_tris_convert_to_quads"; /* api callbacks */ ot->exec= tris_convert_to_quads_exec; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; } static int edge_flip_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data); edge_flip(em); DAG_id_flush_update(obedit->data, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); BKE_mesh_end_editmesh(obedit->data, em); return OPERATOR_FINISHED; } void MESH_OT_edge_flip(wmOperatorType *ot) { /* identifiers */ ot->name= "Edge Flip"; ot->description= "Flip selected edge or adjoining faces"; ot->idname= "MESH_OT_edge_flip"; /* api callbacks */ ot->exec= edge_flip_exec; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; } /********************** Smooth/Solid Operators *************************/ static void mesh_set_smooth_faces(EditMesh *em, short smooth) { EditFace *efa; if(em==NULL) return; for(efa= em->faces.first; efa; efa=efa->next) { if(efa->f & SELECT) { if(smooth) efa->flag |= ME_SMOOTH; else efa->flag &= ~ME_SMOOTH; } } } static int mesh_faces_shade_smooth_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data); mesh_set_smooth_faces(em, 1); BKE_mesh_end_editmesh(obedit->data, em); DAG_id_flush_update(obedit->data, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); return OPERATOR_FINISHED; } void MESH_OT_faces_shade_smooth(wmOperatorType *ot) { /* identifiers */ ot->name= "Shade Smooth"; ot->description= "Display faces 'smooth' (using vertex normals)"; ot->idname= "MESH_OT_faces_shade_smooth"; /* api callbacks */ ot->exec= mesh_faces_shade_smooth_exec; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; } static int mesh_faces_shade_flat_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data); mesh_set_smooth_faces(em, 0); DAG_id_flush_update(obedit->data, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); return OPERATOR_FINISHED; } void MESH_OT_faces_shade_flat(wmOperatorType *ot) { /* identifiers */ ot->name= "Shade Flat"; ot->description= "Display faces 'flat'"; ot->idname= "MESH_OT_faces_shade_flat"; /* api callbacks */ ot->exec= mesh_faces_shade_flat_exec; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; } /* TODO - some way to select on an arbitrary axis */ static int select_axis_exec(bContext *C, wmOperator *op) { Object *obedit= CTX_data_edit_object(C); EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data); int axis= RNA_enum_get(op->ptr, "axis"); int mode= RNA_enum_get(op->ptr, "mode"); /* -1==aligned, 0==neg, 1==pos*/ EditSelection *ese = em->selected.last; if(ese==NULL) return OPERATOR_CANCELLED; if(ese->type==EDITVERT) { EditVert *ev; EditVert *act_vert= (EditVert*)ese->data; float value= act_vert->co[axis]; float limit= CTX_data_tool_settings(C)->doublimit; // XXX if(mode==0) value -= limit; else if (mode==1) value += limit; for(ev=em->verts.first;ev;ev=ev->next) { if(!ev->h) { switch(mode) { case -1: /* aligned */ if(fabs(ev->co[axis] - value) < limit) ev->f |= SELECT; break; case 0: /* neg */ if(ev->co[axis] > value) ev->f |= SELECT; break; case 1: /* pos */ if(ev->co[axis] < value) ev->f |= SELECT; break; } } } } EM_select_flush(em); WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data); return OPERATOR_FINISHED; } void MESH_OT_select_axis(wmOperatorType *ot) { static EnumPropertyItem axis_mode_items[] = { {0, "POSITIVE", 0, "Positive Axis", ""}, {1, "NEGATIVE", 0, "Negative Axis", ""}, {-1, "ALIGNED", 0, "Aligned Axis", ""}, {0, NULL, 0, NULL, NULL}}; static EnumPropertyItem axis_items[] = { {0, "X_AXIS", 0, "X Axis", ""}, {1, "Y_AXIS", 0, "Y Axis", ""}, {2, "Z_AXIS", 0, "Z Axis", ""}, {0, NULL, 0, NULL, NULL}}; /* identifiers */ ot->name= "Select Axis"; ot->description= "Select all data in the mesh on a single axis"; ot->idname= "MESH_OT_select_axis"; /* api callbacks */ ot->exec= select_axis_exec; ot->poll= ED_operator_editmesh; /* flags */ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO; /* properties */ RNA_def_enum(ot->srna, "mode", axis_mode_items, 0, "Axis Mode", "Axis side to use when selecting"); RNA_def_enum(ot->srna, "axis", axis_items, 0, "Axis", "Select the axis to compare each vertex on"); }