This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/src/editmesh.c
2004-10-09 20:49:55 +00:00

1837 lines
40 KiB
C

/**
* $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 <stdlib.h>
#include <string.h>
#include <math.h>
#ifdef HAVE_CONFIG_H
#include <config.h>
#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"
#include "render.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, example->e1);
e2= addedgelist(v2, v3, example->e2);
if(v4) e3= addedgelist(v3, v4, example->e3);
else e3= addedgelist(v3, v1, example->e3);
if(v4) e4= addedgelist(v4, v1, example->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; a<EDHASHSIZE; a++, he++) {
if(he->eed) used++;
hen= he->next;
nr= 0;
while(hen) {
nr++;
hen= hen->next;
}
if(max<nr) max= nr;
}
printf("hastab used %d max %d\n", used, max);
}
#endif
if(em->hashedgetab) 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->f<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->f>=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; a<tot; a++, mvert++) {
eve= addvertlist(mvert->co);
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; a<me->totedge; 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; a<me->totface; 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) */
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) { // 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;
}
/* displists of all users, including this one */
freedisplist(&me->disp);
freedisplist(&G.obedit->disp);
/* sticky */
if(me->msticky) {
if (ototvert<me->totvert) {
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;
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];
int ok, flag;
TEST_EDITMESH
waitcursor(1);
me= get_mesh(G.obedit);
if(me->key) {
error("Can't separate with vertex keys");
return;
}
/* 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;
}
/* test for split */
ok= 0;
eed= em->edges.first;
while(eed) {
flag= (eed->v1->f & SELECT)+(eed->v2->f & SELECT);
if(flag==SELECT) {
ok= 1;
break;
}
eed= eed->next;
}
if(ok) {
/* SPLIT: first make duplicate */
adduplicateflag(SELECT);
/* SPLIT: old faces have 3x flag 128 set, delete these ones */
delfaceflag(128);
}
/* 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 ok, vertsep=0, flag;
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{
/* Test for splitting: Separate selected */
ok= 0;
eed= em->edges.first;
while(eed) {
flag= (eed->v1->f & SELECT)+(eed->v2->f & SELECT);
if(flag==SELECT) {
ok= 1;
break;
}
eed= eed->next;
}
if(ok) {
/* SPLIT: first make duplicate */
adduplicateflag(SELECT);
/* SPLIT: old faces have 3x flag 128 set, delete these ones */
delfaceflag(128);
}
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; a<um->totvert; 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; a<um->totvert; 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; a<um->totedge; 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; a<um->totface; 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 *************/