/**
 * $Id: editmesh.c,v 1.125 2004/12/21 12:10:35 ton Exp $
 *
 * ***** 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, 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; 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->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; 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) */
	/* 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 (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;

	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; 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 *************/


