/** * $Id$ * * ***** BEGIN GPL/BL DUAL 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. The Blender * Foundation also sells licenses for use in proprietary software under * the Blender License. See http://www.blender.org/BL/ for information * about this. * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. * All rights reserved. * * The Original Code is: all of this file. * * Contributor(s): none yet. * * ***** END GPL/BL DUAL LICENSE BLOCK ***** */ #include #include #include #ifdef HAVE_CONFIG_H #include #endif #ifdef WIN32 #include "BLI_winstuff.h" #endif #include "MEM_guardedalloc.h" #include "PIL_time.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_screen_types.h" #include "DNA_key_types.h" #include "DNA_scene_types.h" #include "DNA_view3d_types.h" #include "DNA_material_types.h" #include "DNA_texture_types.h" #include "DNA_userdef_types.h" #include "BLI_blenlib.h" #include "BLI_arithb.h" #include "BLI_editVert.h" #include "BLI_dynstr.h" #include "BKE_utildefines.h" #include "BKE_key.h" #include "BKE_object.h" #include "BKE_displist.h" #include "BKE_global.h" #include "BKE_library.h" #include "BKE_main.h" #include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_texture.h" #include "BIF_editkey.h" #include "BIF_editmesh.h" #include "BIF_editmode_undo.h" #include "BIF_interface.h" #include "BIF_mywindow.h" #include "BIF_space.h" #include "BIF_screen.h" #include "BIF_toolbox.h" #include "BSE_view.h" #include "BSE_edit.h" #include "BSE_trans_types.h" #include "BDR_drawobject.h" #include "BDR_editobject.h" #include "BDR_editface.h" #include "BDR_vpaint.h" #include "mydevice.h" #include "blendef.h" /* own include */ #include "editmesh.h" /* editmesh.c: - add/alloc/free data - hashtables - enter/exit editmode */ /* ***************** HASH ********************* */ #define EDHASHSIZE (512*512) #define EDHASH(a, b) (a % EDHASHSIZE) /* ************ ADD / REMOVE / FIND ****************** */ /* used to bypass normal calloc with fast one */ static void *(*callocvert)(size_t, size_t) = calloc; static void *(*callocedge)(size_t, size_t) = calloc; static void *(*callocface)(size_t, size_t) = calloc; EditVert *addvertlist(float *vec) { EditMesh *em = G.editMesh; EditVert *eve; static int hashnr= 0; eve= callocvert(sizeof(EditVert), 1); BLI_addtail(&em->verts, eve); if(vec) VECCOPY(eve->co, vec); eve->hash= hashnr++; if( hashnr>=EDHASHSIZE) hashnr= 0; /* new verts get keyindex of -1 since they did not * have a pre-editmode vertex order */ eve->keyindex = -1; return eve; } void free_editvert (EditVert *eve) { if(eve->dw) MEM_freeN(eve->dw); if(eve->fast==0) free(eve); } EditEdge *findedgelist(EditVert *v1, EditVert *v2) { EditVert *v3; struct HashEdge *he; /* swap ? */ if( (long)v1 > (long)v2) { v3= v2; v2= v1; v1= v3; } if(G.editMesh->hashedgetab==NULL) G.editMesh->hashedgetab= MEM_callocN(EDHASHSIZE*sizeof(struct HashEdge), "hashedgetab"); he= G.editMesh->hashedgetab + EDHASH(v1->hash, v2->hash); while(he) { if(he->eed && he->eed->v1==v1 && he->eed->v2==v2) return he->eed; he= he->next; } return 0; } static void insert_hashedge(EditEdge *eed) { /* assuming that eed is not in the list yet, and that a find has been done before */ struct HashEdge *first, *he; first= G.editMesh->hashedgetab + EDHASH(eed->v1->hash, eed->v2->hash); if( first->eed==0 ) { first->eed= eed; } else { he= &eed->hash; he->eed= eed; he->next= first->next; first->next= he; } } static void remove_hashedge(EditEdge *eed) { /* assuming eed is in the list */ struct HashEdge *first, *he, *prev=NULL; he=first= G.editMesh->hashedgetab + EDHASH(eed->v1->hash, eed->v2->hash); while(he) { if(he->eed == eed) { /* remove from list */ if(he==first) { if(first->next) { he= first->next; first->eed= he->eed; first->next= he->next; } else he->eed= 0; } else { prev->next= he->next; } return; } prev= he; he= he->next; } } EditEdge *addedgelist(EditVert *v1, EditVert *v2, EditEdge *example) { EditMesh *em = G.editMesh; EditVert *v3; EditEdge *eed; int swap= 0; if(v1==v2) return NULL; if(v1==NULL || v2==NULL) return NULL; /* swap ? */ if(v1>v2) { v3= v2; v2= v1; v1= v3; swap= 1; } /* find in hashlist */ eed= findedgelist(v1, v2); if(eed==NULL) { eed= (EditEdge *)callocedge(sizeof(EditEdge), 1); eed->v1= v1; eed->v2= v2; BLI_addtail(&em->edges, eed); eed->dir= swap; insert_hashedge(eed); /* copy edge data: rule is to do this with addedgelist call, before addfacelist */ if(example) { eed->crease= example->crease; eed->seam = example->seam; eed->h |= (example->h & EM_FGON); } } return eed; } void remedge(EditEdge *eed) { EditMesh *em = G.editMesh; BLI_remlink(&em->edges, eed); remove_hashedge(eed); } void free_editedge(EditEdge *eed) { if(eed->fast==0) free(eed); } void free_editface(EditFace *efa) { if(efa->fast==0) free(efa); } void free_vertlist(ListBase *edve) { EditVert *eve, *next; if (!edve) return; eve= edve->first; while(eve) { next= eve->next; free_editvert(eve); eve= next; } edve->first= edve->last= NULL; } void free_edgelist(ListBase *lb) { EditEdge *eed, *next; eed= lb->first; while(eed) { next= eed->next; free_editedge(eed); eed= next; } lb->first= lb->last= NULL; } void free_facelist(ListBase *lb) { EditFace *efa, *next; efa= lb->first; while(efa) { next= efa->next; free_editface(efa); efa= next; } lb->first= lb->last= NULL; } EditFace *addfacelist(EditVert *v1, EditVert *v2, EditVert *v3, EditVert *v4, EditFace *example, EditFace *exampleEdges) { EditMesh *em = G.editMesh; EditFace *efa; EditEdge *e1, *e2=0, *e3=0, *e4=0; /* add face to list and do the edges */ if(exampleEdges) { e1= addedgelist(v1, v2, exampleEdges->e1); e2= addedgelist(v2, v3, exampleEdges->e2); if(v4) e3= addedgelist(v3, v4, exampleEdges->e3); else e3= addedgelist(v3, v1, exampleEdges->e3); if(v4) e4= addedgelist(v4, v1, exampleEdges->e4); } else { e1= addedgelist(v1, v2, NULL); e2= addedgelist(v2, v3, NULL); if(v4) e3= addedgelist(v3, v4, NULL); else e3= addedgelist(v3, v1, NULL); if(v4) e4= addedgelist(v4, v1, NULL); } if(v1==v2 || v2==v3 || v1==v3) return NULL; if(e2==0) return NULL; efa= (EditFace *)callocface(sizeof(EditFace), 1); efa->v1= v1; efa->v2= v2; efa->v3= v3; efa->v4= v4; efa->e1= e1; efa->e2= e2; efa->e3= e3; efa->e4= e4; if(example) { efa->mat_nr= example->mat_nr; efa->tf= example->tf; efa->flag= example->flag; } else { if (G.obedit && G.obedit->actcol) efa->mat_nr= G.obedit->actcol-1; default_uv(efa->tf.uv, 1.0); /* Initialize colors */ efa->tf.col[0]= efa->tf.col[1]= efa->tf.col[2]= efa->tf.col[3]= vpaint_get_current_col(); } BLI_addtail(&em->faces, efa); if(efa->v4) { CalcNormFloat4(efa->v1->co, efa->v2->co, efa->v3->co, efa->v4->co, efa->n); CalcCent4f(efa->cent, efa->v1->co, efa->v2->co, efa->v3->co, efa->v4->co); } else { CalcNormFloat(efa->v1->co, efa->v2->co, efa->v3->co, efa->n); CalcCent3f(efa->cent, efa->v1->co, efa->v2->co, efa->v3->co); } return efa; } /* ************************ end add/new/find ************ */ /* ************************ stuct EditMesh manipulation ***************************** */ /* fake callocs for fastmalloc below */ static void *calloc_fastvert(size_t size, size_t nr) { EditVert *eve= G.editMesh->curvert++; eve->fast= 1; return eve; } static void *calloc_fastedge(size_t size, size_t nr) { EditEdge *eed= G.editMesh->curedge++; eed->fast= 1; return eed; } static void *calloc_fastface(size_t size, size_t nr) { EditFace *efa= G.editMesh->curface++; efa->fast= 1; return efa; } /* allocate 1 chunk for all vertices, edges, faces. These get tagged to prevent it from being freed */ static void init_editmesh_fastmalloc(EditMesh *em, int totvert, int totedge, int totface) { if(totvert) em->allverts= MEM_callocN(totvert*sizeof(EditVert), "allverts"); else em->allverts= NULL; em->curvert= em->allverts; if(totedge==0) totedge= 4*totface; // max possible if(totedge) em->alledges= MEM_callocN(totedge*sizeof(EditEdge), "alledges"); else em->alledges= NULL; em->curedge= em->alledges; if(totface) em->allfaces= MEM_callocN(totface*sizeof(EditFace), "allfaces"); else em->allfaces= NULL; em->curface= em->allfaces; callocvert= calloc_fastvert; callocedge= calloc_fastedge; callocface= calloc_fastface; } static void end_editmesh_fastmalloc(void) { callocvert= calloc; callocedge= calloc; callocface= calloc; } void free_editMesh(EditMesh *em) { if(em==NULL) return; if(em->verts.first) free_vertlist(&em->verts); if(em->edges.first) free_edgelist(&em->edges); if(em->faces.first) free_facelist(&em->faces); /* DEBUG: hashtabs are slowest part of enter/exit editmode. here a testprint */ #if 0 if(em->hashedgetab) { HashEdge *he, *hen; int a, used=0, max=0, nr; he= em->hashedgetab; for(a=0; aeed) used++; hen= he->next; nr= 0; while(hen) { nr++; hen= hen->next; } if(maxhashedgetab) MEM_freeN(em->hashedgetab); em->hashedgetab= NULL; if(em->allverts) MEM_freeN(em->allverts); if(em->alledges) MEM_freeN(em->alledges); if(em->allfaces) MEM_freeN(em->allfaces); em->allverts= em->curvert= NULL; em->alledges= em->curedge= NULL; em->allfaces= em->curface= NULL; G.totvert= G.totface= 0; } /* on G.editMesh */ static void editMesh_set_hash(void) { EditEdge *eed; G.editMesh->hashedgetab= NULL; for(eed=G.editMesh->edges.first; eed; eed= eed->next) { if( findedgelist(eed->v1, eed->v2)==NULL ) insert_hashedge(eed); } } /* ************************ IN & OUT EDITMODE ***************************** */ static void edge_normal_compare(EditEdge *eed, EditFace *efa1) { EditFace *efa2; float cent1[3], cent2[3]; float inp; efa2= (EditFace *)eed->vn; if(efa1==efa2) return; inp= efa1->n[0]*efa2->n[0] + efa1->n[1]*efa2->n[1] + efa1->n[2]*efa2->n[2]; if(inp<0.999 && inp >-0.999) eed->f2= 1; if(efa1->v4) CalcCent4f(cent1, efa1->v1->co, efa1->v2->co, efa1->v3->co, efa1->v4->co); else CalcCent3f(cent1, efa1->v1->co, efa1->v2->co, efa1->v3->co); if(efa2->v4) CalcCent4f(cent2, efa2->v1->co, efa2->v2->co, efa2->v3->co, efa2->v4->co); else CalcCent3f(cent2, efa2->v1->co, efa2->v2->co, efa2->v3->co); VecSubf(cent1, cent2, cent1); Normalise(cent1); inp= cent1[0]*efa1->n[0] + cent1[1]*efa1->n[1] + cent1[2]*efa1->n[2]; if(inp < -0.001 ) eed->f1= 1; } static void edge_drawflags(void) { EditMesh *em = G.editMesh; EditVert *eve; EditEdge *eed, *e1, *e2, *e3, *e4; EditFace *efa; /* - count number of times edges are used in faces: 0 en 1 time means draw edge * - edges more than 1 time used: in *vn is pointer to first face * - check all faces, when normal differs to much: draw (flag becomes 1) */ /* later on: added flags for 'cylinder' and 'sphere' intersection tests in old game engine (2.04) */ recalc_editnormals(); /* init */ eve= em->verts.first; while(eve) { eve->f1= 1; /* during test it's set at zero */ eve= eve->next; } eed= em->edges.first; while(eed) { eed->f2= eed->f1= 0; eed->vn= 0; eed= eed->next; } efa= em->faces.first; while(efa) { e1= efa->e1; e2= efa->e2; e3= efa->e3; e4= efa->e4; if(e1->f2<3) e1->f2+= 1; if(e2->f2<3) e2->f2+= 1; if(e3->f2<3) e3->f2+= 1; if(e4 && e4->f2<3) e4->f2+= 1; if(e1->vn==0) e1->vn= (EditVert *)efa; if(e2->vn==0) e2->vn= (EditVert *)efa; if(e3->vn==0) e3->vn= (EditVert *)efa; if(e4 && e4->vn==0) e4->vn= (EditVert *)efa; efa= efa->next; } if(G.f & G_ALLEDGES) { efa= em->faces.first; while(efa) { if(efa->e1->f2>=2) efa->e1->f2= 1; if(efa->e2->f2>=2) efa->e2->f2= 1; if(efa->e3->f2>=2) efa->e3->f2= 1; if(efa->e4 && efa->e4->f2>=2) efa->e4->f2= 1; efa= efa->next; } } else { /* handle single-edges for 'test cylinder flag' (old engine) */ eed= em->edges.first; while(eed) { if(eed->f2==1) eed->f1= 1; eed= eed->next; } /* all faces, all edges with flag==2: compare normal */ efa= em->faces.first; while(efa) { if(efa->e1->f2==2) edge_normal_compare(efa->e1, efa); if(efa->e2->f2==2) edge_normal_compare(efa->e2, efa); if(efa->e3->f2==2) edge_normal_compare(efa->e3, efa); if(efa->e4 && efa->e4->f2==2) edge_normal_compare(efa->e4, efa); efa= efa->next; } /* sphere collision flag */ eed= em->edges.first; while(eed) { if(eed->f1!=1) { eed->v1->f1= eed->v2->f1= 0; } eed= eed->next; } } } /* turns Mesh into editmesh */ void make_editMesh() { EditMesh *em = G.editMesh; Mesh *me= G.obedit->data; MFace *mface; TFace *tface; MVert *mvert; KeyBlock *actkey=0; EditVert *eve, **evlist, *eve1, *eve2, *eve3, *eve4; EditFace *efa; EditEdge *eed; int tot, a; if(G.obedit==NULL) return; /* because of reload */ free_editMesh(G.editMesh); G.totvert= tot= me->totvert; if(tot==0) { countall(); return; } waitcursor(1); /* initialize fastmalloc for editmesh */ init_editmesh_fastmalloc(G.editMesh, me->totvert, me->totedge, me->totface); /* keys? */ if(me->key) { actkey= me->key->block.first; while(actkey) { if(actkey->flag & SELECT) break; actkey= actkey->next; } } if(actkey) { key_to_mesh(actkey, me); tot= actkey->totelem; } /* make editverts */ mvert= me->mvert; evlist= (EditVert **)MEM_mallocN(tot*sizeof(void *),"evlist"); for(a=0; aco); evlist[a]= eve; // face select sets selection in next loop if( (G.f & G_FACESELECT)==0 ) eve->f |= (mvert->flag & 1); if (mvert->flag & ME_HIDE) eve->h= 1; eve->no[0]= mvert->no[0]/32767.0; eve->no[1]= mvert->no[1]/32767.0; eve->no[2]= mvert->no[2]/32767.0; /* lets overwrite the keyindex of the editvert * with the order it used to be in before * editmode */ eve->keyindex = a; if (me->dvert){ eve->totweight = me->dvert[a].totweight; if (me->dvert[a].dw){ eve->dw = MEM_callocN (sizeof(MDeformWeight) * me->dvert[a].totweight, "deformWeight"); memcpy (eve->dw, me->dvert[a].dw, sizeof(MDeformWeight) * me->dvert[a].totweight); } } } if(actkey && actkey->totelem!=me->totvert); else { unsigned int *mcol; /* make edges */ if(me->medge) { MEdge *medge= me->medge; for(a=0; atotedge; a++, medge++) { eed= addedgelist(evlist[medge->v1], evlist[medge->v2], NULL); eed->crease= ((float)medge->crease)/255.0; if(medge->flag & ME_SEAM) eed->seam= 1; if(medge->flag & SELECT) eed->f |= SELECT; if(medge->flag & ME_FGON) eed->h= EM_FGON; // 2 different defines! if(medge->flag & ME_HIDE) eed->h |= 1; } } /* make faces */ mface= me->mface; tface= me->tface; mcol= (unsigned int *)me->mcol; for(a=0; atotface; a++, mface++) { eve1= evlist[mface->v1]; eve2= evlist[mface->v2]; if(mface->v3) eve3= evlist[mface->v3]; else eve3= NULL; if(mface->v4) eve4= evlist[mface->v4]; else eve4= NULL; efa= addfacelist(eve1, eve2, eve3, eve4, NULL, NULL); if(efa) { if(mcol) memcpy(efa->tf.col, mcol, 4*sizeof(int)); if(me->tface) { efa->tf= *tface; if( tface->flag & TF_SELECT) { if(G.f & G_FACESELECT) { EM_select_face(efa, 1); } } } efa->mat_nr= mface->mat_nr; efa->flag= mface->flag & ~ME_HIDE; if((G.f & G_FACESELECT)==0) { /* select face flag, if no edges we flush down */ if(mface->flag & ME_FACE_SEL) { efa->f |= SELECT; if(me->medge==NULL) EM_select_face(efa, 1); } } if(mface->flag & ME_HIDE) efa->h= 1; } if(me->tface) tface++; if(mcol) mcol+=4; } } /* flush hide flags when no medge */ if(me->medge==NULL) { for(eed= em->edges.first; eed; eed= eed->next) { if(eed->v1->h || eed->v2->h) eed->h |= 1; else eed->h &= ~1; } } MEM_freeN(evlist); end_editmesh_fastmalloc(); // resets global function pointers /* this creates coherent selections. also needed for older files */ EM_selectmode_set(); /* paranoia check to enforce hide rules */ EM_hide_reset(); /* sets helper flags which arent saved */ EM_fgon_flags(); countall(); if (mesh_uses_displist(me)) makeDispList(G.obedit); waitcursor(0); } /** Rotates MFace and UVFace vertices in case the last * vertex index is = 0. * This function is a hack and may only be called in the * conversion from EditMesh to Mesh data. * This function is similar to test_index_mface in * blenkernel/intern/mesh.c. * To not clutter the blenkernel code with more bad level * calls/structures, this function resides here. */ static void fix_faceindices(MFace *mface, EditFace *efa, int nr) { int a; float tmpuv[2]; unsigned int tmpcol; /* first test if the face is legal */ if(mface->v3 && mface->v3==mface->v4) { mface->v4= 0; nr--; } if(mface->v2 && mface->v2==mface->v3) { mface->v3= mface->v4; mface->v4= 0; nr--; } if(mface->v1==mface->v2) { mface->v2= mface->v3; mface->v3= mface->v4; mface->v4= 0; nr--; } /* prevent a zero index value at the wrong location */ if(nr==2) { if(mface->v2==0) SWAP(int, mface->v1, mface->v2); } else if(nr==3) { if(mface->v3==0) { SWAP(int, mface->v1, mface->v2); SWAP(int, mface->v2, mface->v3); /* rotate face UV coordinates, too */ UVCOPY(tmpuv, efa->tf.uv[0]); UVCOPY(efa->tf.uv[0], efa->tf.uv[1]); UVCOPY(efa->tf.uv[1], efa->tf.uv[2]); UVCOPY(efa->tf.uv[2], tmpuv); /* same with vertex colours */ tmpcol = efa->tf.col[0]; efa->tf.col[0] = efa->tf.col[1]; efa->tf.col[1] = efa->tf.col[2]; efa->tf.col[2] = tmpcol; a= mface->edcode; mface->edcode= 0; if(a & ME_V1V2) mface->edcode |= ME_V3V1; if(a & ME_V2V3) mface->edcode |= ME_V1V2; if(a & ME_V3V1) mface->edcode |= ME_V2V3; a= mface->puno; mface->puno &= ~15; if(a & ME_FLIPV1) mface->puno |= ME_FLIPV2; if(a & ME_FLIPV2) mface->puno |= ME_FLIPV3; if(a & ME_FLIPV3) mface->puno |= ME_FLIPV1; } } else if(nr==4) { if(mface->v3==0 || mface->v4==0) { SWAP(int, mface->v1, mface->v3); SWAP(int, mface->v2, mface->v4); /* swap UV coordinates */ UVCOPY(tmpuv, efa->tf.uv[0]); UVCOPY(efa->tf.uv[0], efa->tf.uv[2]); UVCOPY(efa->tf.uv[2], tmpuv); UVCOPY(tmpuv, efa->tf.uv[1]); UVCOPY(efa->tf.uv[1], efa->tf.uv[3]); UVCOPY(efa->tf.uv[3], tmpuv); /* swap vertex colours */ tmpcol = efa->tf.col[0]; efa->tf.col[0] = efa->tf.col[2]; efa->tf.col[2] = tmpcol; tmpcol = efa->tf.col[1]; efa->tf.col[1] = efa->tf.col[3]; efa->tf.col[3] = tmpcol; a= mface->edcode; mface->edcode= 0; if(a & ME_V1V2) mface->edcode |= ME_V3V4; if(a & ME_V2V3) mface->edcode |= ME_V2V3; if(a & ME_V3V4) mface->edcode |= ME_V1V2; if(a & ME_V4V1) mface->edcode |= ME_V4V1; a= mface->puno; mface->puno &= ~15; if(a & ME_FLIPV1) mface->puno |= ME_FLIPV3; if(a & ME_FLIPV2) mface->puno |= ME_FLIPV4; if(a & ME_FLIPV3) mface->puno |= ME_FLIPV1; if(a & ME_FLIPV4) mface->puno |= ME_FLIPV2; } } } /* makes Mesh out of editmesh */ void load_editMesh(void) { EditMesh *em = G.editMesh; Mesh *me= G.obedit->data; MVert *mvert, *oldverts; MEdge *medge=NULL; MFace *mface; MSticky *ms; KeyBlock *actkey=NULL, *currkey; EditVert *eve; EditFace *efa; EditEdge *eed; float *fp, *newkey, *oldkey, nor[3]; int i, a, ototvert, totedge=0; MDeformVert *dvert; int usedDvert = 0; waitcursor(1); countall(); /* this one also tests of edges are not in faces: */ /* eed->f2==0: not in face, f2==1: draw it */ /* eed->f1 : flag for dynaface (cylindertest, old engine) */ /* eve->f1 : flag for dynaface (sphere test, old engine) */ /* eve->f2 : being used in vertexnormals */ edge_drawflags(); /* this sets efa->puno, punoflag (for vertex normal & projection) */ vertexnormals( (me->flag & ME_NOPUNOFLIP)==0 ); eed= em->edges.first; while(eed) { totedge++; if(me->medge==NULL && (eed->f2==0)) G.totface++; eed= eed->next; } /* new Vertex block */ if(G.totvert==0) mvert= NULL; else mvert= MEM_callocN(G.totvert*sizeof(MVert), "loadeditMesh vert"); /* new Edge block */ if(totedge) { if(me->medge==NULL) totedge= 0; // if edges get added is defined by orig mesh else medge= MEM_callocN(totedge*sizeof(MEdge), "loadeditMesh edge"); } /* new Face block */ if(G.totface==0) mface= NULL; else mface= MEM_callocN(G.totface*sizeof(MFace), "loadeditMesh face"); if (G.totvert==0) dvert= NULL; else dvert = MEM_callocN(G.totvert*sizeof(MDeformVert), "loadeditMesh3"); if (me->dvert) free_dverts(me->dvert, me->totvert); me->dvert=dvert; /* lets save the old verts just in case we are actually working on * a key ... we now do processing of the keys at the end */ oldverts = me->mvert; ototvert= me->totvert; /* put new data in Mesh */ me->mvert= mvert; me->totvert= G.totvert; if(me->medge) MEM_freeN(me->medge); me->medge= medge; if(medge) me->totedge= totedge; else me->totedge= 0; if(me->mface) MEM_freeN(me->mface); me->mface= mface; me->totface= G.totface; /* the vertices, abuse ->vn as counter */ eve= em->verts.first; a= 0; while(eve) { VECCOPY(mvert->co, eve->co); mvert->mat_nr= 255; /* what was this for, halos? */ /* vertex normal */ VECCOPY(nor, eve->no); VecMulf(nor, 32767.0); VECCOPY(mvert->no, nor); /* NEW VERSION */ if (dvert){ dvert->totweight=eve->totweight; if (eve->dw){ dvert->dw = MEM_callocN (sizeof(MDeformWeight)*eve->totweight, "deformWeight"); memcpy (dvert->dw, eve->dw, sizeof(MDeformWeight)*eve->totweight); usedDvert++; } } eve->vn= (EditVert *)(long)(a++); /* counter */ mvert->flag= 0; if(eve->f1==1) mvert->flag |= ME_SPHERETEST; mvert->flag |= (eve->f & SELECT); if (eve->h) mvert->flag |= ME_HIDE; eve= eve->next; mvert++; dvert++; } /* If we didn't actually need the dverts, get rid of them */ if (!usedDvert){ free_dverts(me->dvert, G.totvert); me->dvert=NULL; } /* the edges */ if(medge) { eed= em->edges.first; while(eed) { medge->v1= (unsigned int) eed->v1->vn; medge->v2= (unsigned int) eed->v2->vn; medge->flag= eed->f & SELECT; if(eed->f2<2) medge->flag |= ME_EDGEDRAW; if(eed->seam) medge->flag |= ME_SEAM; if(eed->h & EM_FGON) medge->flag |= ME_FGON; // different defines yes if(eed->h & 1) medge->flag |= ME_HIDE; medge->crease= (char)(255.0*eed->crease); medge++; eed= eed->next; } } /* the faces */ efa= em->faces.first; i = 0; while(efa) { mface= &((MFace *) me->mface)[i]; mface->v1= (unsigned int) efa->v1->vn; mface->v2= (unsigned int) efa->v2->vn; mface->v3= (unsigned int) efa->v3->vn; if(efa->v4) mface->v4= (unsigned int) efa->v4->vn; mface->mat_nr= efa->mat_nr; mface->puno= efa->puno; mface->flag= efa->flag; /* bit 0 of flag is already taken for smooth... */ if(efa->f & 1) mface->flag |= ME_FACE_SEL; else mface->flag &= ~ME_FACE_SEL; if(efa->h) mface->flag |= ME_HIDE; /* mat_nr in vertex */ if(me->totcol>1) { mvert= me->mvert+mface->v1; if(mvert->mat_nr == (char)255) mvert->mat_nr= mface->mat_nr; mvert= me->mvert+mface->v2; if(mvert->mat_nr == (char)255) mvert->mat_nr= mface->mat_nr; mvert= me->mvert+mface->v3; if(mvert->mat_nr == (char)255) mvert->mat_nr= mface->mat_nr; if(mface->v4) { mvert= me->mvert+mface->v4; if(mvert->mat_nr == (char)255) mvert->mat_nr= mface->mat_nr; } } /* watch: efa->e1->f2==0 means loose edge */ if(efa->e1->f2==1) { mface->edcode |= ME_V1V2; efa->e1->f2= 2; } if(efa->e2->f2==1) { mface->edcode |= ME_V2V3; efa->e2->f2= 2; } if(efa->e3->f2==1) { if(efa->v4) { mface->edcode |= ME_V3V4; } else { mface->edcode |= ME_V3V1; } efa->e3->f2= 2; } if(efa->e4 && efa->e4->f2==1) { mface->edcode |= ME_V4V1; efa->e4->f2= 2; } /* no index '0' at location 3 or 4 */ if(efa->v4) fix_faceindices(mface, efa, 4); else fix_faceindices(mface, efa, 3); i++; efa= efa->next; } /* add loose edges as a face */ if(medge==NULL) { eed= em->edges.first; while(eed) { if( eed->f2==0 ) { mface= &((MFace *) me->mface)[i]; mface->v1= (unsigned int) eed->v1->vn; mface->v2= (unsigned int) eed->v2->vn; test_index_mface(mface, 2); mface->edcode= ME_V1V2; i++; } eed= eed->next; } } tex_space_mesh(me); /* tface block */ if( me->tface && me->totface ) { TFace *tfn, *tf; tf=tfn= MEM_callocN(sizeof(TFace)*me->totface, "tface"); efa= em->faces.first; while(efa) { *tf= efa->tf; if(G.f & G_FACESELECT) { if( efa->f & SELECT) tf->flag |= TF_SELECT; else tf->flag &= ~TF_SELECT; } tf++; efa= efa->next; } if(me->tface) MEM_freeN(me->tface); me->tface= tfn; } else if(me->tface) { MEM_freeN(me->tface); me->tface= NULL; } /* mcol: same as tface... */ if(me->mcol && me->totface) { unsigned int *mcn, *mc; mc=mcn= MEM_mallocN(4*sizeof(int)*me->totface, "mcol"); efa= em->faces.first; while(efa) { memcpy(mc, efa->tf.col, 4*sizeof(int)); mc+=4; efa= efa->next; } if(me->mcol) MEM_freeN(me->mcol); me->mcol= (MCol *)mcn; } else if(me->mcol) { MEM_freeN(me->mcol); me->mcol= 0; } /* are there keys? */ if(me->key) { /* find the active key */ actkey= me->key->block.first; while(actkey) { if(actkey->flag & SELECT) break; actkey= actkey->next; } /* Lets reorder the key data so that things line up roughly * with the way things were before editmode */ currkey = me->key->block.first; while(currkey) { fp= newkey= MEM_callocN(me->key->elemsize*G.totvert, "currkey->data"); oldkey = currkey->data; eve= em->verts.first; i = 0; mvert = me->mvert; while(eve) { if (eve->keyindex >= 0 && eve->keyindex < currkey->totelem) { // valid old vertex if(currkey == actkey) { if (actkey == me->key->refkey) { VECCOPY(fp, mvert->co); } else { VECCOPY(fp, mvert->co); if(oldverts) { VECCOPY(mvert->co, oldverts[eve->keyindex].co); } } } else { if(oldkey) { VECCOPY(fp, oldkey + 3 * eve->keyindex); } } } else { VECCOPY(fp, mvert->co); } fp+= 3; ++i; ++mvert; eve= eve->next; } currkey->totelem= G.totvert; if(currkey->data) MEM_freeN(currkey->data); currkey->data = newkey; currkey= currkey->next; } } if(oldverts) MEM_freeN(oldverts); if(actkey) do_spec_key(me->key); /* te be sure: clear ->vn pointers */ eve= em->verts.first; while(eve) { eve->vn= 0; eve= eve->next; } /* we do make displist here for dependencies (like particles) */ if (mesh_uses_displist(me)) makeDispList(G.obedit); /* sticky */ if(me->msticky) { if (ototverttotvert) { ms= MEM_callocN(me->totvert*sizeof(MSticky), "msticky"); memcpy(ms, me->msticky, ototvert*sizeof(MSticky)); MEM_freeN(me->msticky); me->msticky= ms; error("Sticky was too small"); } } waitcursor(0); } void remake_editMesh(void) { make_editMesh(); allqueue(REDRAWVIEW3D, 0); makeDispList(G.obedit); BIF_undo_push("Undo all changes"); } /* *************** SEPARATE (partial exit editmode) *************/ void separatemenu(void) { short event; if(G.editMesh->verts.first==NULL) return; event = pupmenu("Separate (No undo!) %t|Selected%x1|All Loose Parts%x2"); if (event==0) return; waitcursor(1); switch (event) { case 1: separate_mesh(); break; case 2: separate_mesh_loose(); break; } waitcursor(0); } void separate_mesh(void) { EditMesh *em = G.editMesh; EditMesh emcopy; EditVert *eve, *v1; EditEdge *eed, *e1; EditFace *efa, *vl1; Object *oldob; Mesh *me, *men; Base *base, *oldbase; ListBase edve, eded, edvl; float trans[9]; TEST_EDITMESH waitcursor(1); me= get_mesh(G.obedit); if(me->key) { error("Can't separate with vertex keys"); return; } EM_selectmode_set(); // enforce full consistant selection flags /* we are going to abuse the system as follows: * 1. add a duplicate object: this will be the new one, we remember old pointer * 2: then do a split if needed. * 3. put apart: all NOT selected verts, edges, faces * 4. call load_editMesh(): this will be the new object * 5. freelist and get back old verts, edges, facs */ /* make only obedit selected */ base= FIRSTBASE; while(base) { if(base->lay & G.vd->lay) { if(base->object==G.obedit) base->flag |= SELECT; else base->flag &= ~SELECT; } base= base->next; } /* no test for split, split doesn't split when a loose part is selected */ /* SPLIT: first make duplicate */ adduplicateflag(SELECT); /* SPLIT: old faces have 3x flag 128 set, delete these ones */ delfaceflag(128); /* since we do tricky things with verts/edges/faces, this makes sure all is selected coherent */ EM_selectmode_set(); /* set apart: everything that is not selected */ edve.first= edve.last= eded.first= eded.last= edvl.first= edvl.last= 0; eve= em->verts.first; while(eve) { v1= eve->next; if((eve->f & SELECT)==0) { BLI_remlink(&em->verts, eve); BLI_addtail(&edve, eve); } eve= v1; } eed= em->edges.first; while(eed) { e1= eed->next; if((eed->f & SELECT)==0) { BLI_remlink(&em->edges, eed); BLI_addtail(&eded, eed); } eed= e1; } efa= em->faces.first; while(efa) { vl1= efa->next; if((efa->f & SELECT)==0) { BLI_remlink(&em->faces, efa); BLI_addtail(&edvl, efa); } efa= vl1; } oldob= G.obedit; oldbase= BASACT; trans[0]=trans[1]=trans[2]=trans[3]=trans[4]=trans[5]= 0.0; trans[6]=trans[7]=trans[8]= 1.0; G.qual |= LR_ALTKEY; /* patch to make sure we get a linked duplicate */ adduplicate(trans); G.qual &= ~LR_ALTKEY; G.obedit= BASACT->object; /* basact was set in adduplicate() */ men= copy_mesh(me); set_mesh(G.obedit, men); /* because new mesh is a copy: reduce user count */ men->id.us--; load_editMesh(); BASACT->flag &= ~SELECT; /* we cannot free the original buffer... */ emcopy= *G.editMesh; emcopy.allverts= NULL; emcopy.alledges= NULL; emcopy.allfaces= NULL; free_editMesh(&emcopy); em->verts= edve; em->edges= eded; em->faces= edvl; /* hashedges are freed now, make new! */ editMesh_set_hash(); G.obedit= oldob; BASACT= oldbase; BASACT->flag |= SELECT; waitcursor(0); countall(); allqueue(REDRAWVIEW3D, 0); makeDispList(G.obedit); } void separate_mesh_loose(void) { EditMesh *em = G.editMesh; EditMesh emcopy; EditVert *eve, *v1; EditEdge *eed, *e1; EditFace *efa, *vl1; Object *oldob; Mesh *me, *men; Base *base, *oldbase; ListBase edve, eded, edvl; float trans[9]; int vertsep=0; short done=0, check=1; TEST_EDITMESH waitcursor(1); /* we are going to abuse the system as follows: * 1. add a duplicate object: this will be the new one, we remember old pointer * 2: then do a split if needed. * 3. put apart: all NOT selected verts, edges, faces * 4. call load_editMesh(): this will be the new object * 5. freelist and get back old verts, edges, facs */ while(!done){ vertsep=check=1; countall(); me= get_mesh(G.obedit); if(me->key) { error("Can't separate a mesh with vertex keys"); return; } /* make only obedit selected */ base= FIRSTBASE; while(base) { if(base->lay & G.vd->lay) { if(base->object==G.obedit) base->flag |= SELECT; else base->flag &= ~SELECT; } base= base->next; } /*--------- Select connected-----------*/ EM_clear_flag_all(SELECT); /* Select a random vert to start with */ eve= em->verts.first; eve->f |= SELECT; while(check==1) { check= 0; eed= em->edges.first; while(eed) { if(eed->h==0) { if(eed->v1->f & SELECT) { if( (eed->v2->f & SELECT)==0 ) { eed->v2->f |= SELECT; vertsep++; check= 1; } } else if(eed->v2->f & SELECT) { if( (eed->v1->f & SELECT)==0 ) { eed->v1->f |= SELECT; vertsep++; check= SELECT; } } } eed= eed->next; } } /*----------End of select connected--------*/ /* If the amount of vertices that is about to be split == the total amount of verts in the mesh, it means that there is only 1 unconnected object, so we don't have to separate */ if(G.totvert==vertsep) done=1; else{ /* No splitting: select connected goes fine */ EM_select_flush(); // from verts->edges->faces /* set apart: everything that is not selected */ edve.first= edve.last= eded.first= eded.last= edvl.first= edvl.last= 0; eve= em->verts.first; while(eve) { v1= eve->next; if((eve->f & SELECT)==0) { BLI_remlink(&em->verts, eve); BLI_addtail(&edve, eve); } eve= v1; } eed= em->edges.first; while(eed) { e1= eed->next; if( (eed->f & SELECT)==0 ) { BLI_remlink(&em->edges, eed); BLI_addtail(&eded, eed); } eed= e1; } efa= em->faces.first; while(efa) { vl1= efa->next; if( (efa->f & SELECT)==0 ) { BLI_remlink(&em->faces, efa); BLI_addtail(&edvl, efa); } efa= vl1; } oldob= G.obedit; oldbase= BASACT; trans[0]=trans[1]=trans[2]=trans[3]=trans[4]=trans[5]= 0.0; trans[6]=trans[7]=trans[8]= 1.0; G.qual |= LR_ALTKEY; /* patch to make sure we get a linked duplicate */ adduplicate(trans); G.qual &= ~LR_ALTKEY; G.obedit= BASACT->object; /* basact was set in adduplicate() */ men= copy_mesh(me); set_mesh(G.obedit, men); /* because new mesh is a copy: reduce user count */ men->id.us--; load_editMesh(); BASACT->flag &= ~SELECT; /* we cannot free the original buffer... */ emcopy= *G.editMesh; emcopy.allverts= NULL; emcopy.alledges= NULL; emcopy.allfaces= NULL; free_editMesh(&emcopy); em->verts= edve; em->edges= eded; em->faces= edvl; /* hashedges are freed now, make new! */ editMesh_set_hash(); G.obedit= oldob; BASACT= oldbase; BASACT->flag |= SELECT; } } /* unselect the vertices that we (ab)used for the separation*/ EM_clear_flag_all(SELECT); waitcursor(0); countall(); allqueue(REDRAWVIEW3D, 0); makeDispList(G.obedit); } /* ******************************************** */ /* *************** UNDO ***************************** */ /* new mesh undo, based on pushing editmesh data itself */ /* reuses same code as for global and curve undo... unify that (ton) */ /* only one 'hack', to save memory it doesn't store the first push, but does a remake editmesh */ /* a compressed version of editmesh data */ typedef struct EditVertC { float no[3]; float co[3]; unsigned char f, h; short totweight; struct MDeformWeight *dw; int keyindex; } EditVertC; typedef struct EditEdgeC { int v1, v2; unsigned char f, h, seam, pad; short crease, fgoni; } EditEdgeC; typedef struct EditFaceC { int v1, v2, v3, v4; unsigned char mat_nr, flag, f, h, puno, fgonf; short pad1; } EditFaceC; typedef struct UndoMesh { EditVertC *verts; EditEdgeC *edges; EditFaceC *faces; TFace *tfaces; int totvert, totedge, totface; } UndoMesh; /* for callbacks */ static void free_undoMesh(void *umv) { UndoMesh *um= umv; EditVertC *evec; int a; for(a=0, evec= um->verts; atotvert; a++, evec++) { if(evec->dw) MEM_freeN(evec->dw); } if(um->verts) MEM_freeN(um->verts); if(um->edges) MEM_freeN(um->edges); if(um->faces) MEM_freeN(um->faces); if(um->tfaces) MEM_freeN(um->tfaces); MEM_freeN(um); } static void *editMesh_to_undoMesh(void) { EditMesh *em= G.editMesh; UndoMesh *um; Mesh *me= G.obedit->data; EditVert *eve; EditEdge *eed; EditFace *efa; EditVertC *evec=NULL; EditEdgeC *eedc=NULL; EditFaceC *efac=NULL; TFace *tface= NULL; int a=0; um= MEM_callocN(sizeof(UndoMesh), "undomesh"); for(eve=em->verts.first; eve; eve= eve->next) um->totvert++; for(eed=em->edges.first; eed; eed= eed->next) um->totedge++; for(efa=em->faces.first; efa; efa= efa->next) um->totface++; /* malloc blocks */ if(um->totvert) evec= um->verts= MEM_callocN(um->totvert*sizeof(EditVertC), "allvertsC"); if(um->totedge) eedc= um->edges= MEM_callocN(um->totedge*sizeof(EditEdgeC), "alledgesC"); if(um->totface) efac= um->faces= MEM_callocN(um->totface*sizeof(EditFaceC), "allfacesC"); if(me->tface) tface= um->tfaces= MEM_mallocN(um->totface*sizeof(TFace), "all tfacesC"); //printf("copy editmesh %d\n", um->totvert*sizeof(EditVert) + um->totedge*sizeof(EditEdge) + um->totface*sizeof(EditFace)); //printf("copy undomesh %d\n", um->totvert*sizeof(EditVertC) + um->totedge*sizeof(EditEdgeC) + um->totface*sizeof(EditFaceC)); /* now copy vertices */ for(eve=em->verts.first; eve; eve= eve->next, evec++, a++) { VECCOPY(evec->co, eve->co); VECCOPY(evec->no, eve->no); evec->f= eve->f; evec->h= eve->h; evec->keyindex= eve->keyindex; evec->totweight= eve->totweight; evec->dw= MEM_dupallocN(eve->dw); eve->vn= (EditVert *)a; } /* copy edges */ for(eed=em->edges.first; eed; eed= eed->next, eedc++) { eedc->v1= (int)eed->v1->vn; eedc->v2= (int)eed->v2->vn; eedc->f= eed->f; eedc->h= eed->h; eedc->seam= eed->seam; eedc->crease= (short)(eed->crease*255.0); eedc->fgoni= eed->fgoni; } /* copy faces */ for(efa=em->faces.first; efa; efa= efa->next, efac++) { efac->v1= (int)efa->v1->vn; efac->v2= (int)efa->v2->vn; efac->v3= (int)efa->v3->vn; if(efa->v4) efac->v4= (int)efa->v4->vn; else efac->v4= -1; efac->mat_nr= efa->mat_nr; efac->flag= efa->flag; efac->f= efa->f; efac->h= efa->h; efac->puno= efa->puno; efac->fgonf= efa->fgonf; if(tface) { *tface= efa->tf; tface++; } } return um; } static void undoMesh_to_editMesh(void *umv) { UndoMesh *um= umv; EditMesh *em= G.editMesh; EditVert *eve, **evar=NULL; EditEdge *eed; EditFace *efa; EditVertC *evec; EditEdgeC *eedc; EditFaceC *efac; TFace *tface; int a=0; free_editMesh(G.editMesh); /* malloc blocks */ memset(em, 0, sizeof(EditMesh)); init_editmesh_fastmalloc(em, um->totvert, um->totedge, um->totface); /* now copy vertices */ if(um->totvert) evar= MEM_mallocN(um->totvert*sizeof(EditVert *), "vertex ar"); for(a=0, evec= um->verts; atotvert; a++, evec++) { eve= addvertlist(evec->co); evar[a]= eve; VECCOPY(eve->no, evec->no); eve->f= evec->f; eve->h= evec->h; eve->totweight= evec->totweight; eve->keyindex= evec->keyindex; eve->dw= MEM_dupallocN(evec->dw); } /* copy edges */ for(a=0, eedc= um->edges; atotedge; a++, eedc++) { eed= addedgelist(evar[eedc->v1], evar[eedc->v2], NULL); eed->f= eedc->f; eed->h= eedc->h; eed->seam= eedc->seam; eed->fgoni= eedc->fgoni; eed->crease= ((float)eedc->crease)/255.0; } /* copy faces */ tface= um->tfaces; for(a=0, efac= um->faces; atotface; a++, efac++) { if(efac->v4 != -1) efa= addfacelist(evar[efac->v1], evar[efac->v2], evar[efac->v3], evar[efac->v4], NULL, NULL); else efa= addfacelist(evar[efac->v1], evar[efac->v2], evar[efac->v3], NULL, NULL ,NULL); efa->mat_nr= efac->mat_nr; efa->flag= efac->flag; efa->f= efac->f; efa->h= efac->h; efa->puno= efac->puno; efa->fgonf= efac->fgonf; if(tface) { efa->tf= *tface; tface++; } } end_editmesh_fastmalloc(); if(evar) MEM_freeN(evar); } /* and this is all the undo system needs to know */ void undo_push_mesh(char *name) { undo_editmode_push(name, free_undoMesh, undoMesh_to_editMesh, editMesh_to_undoMesh); } /* *************** END UNDO *************/