/**********************************************************************
 *<
	FILE: editmops.cpp

	DESCRIPTION:  Edit Mesh OSM operations

	CREATED BY: Rolf Berteig

	HISTORY: created 30 March, 1995

 *>	Copyright (c) 1994, All Rights Reserved.
 **********************************************************************/
#include "mods.h"
#include "editmesh.h"
#include "decomp.h"
#include "imtl.h"

// in mods.cpp
extern HINSTANCE hInstance;


void EditMeshMod::ClearMeshDataFlag(ModContextList& mcList,DWORD f)
	{
	for ( int i = 0; i < mcList.Count(); i++ ) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if ( !meshData ) continue;
		meshData->SetFlag(f,FALSE);
		}
	}


void EditMeshMod::XFormVerts( 
		XFormProc *xproc, 
		TimeValue t, 
		Matrix3& partm, 
		Matrix3& tmAxis  ) 
	{	
	ModContextList mcList;		
	INodeTab nodes;
	Matrix3 mat,imat;	
	Interval valid;
	int numAxis;
	BOOL affectRegion = IsDlgButtonChecked(hEditMeshParams,IDC_AFFECTREGION);
	BOOL ignoreBack = IsDlgButtonChecked(hEditMeshParams,IDC_BACKFACE_VERTS);

	if (!iObjParams) return;	

	iObjParams->GetModContexts(mcList,nodes);	
	if (iObjParams->GetRefCoordSys()!=COORDS_LOCAL && 
		selLevel==EM_VERTEX) {
		// For vertices, unless we're using a local coord. system, there's
		// only 1 axis.
		numAxis = NUMAXIS_ALL;
	} else {
		numAxis = iObjParams->GetNumAxis();
		}

	ClearMeshDataFlag(mcList,EMD_BEENDONE);

	for ( int i = 0; i < mcList.Count(); i++ ) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if ( !meshData ) continue;
		if ( meshData->GetFlag(EMD_BEENDONE) ) continue;
		
		// If the mesh isn't yet cache, this will cause it to get cached.
		Mesh *mesh = meshData->TempData(this)->GetMesh(t);
		
		// If this is the first edit, then the delta arrays will be allocated
		meshData->BeginEdit(t);

		// Selected vertices - either directly or indirectly through selected faces or edges.
		BitArray sel = mesh->VertexTempSel();

		// Keep track of the average delta and the average center of selected verts
		Point3 vcent(0,0,0);
		Point3 vdelt(0,0,0);
		int numSel = 0;

		Point3Tab *vertNorms = NULL;
		Point3 selNorm(0,0,0);
		if (affectRegion && ignoreBack) {
			vertNorms = meshData->TempData(this)->VertNormals(t);
			// Average the selected vert's normals			
			for (int k=0; k<mesh->getNumVerts(); k++) {
				if (mesh->vertSel[k]) {
					selNorm += (*vertNorms)[k];					
					}
				}
			selNorm = Normalize(selNorm);
			}

		// Save the unmodified verts.
		if ( theHold.Holding() && !meshData->GetFlag(EMD_HELD) ) {
			theHold.Put(new VertexEditRestore(meshData,this));
			meshData->SetFlag(EMD_HELD,TRUE);
			}

		// Compute the transforms
		if (numAxis==NUMAXIS_INDIVIDUAL) {
			if (selLevel==EM_FACE || selLevel==EM_EDGE) {
				DWORDTab *clust = meshData->TempData(this)->VertCluster(t);
				DWORD count;
				
				if (selLevel==EM_FACE) {
					count = meshData->TempData(this)->FaceCluster(t)->count;
				} else {
					count = meshData->TempData(this)->EdgeCluster(t)->count;
					}
			
				// Do each cluster one at a time
				for ( DWORD j = 0; j < count; j++ ) {
					tmAxis = iObjParams->GetTransformAxis(nodes[i],j);
					mat    = nodes[i]->GetObjectTM(t) * Inverse(tmAxis);
					imat   = Inverse(mat);
					xproc->SetMat(mat);

					for ( int k = 0; k < mesh->getNumVerts(); k++ ) {
						if (sel[k]) {
							if ((*clust)[k]==j) {
								// XForm the cache vertices
								Point3 old = mesh->getVert(k);
								Point3 delta;
								delta = xproc->proc(old,mat,imat) - old;
								
								mesh->setVert(k,delta+old);
				
								// Move the delta's vertices.
								meshData->vdelta.Move(t,k,delta);
								vdelt += delta;
								vcent += old;
								numSel++;
								}
							}
						}
					}
			} else 
			if (selLevel == EM_VERTEX) {
				for ( int k = 0; k < mesh->getNumVerts(); k++ ) {
					if (sel[k]) {
						Point3 delta;

						tmAxis = iObjParams->GetTransformAxis(nodes[i],k);
						mat    = nodes[i]->GetObjectTM(t) * Inverse(tmAxis);
						imat   = Inverse(mat);
						xproc->SetMat(mat);
						
						// XForm the cache vertices
						Point3 old = mesh->getVert(k);
						delta = xproc->proc(old,mat,imat) - old;
						mesh->setVert(k,old+delta);
		
						// Move the delta's vertices.
						meshData->vdelta.Move(t,k,delta);
						vdelt += delta;
						vcent += old;
						numSel++;
						}
					}
				
				}						
		} else {
			mat = nodes[i]->GetObjectTM(t) * Inverse(tmAxis);
			imat = Inverse(mat);
			xproc->SetMat(mat);
					
			for ( int j = 0; j < mesh->getNumVerts(); j++ ) {
				if ( sel[j] ) {
					// XForm the cache vertices
					Point3 old = mesh->getVert(j);
					Point3 delta;
					
					delta = xproc->proc(old,mat,imat)-old;
					mesh->setVert(j,delta+old);
				
					// Move the delta's vertices.
					meshData->vdelta.Move(t,j,delta);
					vdelt += delta;
					vcent += old;
					numSel++;
					}
				}
			}
		
		if (numSel && affectRegion) {
			vdelt /= float(numSel);
			vcent /= float(numSel);

			InitCalcRegion(vcent,vdelt);

			// Affect deselected vertices
			for (int j = 0; j < mesh->getNumVerts(); j++) {
				if (!sel[j]) {
					// Skip backfacing verts
					if (vertNorms) {
						if (DotProd((*vertNorms)[j],selNorm)<=0.0f) {
							continue;
							}
						}
					Point3 delta = CalcRegionAffect(mesh->verts[j],vcent,vdelt);
					mesh->verts[j] += delta;
					meshData->vdelta.Move(t,j,delta);
					}
				}
			}

		meshData->TempData(this)->Invalidate(PART_GEOM);
		meshData->SetFlag(EMD_BEENDONE,TRUE);		
		}
	
	nodes.DisposeTemporary();
	ClearMeshDataFlag(mcList,EMD_BEENDONE);
	NotifyDependents(FOREVER, PART_GEOM, REFMSG_CHANGE);
	}


void EditMeshMod::InitCalcRegion(Point3 center,Point3 delta)
	{		
	}

Point3 EditMeshMod::CalcRegionAffect(Point3 v,Point3 center,Point3 delta)
	{
	float dist = Length(v-center);
	if (dist > falloff) return Point3(0,0,0);
	return AffectRegionFunction(dist,falloff,pinch,bubble) * delta;
	//return ((falloff - dist)/falloff) * delta;
	}
	


void EditMeshMod::Move( TimeValue t, Matrix3& partm, Matrix3& tmAxis, Point3& val, BOOL localOrigin )
	{
	MoveXForm proc(val);
	XFormVerts(&proc,t,partm,tmAxis); 	
	}

void EditMeshMod::Rotate( TimeValue t, Matrix3& partm, Matrix3& tmAxis, Quat& val, BOOL localOrigin )
	{
	RotateXForm proc(val);
	XFormVerts(&proc,t,partm,tmAxis); 	
	}

void EditMeshMod::Scale( TimeValue t, Matrix3& partm, Matrix3& tmAxis, Point3& val, BOOL localOrigin )
	{
	ScaleXForm proc(val);
	XFormVerts(&proc,t,partm,tmAxis); 	
	}


void EditMeshMod::TransformStart(TimeValue t)
	{
	if (iObjParams) iObjParams->LockAxisTripods(TRUE);
	}

void EditMeshMod::TransformFinish(TimeValue t)
	{
	if (iObjParams) iObjParams->LockAxisTripods(FALSE);
	}

void EditMeshMod::TransformCancel(TimeValue t)
	{
	if (iObjParams) iObjParams->LockAxisTripods(FALSE);
	}

void EditMeshMod::DeleteKey()
	{
	ModContextList mcList;	
	INodeTab nodes;
	int delIso = -1;
	int id = 0;

	if (!iObjParams) return;

	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);
	
	theHold.Begin();

	for ( int i = 0; i < mcList.Count(); i++ ) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if ( !meshData ) continue;
		if ( meshData->GetFlag(EMD_BEENDONE) ) continue;		
				
		// If this is the first edit, then the delta arrays will be allocated
		meshData->BeginEdit(iObjParams->GetTime());
		
		// Start a restore object...
		TopoEditRestore *rest = new TopoEditRestore(meshData,this);		
		theHold.Put(rest);

		switch (selLevel) {
			case EM_VERTEX:
				meshData->DeleteVertSet(
					meshData->TempData(this)->GetMesh(iObjParams->GetTime()),
					meshData->vsel);
			 	id = IDS_RB_DELETEVERT;
				break;

			case EM_FACE: {
				Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());
				BitArray iso;
				iso.SetSize(mesh->getNumVerts());
				
				meshData->DeleteFaceSet(
					mesh,meshData->fsel,&iso);

				if (iso.NumberSet()) {
					if (delIso==-1) {
						TSTR str1 = GetString(IDS_RB_DELETEISOLATED);
						TSTR str2 = GetString(IDS_RB_DELETEFACE);
						if (IDYES==MessageBox(
							hEditMeshParams,
							str1,
							str2,
							MB_ICONQUESTION|MB_YESNO)) {
						
							delIso = 1;
							meshData->DeleteVertSet(mesh,iso);
						} else {
							delIso = 0;
							}
					} else {
						if (delIso) {
							meshData->DeleteVertSet(mesh,iso);
							}
						}
					}
				id = IDS_RB_DELETEFACE;
				break;
				}

			case EM_EDGE: {
				BitArray set;				
				AdjFaceList *adj = meshData->TempData(this)->FaceAdjList(iObjParams->GetTime());
				Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());
				set.SetSize(mesh->getNumFaces());

				for (int i=0; i<mesh->getNumFaces(); i++) {
					for (int j=0; j<3; j++) {
						if (meshData->esel[i*3+j]) {
							// Mark this face to be deleted
							set.Set(i);
							
							// Mark the other side
							DWORD fi = (*adj)[i].f[j];
							if (fi!=UNDEFINED) {
								set.Set(fi);
								}
							break;
							}
						}
					}				
				meshData->DeleteFaceSet(mesh,set);
				id = IDS_RB_DELETEEDGE;
				break;
				}
			}
		meshData->SetFlag(EMD_BEENDONE,TRUE);
		meshData->Invalidate(PART_GEOM|PART_TOPO);
		}
	
	theHold.Accept(GetString(id));

	nodes.DisposeTemporary();
	NotifyDependents(FOREVER, PART_TOPO|PART_GEOM, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}

void EditMeshMod::AcceptCloneSelSubComponents(TimeValue t)
	{
	if (selLevel!=EM_VERTEX && selLevel!=EM_FACE) return;
	TSTR buf1 = GetString(IDS_RB_DOCREATENEWOBJECT);
	TSTR buf2 = GetString(IDS_RB_EDITMESH2);
	if (IDNO == MessageBox(hEditMeshParams,
			buf1,buf2,MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2)) {
		return;
		}
	TSTR name;
	if (!GetDetachObjectName(hEditMeshParams,name,iObjParams)) {
		return;
		}
	
	switch (selLevel) {
		case EM_VERTEX:
			DetachVertFaces(name,FALSE);
		 	break;

		case EM_FACE:
			DetachVertFaces(name,TRUE);
			break;
		}
	}

void EditMeshMod::CloneSelSubComponents(TimeValue t)
	{
	ModContextList mcList;	
	INodeTab nodes;

	if (!iObjParams) return;

	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);
	
	theHold.Begin();

	for ( int i = 0; i < mcList.Count(); i++ ) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if ( !meshData ) continue;
		if ( meshData->GetFlag(EMD_BEENDONE) ) continue;		
		Mesh *mesh = meshData->TempData(this)->GetMesh(t);
				
		// If this is the first edit, then the delta arrays will be allocated
		meshData->BeginEdit(t);
		
		// Start a restore object...
		TopoEditRestore *rest = new TopoEditRestore(meshData,this);		
		theHold.Put(rest);

		switch (selLevel) {
			case EM_VERTEX:				
				meshData->CloneSelVerts(mesh);
			 	break;

			case EM_FACE:
				meshData->CloneSelFaces(mesh);
				break;

			case EM_EDGE:
				ExtrudeFaces(
					*mesh, 
					mesh->edgeSel, NULL,
					*meshData->TempData(this)->AdjList(t),
					meshData->tdelta, meshData->vdelta,
					meshData,
					FALSE);					
				break;				
			}
		meshData->SetFlag(EMD_BEENDONE,TRUE);
		meshData->Invalidate(PART_GEOM|PART_TOPO);
		}
	
	theHold.Accept(GetString(IDS_RB_CLONE));

	nodes.DisposeTemporary();
	NotifyDependents(FOREVER, PART_TOPO|PART_GEOM, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}


BOOL EditMeshMod::WeldVerts(float thresh,int type,Point3 weldPoint)
	{
	ModContextList mcList;	
	INodeTab nodes;
	BOOL found = FALSE;

	if (!iObjParams) return FALSE;

	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);
	
	theHold.Begin();

	for ( int i = 0; i < mcList.Count(); i++ ) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if ( !meshData ) continue;
		if ( meshData->GetFlag(EMD_BEENDONE) ) continue;		
				
		// If this is the first edit, then the delta arrays will be allocated
		meshData->BeginEdit(iObjParams->GetTime());
		
		// Start a restore object...
		TopoEditRestore *rest = new TopoEditRestore(meshData,this);		
		theHold.Put(rest);

		// Do the weld
		BOOL ff = meshData->WeldVerts(
			meshData->TempData(this)->GetMesh(iObjParams->GetTime()),
			thresh,type,weldPoint);
		if (ff) found = TRUE;

		meshData->SetFlag(EMD_BEENDONE,TRUE);
		meshData->Invalidate(PART_GEOM|PART_TOPO);
		}
	
	switch (type) {
		case WELD_COLLAPSE: theHold.Accept(GetString(IDS_RB_COLLAPSE)); break;
		case WELD_THRESHOLD: theHold.Accept(GetString(IDS_RB_WELDVERTS)); break;
		case WELD_TOVERT: theHold.Accept(GetString(IDS_RB_WELDVERTS)); break;
		}	

	nodes.DisposeTemporary();
	NotifyDependents(FOREVER, PART_TOPO|PART_GEOM, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	return found;
	}

void EditMeshMod::DoExtrude()
	{
	ModContextList mcList;	
	INodeTab nodes;
	DWORD *clones = NULL;	

	if ( !iObjParams ) return;

	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);

	theHold.Begin();

	for ( int i = 0; i < mcList.Count(); i++ ) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if ( !meshData ) continue;
		if ( meshData->GetFlag(EMD_BEENDONE) ) continue;		
		
		// If the mesh isn't yet cache, this will cause it to get cached.
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());
		
		// If this is the first edit, then the delta arrays will be allocated
		meshData->BeginEdit(iObjParams->GetTime());
				 
		TopoEditRestore *rest = new TopoEditRestore(meshData,this);		
		theHold.Put(rest);

		// Extrude the faces in the cache		
		ExtrudeFaces(
			*mesh, 
			selLevel == EM_FACE ? mesh->faceSel : mesh->edgeSel, NULL,
			*meshData->TempData(this)->AdjList(iObjParams->GetTime()),
			meshData->tdelta, meshData->vdelta,
			meshData,
			selLevel == EM_FACE );
		meshData->TempData(this)->Invalidate(PART_TOPO|PART_GEOM);
		
		meshData->SetFlag(EMD_BEENDONE,TRUE);
		}
	
	// Accept all the extudes so they go on the undo stack
	theHold.Accept(GetString(IDS_RB_EXTRUDE));

	nodes.DisposeTemporary();
	ClearMeshDataFlag(mcList,EMD_BEENDONE);
	NotifyDependents(FOREVER, PART_TOPO|PART_GEOM, REFMSG_CHANGE);
	}					


void ExtrudeFaces( 
		Mesh& mesh,
		BitArray& sel,
		ExtrudeRestore *xrest, 
		AdjEdgeList& el, 
		TopoDelta& tdelta,
		VertexDelta& vdelta,
		EditMeshData *meshData,
		BOOL doFace ) 
	{	
	int nf = mesh.getNumFaces(), nv = mesh.getNumVerts();
	int nvc = nv - tdelta.nverts.Count();
	DWORD *vclones = new DWORD[nv];
	BitArray addClones;
	Face *f0, *f1;
	DWORD v0,v1;
	int a,b, oldnvertCount = tdelta.nverts.Count();
	Tab<Point3> newvert;
	Tab<Point3> addvert;
	Tab<Face> newface;
	Tab<TVFace> newTVface;
	DWORDTab backPatch;
	Face f;
	f.smGroup = 0;
	f.flags   = EDGE_A|EDGE_B;
	DWORDTab invVert;	
	BitArray usedVerts;

	// Flag vertices used by selected faces.
	usedVerts.SetSize(nv);
	for (int i=0; i<nf; i++) {
		usedVerts.Set(mesh.faces[i].v[0]);
		usedVerts.Set(mesh.faces[i].v[1]);
		usedVerts.Set(mesh.faces[i].v[2]);
		}

	// Build a look up table that will give the input mesh indices of
	// a vertex index.
	tdelta.BuildInvCloneTable(invVert);

	// We needed to keep a bit with each clone map to indicate if this is
	// a clone from an added vert
	addClones.SetSize(nv);

	for (i = 0; i < nv; i++) {
		vclones[i] = UNDEFINED;
		}

	for ( i = 0; i < el.edges.Count(); i++ ) {		
		MEdge edge = el.edges[i];
		
		if (doFace) {
			// Does this edge have a selected face and a deselected face?
			if ( edge.f[0] != UNDEFINED ) {
				a = sel[edge.f[0]] ? 1 : 0;			
				f0 = &mesh.faces[edge.f[0]];
			} else {
				f0 = NULL;
				a  = 0;
				}
			if ( edge.f[1] != UNDEFINED ) {
				b =  sel[edge.f[1]] ? 1 : 0;			
				f1 = &mesh.faces[edge.f[1]];
			} else {
				f1 = NULL;
				b  = 0;
				}
		} else {
			if (edge.Selected(mesh.faces,sel)) {
				if (edge.f[0] != UNDEFINED) {
					f0 = &mesh.faces[edge.f[0]];
					a = 1;
				} else {
					f0 = NULL;
					a  = 0;
					}
				if (edge.f[1] != UNDEFINED) {
					f1 = &mesh.faces[edge.f[1]];
					b = 1;
				} else {
					f1 = NULL;
					b = 0;
					}
			} else {
				continue;
				}
			}
		
		if ( a || b ) {
			v0 = edge.v[0];
			v1 = edge.v[1];
			SwapEdgeVerts(a?f0:f1,v0,v1);
			if (!doFace)  {
				DWORD temp = v0;
				v0 = v1;
				v1 = temp;
				}

			// Clone 'em if they're not already cloned.
			if (vclones[v0] == UNDEFINED) {
				// If a vertex is an added vertex then we can't clone it so
				// we must add it
				if (v0 >= (DWORD)nvc) {
					addvert.Append(1,&mesh.verts[v0],20);
					vclones[v0] = nv + addvert.Count()-1;
					
					// Put a record of the addition in the tdelta
					tdelta.nverts.Append(1,&mesh.verts[v0],10);

					// Note that this clone is a clone of an added vert
					addClones.Set(v0);
				} else {
					// We can clone it...
					newvert.Append(1,&mesh.verts[v0],20);
					vclones[v0] = nvc + newvert.Count()-1;

					// Put a record of the cloning in the tdelta
					tdelta.cverts.Append(1,&invVert[v0],10);
					}
				}
			if ( vclones[v1] == UNDEFINED ) {
				// If a vertex is an added vertex then we can't clone it so
				// we must add it
				if (v1 >= (DWORD)nvc) {
					addvert.Append(1,&mesh.verts[v1],20);
					vclones[v1] = nv + addvert.Count()-1;
					
					// Put a record of the addition in the tdelta
					tdelta.nverts.Append(1,&mesh.verts[v1],10);

					// Note that this clone is a clone of an added vert
					addClones.Set(v1);
				} else {
					// We can clone it...
					newvert.Append(1,&mesh.verts[v1],20);
					vclones[v1] = nvc + newvert.Count()-1;
				
					// Put a record of the cloning in the tdelta
					tdelta.cverts.Append(1,&invVert[v1],10);
					}
				}
			}

		if (a!=b || !doFace) {
			// Add 2 faces stitching the clone to their originals.
			f.v[0] = v0;
			f.v[1] = v1;
			f.v[2] = vclones[v1];
			newface.Append(1,&f,20);
			
			// a new TV face
			if (mesh.tvFace) {
				Face &ff = f0?*f0:*f1;
				TVFace &tff = mesh.tvFace[edge.f[0]!=UNDEFINED ? edge.f[0] : edge.f[1]];
				int i0 = GetFaceVertIndex(ff,v0);
				int i1 = GetFaceVertIndex(ff,v1);
				TVFace tf(tff.t[i0],tff.t[i1],tff.t[i1]);
				newTVface.Append(1,&tf);
				}

			// Record if any verts are clones of added verts
			// If the vert doesn't need to be patched, record that it is in
			// fact a valid clone (by using bit 3,4,5).
			DWORD patch = 0;
			if (addClones[v1]) patch = (1<<2);
			else patch = (1<<(2+3));
			backPatch.Append(1,&patch,5);			

			f.v[0] = vclones[v1];
			f.v[1] = vclones[v0];
			f.v[2] = v0;
			newface.Append(1,&f,20);

			// a new TV face
			if (mesh.tvFace) {
				Face &ff = f0?*f0:*f1;
				TVFace &tff = mesh.tvFace[edge.f[0]!=UNDEFINED ? edge.f[0] : edge.f[1]];
				int i0 = GetFaceVertIndex(ff,v0);
				int i1 = GetFaceVertIndex(ff,v1);
				TVFace tf(tff.t[i1],tff.t[i0],tff.t[i0]);
				newTVface.Append(1,&tf);
				}

			// Record if any verts are clones of added verts
			patch = 0;
			if (addClones[v1]) patch |= (1<<0);
			else patch |= (1<<(0+3));
			if (addClones[v0]) patch |= (1<<1);	
			else patch |= (1<<(1+3));	
			backPatch.Append(1,&patch,5);
			}			
		}
	
	// If we're extruding edges then clear the edge selection and
	// select the new edges.
	if (!doFace) {
		mesh.edgeSel.ClearAll();
		mesh.edgeSel.SetSize((nf+newface.Count())*3);
		for (int i=nf+1; i < nf+newface.Count(); i+=2) {
			mesh.edgeSel.Set(i*3);
			}
		}

	// Clones mapping into the added vert space need to be slid
	// up by the number of new cloned verts
	for (i=0; i<nv; i++) {
		if (addClones[i]) {
			vclones[i] += newvert.Count();
			}
		}

	// Any new faces that reference a vertex cloned from an added vert
	// also need to be fixed up.
	for (i=0; i<backPatch.Count(); i++) {
		for (int j=0; j<3; j++) {
			if (backPatch[i] & (1<<j)) {
				newface[i].v[j] += newvert.Count();
			} else 
			if (!(backPatch[i]&(1<<(j+3)))) {
				// This face was not cloned. It could be referencing
				// an added vertex that needs to be shifted up.
				if (newface[i].v[j] >= (DWORD)(nvc)) {
					newface[i].v[j] += newvert.Count();
					}
				}
			}
		}
	backPatch.Resize(0);

	// Now step through selected faces and remap vert indexes
	// so they reference the clones.
	for (i = 0; i < nf; i++) {
		if (doFace && sel[i]) {
			// Save old face data.
			if (xrest) {
				xrest->vmap.Append(1,&mesh.faces[i].v[0],10);
				xrest->vmap.Append(1,&mesh.faces[i].v[1],10);
				xrest->vmap.Append(1,&mesh.faces[i].v[2],10);
				}
			//assert( vclones[mesh.faces[i].v[0]]!=UNDEFINED );
			//assert( vclones[mesh.faces[i].v[1]]!=UNDEFINED );
			//assert( vclones[mesh.faces[i].v[2]]!=UNDEFINED );			
			if (vclones[mesh.faces[i].v[0]]==UNDEFINED) continue;
			if (vclones[mesh.faces[i].v[1]]==UNDEFINED) continue;
			if (vclones[mesh.faces[i].v[2]]==UNDEFINED) continue;

			for ( int j = 0; j < 3; j++ ) {				
				// remap faces.
				mesh.faces[i].v[j] = vclones[mesh.faces[i].v[j]];				
				}			

			// Also note the change in tdelta
			tdelta.ModifyFace(i,mesh.faces[i]);
		} else {
			// Any face that references an added vertex must be shifted
			// up by the number of new cloned verts we're adding
			for ( int j = 0; j < 3; j++ ) {
				if (mesh.faces[i].v[j] >= (DWORD)(nvc)) {
					mesh.faces[i].v[j] += newvert.Count();
					}
				}
			
			// Also note the change in tdelta
			tdelta.ModifyFace(i,mesh.faces[i]);
			}
		}
	if (xrest) xrest->vmap.Shrink();
	tdelta.cverts.Shrink();

	// Record the number of new vertices and faces added
	if (xrest) {
		xrest->nv = newvert.Count();
		xrest->nf = newface.Count();
		}

	// The cloned vertices need to inherit their originals delta.
	// Slide down verts that were added. Cloned verts come before added verts
	Point3Tab tempDeltas = vdelta.deltas;
	BitArray tempHide    = vdelta.hide;
	vdelta.SetSize(nv+newvert.Count()+addvert.Count(),FALSE);	
	for (i=0; i<tdelta.nverts.Count()-oldnvertCount; i++) {				
		vdelta.deltas[nv+newvert.Count()-1-i] =
			tempDeltas[nv-1-i];				
		vdelta.hide.Set(nv+newvert.Count()-1-i,tempHide[nv-1-i]);		
		}
	tempDeltas.Resize(0);
	tempHide.SetSize(0);

	// Now copy the clones into place. Except clones from added verts
	// who's deltas remain 0
	for (i=0; i<mesh.getNumVerts(); i++) {
		if (vclones[i] != UNDEFINED && !addClones[i]) {
			Point3 p = vdelta[i];
			vdelta.Set(0,vclones[i],p);			
			}
		}
	delete[] vclones;
	
	// Store the topological changes in the tdelta
	tdelta.nfaces.Resize(tdelta.nfaces.Count()+newface.Count());
	for ( i = 0; i < newface.Count(); i++ ) {
		tdelta.nfaces.Append(1,&newface[i]);
		}	
	if (newTVface.Count()) {
		tdelta.nTVfaces.Resize(tdelta.nTVfaces.Count()+newTVface.Count());
		for ( i = 0; i < newTVface.Count(); i++ ) {
			tdelta.nTVfaces.Append(1,&newTVface[i]);
			}	
		}

	// Now add in the new verts and faces.
	nv += newvert.Count() + addvert.Count();
	nf += newface.Count();
	if (mesh.getNumVerts() != nv) {
		Point3 *verts = new Point3[nv];
		
		// First copy original and previously cloned verts
		for (i=0; i<nvc; i++) {
			verts[i] = mesh.verts[i];
			}

		// Next copy new verts
		for ( ; i < nvc+newvert.Count(); i++) {
			verts[i] = newvert[i-nvc];
			}

		// Now the added verts added from previous operations
		for ( ; i < nv - addvert.Count(); i++ ) {
			verts[i] = mesh.verts[i-newvert.Count()];
			}
		
		// And then added verts added from this operation
		for ( ; i < nv; i++ ) {
			verts[i] = addvert[i-nv+addvert.Count()];
			}
		
		delete[] mesh.verts;
		mesh.verts    = verts;
		mesh.numVerts = nv;
		mesh.vertSel.SetSize(nv,TRUE);
		}
	if (mesh.getNumFaces() != nf) {
		Face *faces = new Face[nf];
		for ( i = 0; i < mesh.getNumFaces(); i++ ) {
			faces[i] = mesh.faces[i];
			}
		for ( ; i < nf; i++ ) {
			faces[i] = newface[i-mesh.getNumFaces()];
			}
		delete[] mesh.faces;
		mesh.faces    = faces;		
		
		if (mesh.tvFace) {
			TVFace *tvfaces = new TVFace[nf];
			for ( i = 0; i < mesh.getNumFaces(); i++ ) {
				tvfaces[i] = mesh.tvFace[i];
				}
			for ( ; i < nf; i++ ) {
				tvfaces[i] = newTVface[i-mesh.getNumFaces()];
				}
			delete[] mesh.tvFace;
			mesh.tvFace = tvfaces;
			}

		mesh.numFaces = nf;
		mesh.faceSel.SetSize(nf,TRUE);
		mesh.edgeSel.SetSize(nf*3,TRUE);
		}
	
	meshData->vsel = mesh.vertSel;
	meshData->fsel = mesh.faceSel;
	meshData->esel = mesh.edgeSel;	

	// Now run through the resulting mesh and make sure
	// all the used verts are still used.
	usedVerts.SetSize(nv,TRUE);
	for (i=0; i<nf; i++) {
		usedVerts.Clear(mesh.faces[i].v[0]);
		usedVerts.Clear(mesh.faces[i].v[1]);
		usedVerts.Clear(mesh.faces[i].v[2]);
		}
	// Any of these that aren't being used anymore can
	// be deleted.
	if (usedVerts.NumberSet()) {
		meshData->DeleteVertSet(&mesh,usedVerts);
		}

	mesh.InvalidateGeomCache();
	}

/*-------------------------------------------------------------------*/

void ExtrudeCMode::EnterMode()
	{
	if ( em->hEditMeshParams ) {
		ICustButton *but = GetICustButton(GetDlgItem(em->hEditMeshParams,IDC_EXTRUDE));
		but->SetCheck(TRUE);
		ReleaseICustButton(but);
		}
	}

void ExtrudeCMode::ExitMode()
	{
	if ( em->hEditMeshParams ) {
		em->EndNormalMove(em->iObjParams->GetTime(),TRUE);
		ICustButton *but = GetICustButton(GetDlgItem(em->hEditMeshParams,IDC_EXTRUDE));
		but->SetCheck(FALSE);
		ReleaseICustButton(but);
		}
	}

void EditMeshMod::StartExtrudeMode()
	{
	if ( !iObjParams ) return;

	iObjParams->SetCommandMode(extrudeMode);
	}


int ExtrudeMouseProc::proc( 
		HWND hwnd, 
		int msg, 
		int point, 
		int flags, 
		IPoint2 m )
	{	
	ViewExp *vpt = ip->GetViewport(hwnd);
	switch ( msg ) {
		case MOUSE_PROPCLICK:
			ip->SetStdCommandMode(CID_OBJMOVE);
			break;

		case MOUSE_POINT:
			if ( point == 0 ) {							
				em->BeginNormalMove(ip->GetTime());				
				om = m;				
			} else {												
				ip->RedrawViews(ip->GetTime(),REDRAW_END);
				em->EndNormalMove(ip->GetTime(),TRUE);
				}
			break;

		case MOUSE_MOVE: {
			Point3 p0 = vpt->MapScreenToView(om,float(-200));
			Point3 p1 = vpt->MapScreenToView(m,float(-200));

			em->NormalMove( ip->GetTime(), Length(p1-p0) );
			em->amountSpin->SetValue(Length(p1-p0),FALSE);

			ip->RedrawViews(ip->GetTime(),REDRAW_INTERACTIVE);
			break;
			}

		case MOUSE_ABORT:
			em->EndNormalMove(ip->GetTime(),FALSE);			
			ip->RedrawViews(ip->GetTime(),REDRAW_END);
			break;		
		}
	
	if ( vpt ) ip->ReleaseViewport(vpt);
	return TRUE;
	}


void EditMeshMod::BeginNormalMove(TimeValue t)
	{
	EndNormalMove(t);
	theHold.SuperBegin();
	DoExtrude();
	theHold.Begin();
	inNormalMove = TRUE;
	}

void EditMeshMod::EndNormalMove(TimeValue t,BOOL accept)
	{	
	if ( !inNormalMove ) return;
	if ( !iObjParams ) return;	
	
	theHold.Accept(IDS_DS_MOVE);
	if (accept) {
		theHold.SuperAccept(IDS_RB_EXTRUDE);
	} else {
		theHold.SuperCancel();
		}
		
	inNormalMove = FALSE;
	if ( amountSpin ) {
		amountSpin->SetValue(0,FALSE);
		}
	}

void EditMeshMod::NormalMove( TimeValue t, float amount ) 
	{	
	ModContextList mcList;		
	INodeTab nodes;

	if ( !iObjParams ) return;
	theHold.Restore();
	iObjParams->GetModContexts(mcList,nodes);

	for ( int i = 0; i < mcList.Count(); i++ ) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if ( !meshData ) continue;
		Mesh *mesh = meshData->TempData(this)->GetMesh(t);
		
		meshData->BeginEdit(t);				
		BitArray sel = mesh->VertexTempSel();
		Point3Tab *normals = meshData->TempData(this)->ClustNormals(t);
		DWORDTab *clust = meshData->TempData(this)->VertCluster(t);

		if ( theHold.Holding() && !meshData->GetFlag(EMD_HELD) ) {
			theHold.Put(new VertexEditRestore(meshData,this));
			meshData->SetFlag(EMD_HELD,TRUE);
			}
		
		// Move the cache vertices
		for ( int j = 0; j < mesh->getNumVerts(); j++ ) {
			if ( sel[j] ) {
				Point3 delta = (*normals)[(*clust)[j]] * amount;
				mesh->setVert(j,mesh->getVert(j)+delta);
				
				// Move the delta's vertices.
				meshData->vdelta.Move(t,j,delta);
				}
			}
		meshData->TempData(this)->Invalidate(PART_GEOM);
		}
	
	nodes.DisposeTemporary();
	NotifyDependents(FOREVER, PART_GEOM, REFMSG_CHANGE);
	}


HCURSOR ExtrudeSelectionProcessor::GetTransformCursor() 
	{ 
	static HCURSOR hCur = NULL;

	if ( !hCur ) {
		hCur = LoadCursor(hInstance,MAKEINTRESOURCE(IDC_EXTRUDECUR)); 
		}

	return hCur; 
	}

/*-------------------------------------------------------------------*/

void WeldVertCMode::EnterMode()
	{
	if (em->hEditMeshParams) {
		ICustButton *but = GetICustButton(GetDlgItem(em->hEditMeshParams,IDC_VERT_WELDTOVERT));
		but->SetCheck(TRUE);
		ReleaseICustButton(but);
		}
	}

void WeldVertCMode::ExitMode()
	{
	if (em->hEditMeshParams) {
		ICustButton *but = GetICustButton(GetDlgItem(em->hEditMeshParams,IDC_VERT_WELDTOVERT));
		but->SetCheck(FALSE);
		ReleaseICustButton(but);
		}
	}

BOOL WeldVertMouseProc::HitTestVerts(
		IPoint2 &m, ViewExp *vpt,int &v)
	{
	vpt->ClearSubObjHitList();	
	ip->SubObHitTest(ip->GetTime(),HITTYPE_POINT,0, 
			HIT_UNSELONLY, &m, vpt);
	if (vpt->NumSubObjHits()) {
		HitLog& hitLog = vpt->GetSubObjHitList();
		HitRecord *hr = hitLog.ClosestHit();
		assert(hr);		
		v = hr->hitInfo;		
		return TRUE;		
		}
	return FALSE;
	}

int WeldVertMouseProc::proc( 
		HWND hwnd, 
		int msg, 
		int point, 
		int flags, 
		IPoint2 m )
	{
	ViewExp *vpt = ip->GetViewport(hwnd);
	switch (msg) {
		case MOUSE_PROPCLICK:
			ip->SetStdCommandMode(CID_OBJMOVE);
			break;

		case MOUSE_POINT:
			if (point==1) {
				int vert;				
				if (HitTestVerts(m,vpt,vert)) {
					// Get the hit record
					HitLog& hitLog = vpt->GetSubObjHitList();
					HitRecord *hr = hitLog.ClosestHit();
					assert(hr);

					// Find the point on the mesh that was hit
					EditMeshData* meshData = (EditMeshData*)hr->modContext->localData;
					Mesh *mesh = meshData->TempData(em)->GetMesh(ip->GetTime());
					Point3 pt = mesh->verts[vert];

					// Select the point that was hit
					em->SelectSubComponent(hr,TRUE,FALSE,FALSE);

					// Do the weld
					em->WeldVerts(0.0f,WELD_TOVERT,pt);
					}
				}
			break;

		case MOUSE_MOVE: {
			int vert;
			if (HitTestVerts(m,vpt,vert)) {
				SetCursor(ip->GetSysCursor(SYSCUR_SELECT));
			} else {
				SetCursor(ip->GetSysCursor(SYSCUR_MOVE));
				}
			break;
			}
		}
	
	if (vpt) ip->ReleaseViewport(vpt);
	return MoveModBox::proc(hwnd,msg,point,flags,m);	
	}

HCURSOR WeldVertSelectionProcessor::GetTransformCursor() 
	{ 	
	return ip->GetSysCursor(SYSCUR_MOVE); 
	}

//----------------------------------------------------------

void CreateVertCMode::EnterMode()
	{
	if (em->hEditMeshParams) {
		ICustButton *but = GetICustButton(GetDlgItem(em->hEditMeshParams,IDC_VERT_CREATE));
		but->SetCheck(TRUE);
		ReleaseICustButton(but);
		}
	}

void CreateVertCMode::ExitMode()
	{
	if ( em->hEditMeshParams ) {
		em->EndNormalMove(em->iObjParams->GetTime(),TRUE);
		ICustButton *but = GetICustButton(GetDlgItem(em->hEditMeshParams,IDC_VERT_CREATE));
		but->SetCheck(FALSE);
		ReleaseICustButton(but);
		}
	}


int CreateVertMouseProc::proc(
		HWND hwnd, 
		int msg, 
		int point, 
		int flags, 
		IPoint2 m )
	{
	static HCURSOR hCur = NULL;
	if (!hCur) {
		hCur = LoadCursor(hInstance,MAKEINTRESOURCE(IDC_ADDVERTCUR)); 
		}

	ViewExp *vpt = ip->GetViewport(hwnd);

	switch (msg) {
		case MOUSE_PROPCLICK:
			ip->SetStdCommandMode(CID_OBJMOVE);
			break;

		case MOUSE_POINT: {						
			em->iObjParams->SetActiveViewport(hwnd);
			Matrix3 ctm;
			vpt->GetConstructionTM(ctm);
			Point3 pt = vpt->SnapPoint(m,m,&ctm);
			pt = pt * ctm;
			//Point3 pt = vpt->GetPointOnCP(m);
			em->AddNewVertex(pt);
			break;
			}
		
		case MOUSE_FREEMOVE:
			SetCursor(hCur);
#ifdef _OSNAP //snap preview
			vpt->SnapPreview(m,m,NULL, SNAP_FORCE_3D_RESULT);
#endif
			break;
		}

	if (vpt) ip->ReleaseViewport(vpt);
	return TRUE;
	}


//----------------------------------------------------------

void BuildFaceCMode::EnterMode()
	{
	if (em->hEditMeshParams) {
		ICustButton *but = GetICustButton(GetDlgItem(em->hEditMeshParams,IDC_BUILDFACE));
		but->SetCheck(TRUE);
		ReleaseICustButton(but);
		em->inBuildFace = TRUE;
		em->NotifyDependents(
			FOREVER, PART_DISPLAY, REFMSG_CHANGE);
		em->iObjParams->RedrawViews(em->iObjParams->GetTime());
		}
	}

void BuildFaceCMode::ExitMode()
	{
	if ( em->hEditMeshParams ) {		
		ICustButton *but = GetICustButton(GetDlgItem(em->hEditMeshParams,IDC_BUILDFACE));
		but->SetCheck(FALSE);
		ReleaseICustButton(but);
		em->inBuildFace = FALSE;
		em->NotifyDependents(
			FOREVER, PART_DISPLAY, REFMSG_CHANGE);
		em->iObjParams->RedrawViews(em->iObjParams->GetTime());
		}
	}

BuildFaceMouseProc::BuildFaceMouseProc(EditMeshMod* mod, IObjParam *i)
	{
	em = mod;
	ip = i;
	meshData = NULL;
	pt = 0;
	}


BOOL BuildFaceMouseProc::HitTestVerts(IPoint2 &m, ViewExp *vpt,int &v)
	{
	vpt->ClearSubObjHitList();
	ip->SubObHitTest(ip->GetTime(),HITTYPE_POINT,0, 
			0, &m, vpt);
	if (vpt->NumSubObjHits()) {
		HitLog& hitLog = vpt->GetSubObjHitList();
		HitRecord *hr = hitLog.ClosestHit();
		assert(hr);
		if (meshData == hr->modContext->localData || meshData == NULL) {			
			for (int i=0; i<pt; i++) {
				if (vts[i]==(int)hr->hitInfo) return FALSE;
				}
			v = hr->hitInfo;
			return TRUE;
			}
		}
	return FALSE;
	}
		
void BuildFaceMouseProc::DrawFace(HWND hWnd,IPoint2 &m)
	{
	HDC hdc = GetDC(hWnd);	
	SetROP2(hdc, R2_XORPEN);
	SetBkMode(hdc, TRANSPARENT);	
	SelectObject(hdc,CreatePen(PS_DOT, 0, RGB(255,255,255)));
	if (pt>0) {
		MoveToEx(hdc,mpts[0].x,mpts[0].y,NULL);
		LineTo(hdc,m.x,m.y);
		}
	if (pt==2) {
		MoveToEx(hdc,mpts[0].x,mpts[0].y,NULL);
		LineTo(hdc,mpts[1].x,mpts[1].y);
		LineTo(hdc,m.x,m.y);
		}
	DeleteObject(SelectObject(hdc,GetStockObject(BLACK_PEN)));
	ReleaseDC(hWnd, hdc);
	}

int BuildFaceMouseProc::proc(
		HWND hwnd, 
		int msg, 
		int point, 
		int flags, 
		IPoint2 m )
	{	
	ViewExp *vpt = ip->GetViewport(hwnd);

	switch (msg) {
		case MOUSE_PROPCLICK:
			ip->SetStdCommandMode(CID_OBJMOVE);
			break;

		case MOUSE_POINT: {
			ip->SetActiveViewport(hwnd);
			
			if (HitTestVerts(m,vpt,vts[pt])) {
				HitLog& hitLog = vpt->GetSubObjHitList();
				HitRecord *hr = hitLog.ClosestHit();
				assert(hr);
				
				if (!meshData) {										
					// The first point
					meshData = (EditMeshData*)hr->modContext->localData;
					lm = m;
					}
				
				// Transform the vertex into screen space and hang on to it
				Mesh *mesh = meshData->TempData(em)->GetMesh(ip->GetTime());
				GraphicsWindow *gw = vpt->getGW();
				Matrix3 mat = hr->nodeRef->GetObjectTM(ip->GetTime());
				gw->setTransform(mat);	
				gw->wTransPoint(&mesh->verts[vts[pt]],&mpts[pt]);
				
				// Erase the face in the last position
				DrawFace(hwnd,lm);
				
				// Note that we got another point
				pt++;

				// Draw the face in the new position
				DrawFace(hwnd,m);
				lm = m;

				// We got 3 points, build a face
				if (pt==3) {					
					if (mesh->DoesFaceExist(vts[0],vts[1],vts[2])) {
						InvalidateRect(vpt->getGW()->getHWnd(),NULL,FALSE);
						TSTR buf1 = GetString(IDS_RB_DUPFACEWARNING);
						TSTR buf2 = GetString(IDS_RB_EDITMESHTITLE);
						MessageBox(hwnd,buf1,buf2,MB_OK|MB_ICONINFORMATION);						
					} else {
						em->BuildNewFace(meshData,vts);
						}
					pt = 0;
					meshData = NULL;
					return FALSE;
					}
			} else {
				if (!pt) {
					// We haven't started a face yet so abort.
					return FALSE;
					}
				}
			break;
			}
		
		case MOUSE_MOVE:
			DrawFace(hwnd,lm);
			DrawFace(hwnd,m);
			lm = m;
			// Fall through

		case MOUSE_FREEMOVE: {
			int vert;
			if (HitTestVerts(m,vpt,vert)) {
				SetCursor(ip->GetSysCursor(SYSCUR_SELECT));
			} else {
				SetCursor(LoadCursor(NULL,IDC_ARROW));
				}			
			break;
			}

		case MOUSE_ABORT:
			DrawFace(hwnd,lm);
			pt = 0;
			meshData = NULL;
			break;
		}

	if (vpt) ip->ReleaseViewport(vpt);
	return TRUE;
	}


void EditMeshMod::BuildNewFace(EditMeshData *meshData,int *v)
	{
	if (!iObjParams) return;

	theHold.Begin();
	TopoEditRestore *rest = new TopoEditRestore(meshData,this);		
	theHold.Put(rest);

	meshData->BeginEdit(iObjParams->GetTime());
	Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());

	Face *faces = new Face[mesh->getNumFaces()+1];
	for (int i=0; i<mesh->getNumFaces(); i++) {
		faces[i] = mesh->faces[i];
		}
	Face face;
	face.setEdgeVisFlags(1,1,1); 
	face.setVerts(v[0],v[1],v[2]);
	face.setSmGroup(0);
	faces[i] = face;
	meshData->tdelta.nfaces.Append(1,&face);
	delete[] mesh->faces;
	mesh->faces = faces;
	
	if (mesh->tvFace) {
		// Add a new tvFace and three new tverts.
		// We'll init the verts with a box mapping.
		
		TVFace *tvfaces = new TVFace[mesh->getNumFaces()+1];
		for (int i=0; i<mesh->getNumFaces(); i++) {
			tvfaces[i] = mesh->tvFace[i];
			}		
		TVFace tvface(mesh->numTVerts,mesh->numTVerts+1,mesh->numTVerts+2);
		meshData->tdelta.nTVfaces.Append(1,&tvface);
		tvfaces[i] = tvface;
		delete[] mesh->tvFace;
		mesh->tvFace = tvfaces;

		Point3 *tVerts = new Point3[mesh->numTVerts+3];
		for (i=0; i<mesh->numTVerts; i++) {
			tVerts[i] = mesh->tVerts[i];
			}
		delete[] mesh->tVerts;
		mesh->tVerts = tVerts;
		mesh->numTVerts += 3;

		Point3 v0 = mesh->verts[v[0]];
		Point3 v1 = mesh->verts[v[1]];
		Point3 v2 = mesh->verts[v[2]];
		Box3 box; box.Init();
		box += v0; box += v1; box += v2;
		Point3 norm = Normalize((v1-v0)^(v2-v1));
		int m=0;
		if (fabs(norm[1])>fabs(norm[m])) m = 1;
		if (fabs(norm[2])>fabs(norm[m])) m = 2;
		int i1 = (m+1)%3;
		int i2 = (m+2)%3;
		mesh->tVerts[mesh->numTVerts-3] = 
			Point3(
				v0[i1]/box.Width()[i1],
				v0[i2]/box.Width()[i2],
				v0[m]/box.Width()[m]);
		mesh->tVerts[mesh->numTVerts-2] = 
			Point3(
				v1[i1]/box.Width()[i1],
				v1[i2]/box.Width()[i2],
				v1[m]/box.Width()[m]);
		mesh->tVerts[mesh->numTVerts-1] = 
			Point3(
				v2[i1]/box.Width()[i1],
				v2[i2]/box.Width()[i2],
				v2[m]/box.Width()[m]);
		
		meshData->tdelta.nTverts.Append(3,&mesh->tVerts[mesh->numTVerts-3]);
		}
	
	mesh->numFaces++;

	mesh->InvalidateGeomCache();
	mesh->faceSel.SetSize(mesh->getNumFaces(),TRUE);
	mesh->edgeSel.SetSize(mesh->getNumFaces()*3,TRUE);
	meshData->Invalidate(PART_GEOM|PART_TOPO);
	meshData->fsel.SetSize(mesh->getNumFaces(),TRUE);
	meshData->esel.SetSize(mesh->getNumFaces()*3,TRUE);

	theHold.Accept(GetString(IDS_RB_BUILDFACE));
	NotifyDependents(FOREVER, PART_TOPO|PART_GEOM, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}

//----------------------------------------------------------


void EditMeshMod::AddNewVertex(Point3 pt)
	{
	ModContextList mcList;	
	INodeTab nodes;
	Point3 zero(0,0,0);

	if (!iObjParams) return;
	iObjParams->GetModContexts(mcList,nodes);	
	theHold.Begin();
	EditMeshData *meshData = (EditMeshData*)mcList[0]->localData;
	if (!meshData) {
		nodes.DisposeTemporary();
		return;
		}
	meshData->BeginEdit(iObjParams->GetTime());		
	TopoEditRestore *rest = new TopoEditRestore(meshData,this);		
	theHold.Put(rest);
	
	pt = pt * Inverse(nodes[0]->GetObjectTM(iObjParams->GetTime()));

	Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());
	Point3 *verts = new Point3[mesh->getNumVerts()+1];
	for (int i=0; i<mesh->getNumVerts(); i++) {
		verts[i] = mesh->verts[i];
		}
	verts[i] = pt;
	delete mesh->verts;
	mesh->verts = verts;
	mesh->numVerts++;
	mesh->vertSel.SetSize(mesh->numVerts,TRUE);
	mesh->vertHide.SetSize(mesh->numVerts,TRUE);
	mesh->InvalidateGeomCache();
	
	meshData->tdelta.nverts.Append(1,&pt);
	meshData->vdelta.deltas.Append(1,&zero);
	meshData->vdelta.hide.SetSize(mesh->numVerts,TRUE);
	meshData->vsel.SetSize(mesh->numVerts,TRUE);
	meshData->Invalidate(PART_GEOM|PART_TOPO);

	theHold.Accept(GetString(IDS_RB_ADDVERTS));
	nodes.DisposeTemporary();
	NotifyDependents(FOREVER, PART_TOPO|PART_GEOM, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}


void EditMeshMod::DetachVertFaces(TSTR &name,BOOL doFaces,BOOL del)
	{
	ModContextList mcList;	
	INodeTab nodes;	

	if (!iObjParams) return;

	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);
	
	theHold.Begin();
	SuspendAnimate();
	AnimateOff();

	for (int i = 0; i < mcList.Count(); i++) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if ( !meshData ) continue;
		if ( meshData->GetFlag(EMD_BEENDONE) ) continue;		
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());
				
		// If this is the first edit, then the delta arrays will be allocated
		meshData->BeginEdit(iObjParams->GetTime());
		
		if (del) {
			// Start a restore object...
			TopoEditRestore *rest = new TopoEditRestore(meshData,this);		
			theHold.Put(rest);
			}

		// Make a new tri object
		TriObject *newObj = CreateNewTriObject();
		Mesh *newMesh = &newObj->mesh;

		// If we're detaching by selected vertices flag faces,
		// otherwise flag vertices
		BitArray vertf;
		BitArray facef;
		vertf.SetSize(mesh->getNumVerts());
		facef.SetSize(mesh->getNumFaces());
				
		if (doFaces) {
			facef = mesh->faceSel;
			for (int j=0; j<mesh->getNumFaces(); j++) {
				if (facef[j]) {
					vertf.Set(mesh->faces[j].v[0]);
					vertf.Set(mesh->faces[j].v[1]);
					vertf.Set(mesh->faces[j].v[2]);					
					}
				}
		} else {
			vertf = mesh->vertSel;
			for (int j=0; j<mesh->getNumFaces(); j++) {
				for (int k=0; k<3; k++) {
					if (vertf[mesh->faces[j].v[k]]) {
						facef.Set(j);
						break;
						}
					}				
				}
			
			// Make sure all verts are copied that flagged
			// faces reference.
			for (j=0; j<mesh->getNumFaces(); j++) {
				if (facef[j]) {
					vertf.Set(mesh->faces[j].v[0]);
					vertf.Set(mesh->faces[j].v[1]);
					vertf.Set(mesh->faces[j].v[2]);					
					}
				}
			}

		int numVerts =0, numFaces = 0;
		numVerts = vertf.NumberSet();
		numFaces = facef.NumberSet();
		newMesh->setNumVerts(numVerts);
		newMesh->setNumFaces(numFaces);

		// Copy in verts. Keep track of were the verts go in the new mesh.
		DWORDTab vmap;
		vmap.SetCount(mesh->getNumVerts());
		int ct=0;
		for (int j=0; j<mesh->getNumVerts(); j++) {			
			if (vertf[j]) {
				vmap[j] = ct;
				newMesh->verts[ct] = mesh->verts[j];
				ct++;
			} else {
				vmap[j] = UNDEFINED;
				}
			}
		
		// Now copy in the faces, remapping to the new vert array.
		ct = 0;
		for (j=0; j<mesh->getNumFaces(); j++) {
			if (facef[j]) {
				newMesh->faces[ct] = mesh->faces[j];
				assert(vmap[newMesh->faces[ct].v[0]]!=UNDEFINED);
				assert(vmap[newMesh->faces[ct].v[1]]!=UNDEFINED);
				assert(vmap[newMesh->faces[ct].v[2]]!=UNDEFINED);
				newMesh->faces[ct].v[0] = vmap[newMesh->faces[ct].v[0]];
				newMesh->faces[ct].v[1] = vmap[newMesh->faces[ct].v[1]];
				newMesh->faces[ct].v[2] = vmap[newMesh->faces[ct].v[2]];
				ct++;
				}
			}
		
		// Do texture coords too.
		if (mesh->tvFace) {
			// Flag UV verts that are referenced by selected faces
			BitArray tvertf;
			tvertf.SetSize(mesh->numTVerts);
			int numTVerts=0;
			for (int j=0; j<mesh->getNumFaces(); j++) {
				if (facef[j]) {
					tvertf.Set(mesh->tvFace[j].t[0]);
					tvertf.Set(mesh->tvFace[j].t[1]);
					tvertf.Set(mesh->tvFace[j].t[2]);					
					}
				}
			numTVerts = tvertf.NumberSet();
			newMesh->setNumTVerts(numTVerts);
			newMesh->setNumTVFaces(numFaces);
				
			// Copy in tverts. Keep track of were the tverts go in the new mesh.
			DWORDTab tvmap;
			tvmap.SetCount(mesh->numTVerts);
			int ct=0;
			for (j=0; j<mesh->numTVerts; j++) {			
				if (tvertf[j]) {
					tvmap[j] = ct;
					newMesh->tVerts[ct] = mesh->tVerts[j];
					ct++;
				} else {
					tvmap[j] = UNDEFINED;
					}
				}
		
			// Now copy in the TV faces, remapping to the new tvert array.
			ct = 0;
			for (j=0; j<mesh->getNumFaces(); j++) {
				if (facef[j]) {
					newMesh->tvFace[ct] = mesh->tvFace[j];
					assert(tvmap[newMesh->tvFace[ct].t[0]]!=UNDEFINED);
					assert(tvmap[newMesh->tvFace[ct].t[1]]!=UNDEFINED);
					assert(tvmap[newMesh->tvFace[ct].t[2]]!=UNDEFINED);
					newMesh->tvFace[ct].t[0] = tvmap[newMesh->tvFace[ct].t[0]];
					newMesh->tvFace[ct].t[1] = tvmap[newMesh->tvFace[ct].t[1]];
					newMesh->tvFace[ct].t[2] = tvmap[newMesh->tvFace[ct].t[2]];
					ct++;
					}
				}
			}
		newMesh->InvalidateGeomCache();

		// Add the object to the scene. Give it the given name
		// and set its transform to ours.
		// Also, give it our material.
		INode *node = iObjParams->CreateObjectNode(newObj);
		Matrix3 ntm = nodes[i]->GetNodeTM(iObjParams->GetTime());
		TSTR uname = name;
		iObjParams->MakeNameUnique(uname);
		node->SetName(uname);
		node->SetNodeTM(iObjParams->GetTime(),ntm);
		node->FlagForeground(iObjParams->GetTime(),FALSE);
		node->SetMtl(nodes[i]->GetMtl());
		node->SetObjOffsetPos(   nodes[i]->GetObjOffsetPos());
		node->SetObjOffsetRot(	 nodes[i]->GetObjOffsetRot());
		node->SetObjOffsetScale( nodes[i]->GetObjOffsetScale());


		if (del) {
			// Delete the faces that were detached.
			meshData->DeleteFaceSet(mesh,facef);
		
			// Now unflag verts that were referenced by a selected face
			// if there is a face left that references them after selected
			// faces have been deleted		
			for (j=0; j<mesh->getNumFaces(); j++) {			
				vertf.Clear(mesh->faces[j].v[0]);
				vertf.Clear(mesh->faces[j].v[1]);
				vertf.Clear(mesh->faces[j].v[2]);			
				}

			// Delete vertices that were only referenced by selected faces
			// which are now delted.
			meshData->DeleteVertSet(mesh,vertf);

			meshData->SetFlag(EMD_BEENDONE,TRUE);
			meshData->Invalidate(PART_GEOM|PART_TOPO);
			}
		}
	
	ResumeAnimate();

	nodes.DisposeTemporary();
	if (del) {
		theHold.Accept(GetString(IDS_RB_DELETEVERT));
		NotifyDependents(FOREVER, PART_TOPO|PART_GEOM, REFMSG_CHANGE);
		}
	iObjParams->RedrawViews(iObjParams->GetTime());
	}

//----------------------------------------------------------


HitRecord *PickEdgeMouseProc::HitTestEdges(IPoint2 &m, ViewExp *vpt)
	{
	vpt->ClearSubObjHitList();
	ip->SubObHitTest(ip->GetTime(),HITTYPE_POINT,0, 0, &m, vpt);
	if (vpt->NumSubObjHits()) {
		HitLog& hitLog = vpt->GetSubObjHitList();
		return hitLog.ClosestHit();
	} else {
		return NULL;
		}
	}


int PickEdgeMouseProc::proc( 
		HWND hwnd, 
		int msg, 
		int point, 
		int flags, 
		IPoint2 m )
	{
	ViewExp *vpt = ip->GetViewport(hwnd);

	switch (msg) {
		case MOUSE_PROPCLICK:
			ip->SetStdCommandMode(CID_OBJMOVE);
			break;

		case MOUSE_POINT: {
			ip->SetActiveViewport(hwnd);
			HitRecord *hr = HitTestEdges(m,vpt);
			if (hr) {
				EditMeshData *meshData = (EditMeshData*)hr->modContext->localData;
				EdgePick(meshData,hr->hitInfo);
				}
			break;
			}
		
		case MOUSE_MOVE:
		case MOUSE_FREEMOVE:			
			if (HitTestEdges(m,vpt)) {
				SetCursor(ip->GetSysCursor(SYSCUR_SELECT));
			} else {
				SetCursor(LoadCursor(NULL,IDC_ARROW));
				}			
			break;			
		}

	if (vpt) ip->ReleaseViewport(vpt);
	return TRUE;	
	}


void DivideEdgeProc::EdgePick(EditMeshData *meshData,DWORD edge)
	{
	meshData->BeginEdit(ip->GetTime());

	BitArray sel;
	sel.SetSize(meshData->esel.GetSize());
	sel.Set(edge);
		
	theHold.Begin();
	TopoEditRestore *rest = new TopoEditRestore(meshData,em);
	theHold.Put(rest);
	
	Mesh *mesh = meshData->TempData(em)->GetMesh(ip->GetTime());
	
	meshData->DivideEdges(mesh, 
		meshData->TempData(em)->AdjList(ip->GetTime()),
		sel);
	meshData->SetFlag(EMD_BEENDONE,TRUE);
	meshData->Invalidate(PART_GEOM|PART_TOPO);
	
	theHold.Accept(GetString(IDS_RB_EDGEDIVIDE));

	em->NotifyDependents(FOREVER, PART_TOPO, REFMSG_CHANGE);
	ip->RedrawViews(ip->GetTime());	
	}

void TurnEdgeProc::EdgePick(EditMeshData *meshData,DWORD edge)
	{
	meshData->BeginEdit(ip->GetTime());

	BitArray sel;
	sel.SetSize(meshData->esel.GetSize());
	sel.Set(edge);
		
	theHold.Begin();
	TopoEditRestore *rest = new TopoEditRestore(meshData,em);
	theHold.Put(rest);
	
	Mesh *mesh = meshData->TempData(em)->GetMesh(ip->GetTime());
	
	meshData->TurnEdges(mesh, 
		meshData->TempData(em)->AdjList(ip->GetTime()),
		sel);
	meshData->SetFlag(EMD_BEENDONE,TRUE);
	meshData->Invalidate(PART_GEOM|PART_TOPO);
	
	theHold.Accept(GetString(IDS_RB_EDGEDIVIDE));

	em->NotifyDependents(FOREVER, PART_TOPO, REFMSG_CHANGE);
	ip->RedrawViews(ip->GetTime());	
	}


void DivideEdgeCMode::EnterMode()
	{
	if (em->hEditMeshParams) {
		ICustButton *but = GetICustButton(GetDlgItem(em->hEditMeshParams,IDC_EDGE_DIVIDE));
		but->SetCheck(TRUE);
		ReleaseICustButton(but);		
		}
	}

void DivideEdgeCMode::ExitMode()
	{
	if (em->hEditMeshParams) {
		ICustButton *but = GetICustButton(GetDlgItem(em->hEditMeshParams,IDC_EDGE_DIVIDE));
		but->SetCheck(FALSE);
		ReleaseICustButton(but);		
		}
	}

void TurnEdgeCMode::EnterMode()
	{
	if (em->hEditMeshParams) {
		ICustButton *but = GetICustButton(GetDlgItem(em->hEditMeshParams,IDC_EDGE_TURN));
		but->SetCheck(TRUE);
		ReleaseICustButton(but);		
		}
	}

void TurnEdgeCMode::ExitMode()
	{
	if (em->hEditMeshParams) {
		ICustButton *but = GetICustButton(GetDlgItem(em->hEditMeshParams,IDC_EDGE_TURN));
		but->SetCheck(FALSE);
		ReleaseICustButton(but);		
		}
	}




//----------------------------------------------------------

BOOL AttachPickMode::Filter(INode *node)
	{
	if (node) {
		// Make sure the node does not depend on this modifier.
		node->BeginDependencyTest();
		em->NotifyDependents(FOREVER,0,REFMSG_TEST_DEPENDENCY);
		if (node->EndDependencyTest()) return FALSE;

		ObjectState os =
			node->GetObjectRef()->Eval(ip->GetTime());
		if (os.obj->IsSubClassOf(triObjectClassID) ||
			os.obj->CanConvertToType(triObjectClassID)) {
			return TRUE;
			}
		}
	return FALSE;
	}

BOOL AttachPickMode::HitTest(
		IObjParam *ip,HWND hWnd,ViewExp *vpt,IPoint2 m,int flags)
	{
	INode *node = ip->PickNode(hWnd,m);
	if (node) {
		// Make sure the node does not depend on this modifier.
		node->BeginDependencyTest();
		em->NotifyDependents(FOREVER,0,REFMSG_TEST_DEPENDENCY);
		if (node->EndDependencyTest()) return FALSE;

		ObjectState os =
			node->GetObjectRef()->Eval(ip->GetTime());
		if (os.obj->IsSubClassOf(triObjectClassID) ||
			os.obj->CanConvertToType(triObjectClassID)) {
			return TRUE;
			}
		}
	return FALSE;
	}

BOOL AttachPickMode::Pick(IObjParam *ip,ViewExp *vpt)
	{
	INode *node = vpt->GetClosestHit();
	if (node) {
		// Make sure the node does not depend on this modifier.
		node->BeginDependencyTest();
		em->NotifyDependents(FOREVER,0,REFMSG_TEST_DEPENDENCY);
		if (node->EndDependencyTest()) return FALSE;

		ObjectState os =
			node->GetObjectRef()->Eval(ip->GetTime());
		if (os.obj->IsSubClassOf(triObjectClassID) ||
			os.obj->CanConvertToType(triObjectClassID)) {
			
			BOOL del = FALSE;
			if (!os.obj->IsSubClassOf(triObjectClassID)) {
				// Convert to tri object
				TriObject *tobj = (TriObject*)os.obj->
					ConvertToType(ip->GetTime(),triObjectClassID);
				assert(tobj);
				if (tobj!=os.obj) del = TRUE;
				os.obj = tobj;
				}

			theHold.Begin();
			em->AttachObject((TriObject*)os.obj,node);					
			if (del) os.obj->DeleteThis();
			ip->DeleteNode(node);
			theHold.Accept(GetString(IDS_RB_ATTACHOBJECT));
			ip->RedrawViews(ip->GetTime());
			}
		}
	return FALSE;
	}

void AttachPickMode::EnterMode(IObjParam *ip)
	{
	if (em->hEditMeshParams) {
		ICustButton *but = GetICustButton(GetDlgItem(em->hEditMeshParams,IDC_OBJ_ATTACH));
		but->SetCheck(TRUE);
		ReleaseICustButton(but);
		}
	}

void AttachPickMode::ExitMode(IObjParam *ip)
	{
	if ( em->hEditMeshParams ) {
		em->EndNormalMove(em->iObjParams->GetTime(),TRUE);
		ICustButton *but = GetICustButton(GetDlgItem(em->hEditMeshParams,IDC_OBJ_ATTACH));
		but->SetCheck(FALSE);
		ReleaseICustButton(but);
		}
	}

void ObjAttachCMode::EnterMode()
	{
	if (em->hEditMeshParams) {
		ICustButton *but = GetICustButton(GetDlgItem(em->hEditMeshParams,IDC_OBJ_ATTACH));
		but->SetCheck(TRUE);
		ReleaseICustButton(but);
		}
	}

void ObjAttachCMode::ExitMode()
	{
	if ( em->hEditMeshParams ) {
		em->EndNormalMove(em->iObjParams->GetTime(),TRUE);
		ICustButton *but = GetICustButton(GetDlgItem(em->hEditMeshParams,IDC_OBJ_ATTACH));
		but->SetCheck(FALSE);
		ReleaseICustButton(but);
		}
	}


int ObjAttachMouseProc::proc(
		HWND hwnd, 
		int msg, 
		int point, 
		int flags, 
		IPoint2 m )
	{
	
	switch (msg) {
		case MOUSE_POINT: {						
			ip->SetActiveViewport(hwnd);			
			INode *node = ip->PickNode(hwnd,m);
			if (node) {
				// Make sure the node does not depend on this modifier.
				node->BeginDependencyTest();
				em->NotifyDependents(FOREVER,0,REFMSG_TEST_DEPENDENCY);
				if (node->EndDependencyTest()) break;;

				ObjectState os =
					node->GetObjectRef()->Eval(ip->GetTime());
				if (os.obj->IsSubClassOf(triObjectClassID) ||
					os.obj->CanConvertToType(triObjectClassID)) {
					
					BOOL del = FALSE;
					if (!os.obj->IsSubClassOf(triObjectClassID)) {
						// Convert to tri object
						TriObject *tobj = (TriObject*)os.obj->
							ConvertToType(ip->GetTime(),triObjectClassID);
						assert(tobj);
						if (tobj!=os.obj) del = TRUE;
						os.obj = tobj;
						}

					theHold.Begin();
					em->AttachObject((TriObject*)os.obj,node);					
					if (del) os.obj->DeleteThis();
					ip->DeleteNode(node);
					theHold.Accept(GetString(IDS_RB_ATTACHOBJECT));
					ip->RedrawViews(ip->GetTime());
					}
				}
			break;
			}
		
		case MOUSE_FREEMOVE:
			INode *node = ip->PickNode(hwnd,m);
			if (node) {
				// Make sure the node does not depend on this modifier.
				node->BeginDependencyTest();
				em->NotifyDependents(FOREVER,0,REFMSG_TEST_DEPENDENCY);
				if (node->EndDependencyTest()) break;;

				ObjectState os =
					node->GetObjectRef()->Eval(ip->GetTime());
				if (os.obj->IsSubClassOf(triObjectClassID) ||
					os.obj->CanConvertToType(triObjectClassID)) {
					SetCursor(ip->GetSysCursor(SYSCUR_SELECT));
					break;
					}
				}
			SetCursor(LoadCursor(NULL,IDC_ARROW));
			break;
		}
	
	return TRUE;
	}

void EditMeshMod::AttachObject(TriObject *obj,INode *node)
	{
	ModContextList mcList;	
	INodeTab nodes;	
	Point3 zero(0,0,0);

	if (!iObjParams) return;
	iObjParams->GetModContexts(mcList,nodes);	
	
	// Combine the materials of the two nodes.
	int c1=0, c2=0;	
	if (nodes[0]->GetMtl() && node->GetMtl()) {
		if (nodes[0]->GetMtl() != node->GetMtl()) {
			Mtl *multi = CombineMaterials(
				nodes[0]->GetMtl(), node->GetMtl(), c1, c2);
			nodes[0]->SetMtl(multi);
			
			theHold.Accept(GetString(IDS_RB_ATTACHOBJECT));
			GetSystemSetting(SYSSET_CLEAR_UNDO);
			theHold.Begin();
			}
	} else if (node->GetMtl()) {
		nodes[0]->SetMtl(node->GetMtl());
		
		theHold.Accept(GetString(IDS_RB_ATTACHOBJECT));
		GetSystemSetting(SYSSET_CLEAR_UNDO);
		theHold.Begin();
		}

	EditMeshData *meshData = (EditMeshData*)mcList[0]->localData;
	if (!meshData) {
		nodes.DisposeTemporary();
		return;
		}
	
	meshData->BeginEdit(iObjParams->GetTime());		
	TopoEditRestore *rest = new TopoEditRestore(meshData,this);		
	theHold.Put(rest);
	
	// Grab the two meshes
	Mesh *toMesh   = meshData->TempData(this)->GetMesh(iObjParams->GetTime());
	Mesh *fromMesh = &obj->mesh;
	
	// Construct a transformation that takes a vertex out of the space of
	// the source node and puts it into the space of the destination node.
	Matrix3 tm = node->GetObjectTM(iObjParams->GetTime()) *
		Inverse(nodes[0]->GetObjectTM(iObjParams->GetTime()));
	
	AffineParts parts;
	decomp_affine(tm,&parts);
	BOOL parity = parts.f<0.0f;

	// Copy over the verts
	int nv = toMesh->getNumVerts() + fromMesh->getNumVerts();
	int oldnv  = toMesh->getNumVerts();
	int oldntv = toMesh->numTVerts;
	
	Point3 *verts = new Point3[nv];
	for (int i=0; i<toMesh->getNumVerts();i++) {
		verts[i] = toMesh->verts[i];
		}
	for (;i<nv; i++) {
		verts[i] = tm * fromMesh->verts[i-toMesh->getNumVerts()];		
		meshData->tdelta.nverts.Append(1,&verts[i],10);
		meshData->vdelta.deltas.Append(1,&zero),10;
		}			
	delete toMesh->verts;
	toMesh->verts    = verts;
	toMesh->numVerts = nv;
	toMesh->vertSel.SetSize(nv,TRUE);	

	// Copy texture verts if we got 'em
	if (fromMesh->tvFace) {
		int ntv = toMesh->numTVerts + fromMesh->numTVerts;
		Point3 *tVerts = new Point3[ntv];
		for (int i=0; i<toMesh->numTVerts;i++) {
			tVerts[i] = toMesh->tVerts[i];
			}
		for (;i<ntv; i++) {
			tVerts[i] = fromMesh->tVerts[i-toMesh->numTVerts];		
			meshData->tdelta.nTverts.Append(1,&tVerts[i],10);			
			}			
		delete toMesh->tVerts;
		toMesh->tVerts    = tVerts;
		toMesh->numTVerts = ntv;
		}

	// Copy over faces
	int nf = toMesh->getNumFaces() + fromMesh->getNumFaces();
	Face *faces = new Face[nf];
	for (i=0; i<toMesh->getNumFaces(); i++) {
		faces[i] = toMesh->faces[i];
		}
	for (;i<nf; i++) {
		faces[i] = fromMesh->faces[i-toMesh->getNumFaces()];
		
		if (parity) {
			// TM had mirror scaling, flip faces
			DWORD vis  = 0;
			if (faces[i].flags&EDGE_A) vis |= EDGE_A;
			if (faces[i].flags&EDGE_B) vis |= EDGE_C;
			if (faces[i].flags&EDGE_C) vis |= EDGE_B;
			DWORD temp = faces[i].v[0];
			faces[i].v[0] = faces[i].v[1];
			faces[i].v[1] = temp;				
			faces[i].flags &= ~EDGE_ALL;
			faces[i].flags |= vis;	
			}

		// Adjust face indices.
		faces[i].v[0] += oldnv;
		faces[i].v[1] += oldnv;
		faces[i].v[2] += oldnv;
		
		// Adjust mat indices
		faces[i].setMatID(faces[i].getMatID()+c1);
		
		meshData->tdelta.nfaces.Append(1,&faces[i],10);
		}
	delete toMesh->faces;
	toMesh->faces = faces;	
	
	// Copy TV faces if we got 'em
	if (fromMesh->tvFace || toMesh->tvFace) {
		int ntvf = toMesh->getNumFaces() + fromMesh->getNumFaces();
		TVFace *tvfaces = new TVFace[ntvf];
		for (i=0; i<toMesh->getNumFaces(); i++) {
			if (toMesh->tvFace) tvfaces[i] = toMesh->tvFace[i];
			else tvfaces[i] = TVFace(0,0,0);
			}
		for (;i<ntvf; i++) {
			if (fromMesh->tvFace) {
				tvfaces[i] = fromMesh->tvFace[i-toMesh->getNumFaces()];

				if (parity) {
					// TM had mirror scaling, flip faces
					DWORD temp = tvfaces[i].t[0];
					tvfaces[i].t[0] = tvfaces[i].t[1];
					tvfaces[i].t[1] = temp;		
					}

				// Adjust face indices.
				tvfaces[i].t[0] += oldntv;
				tvfaces[i].t[1] += oldntv;
				tvfaces[i].t[2] += oldntv;
			} else {
				tvfaces[i] = TVFace(0,0,0);
				}
			meshData->tdelta.nTVfaces.Append(1,&tvfaces[i],10);
			}
		delete toMesh->tvFace;
		toMesh->tvFace = tvfaces;
		}

	toMesh->numFaces = nf;
	toMesh->faceSel.SetSize(nf,TRUE);
	toMesh->edgeSel.SetSize(nf*3,TRUE);
	toMesh->InvalidateGeomCache();	
	toMesh->InvalidateEdgeList();

	// Resize stuff
	meshData->tdelta.nverts.Shrink();
	meshData->vdelta.deltas.Shrink();
	meshData->tdelta.nfaces.Shrink();
	meshData->vdelta.hide.SetSize(toMesh->numVerts,TRUE);
	meshData->vsel.SetSize(toMesh->numVerts,TRUE);
	meshData->fsel.SetSize(toMesh->numFaces,TRUE);
	meshData->esel.SetSize(toMesh->numFaces*3,TRUE);	
	meshData->Invalidate(PART_GEOM|PART_TOPO);
	
	nodes.DisposeTemporary();
	NotifyDependents(FOREVER, PART_TOPO|PART_GEOM, REFMSG_CHANGE);	
	}


//----------------------------------------------------------

static void SetSingleEdgeVis(DWORD fi,DWORD ei,
		Mesh *mesh,EditMeshData *meshData,AdjFaceList *adj,BOOL vis)
	{
	if (vis) {
		mesh->faces[fi].flags |= (1<<ei);
	} else {
		mesh->faces[fi].flags &= ~(1<<ei);
		}
	meshData->tdelta.SetEdgeVisibility(fi,mesh->faces[fi].flags);

	// Do the other side
	DWORD ofi = (*adj)[fi].f[ei];
	if (ofi!=UNDEFINED) {
		int oei = GetEdgeIndex(mesh->faces[ofi],
					mesh->faces[fi].v[ei],
					mesh->faces[fi].v[(ei+1)%3]);
		if (vis) {
			mesh->faces[ofi].flags |= (1<<oei);
		} else {
			mesh->faces[ofi].flags &= ~(1<<oei);
			}
		meshData->tdelta.SetEdgeVisibility(ofi,mesh->faces[ofi].flags);
		}					
	}

void EditMeshMod::SetSelEdgeVis(BOOL vis)
	{
	ModContextList mcList;	
	INodeTab nodes;

	if (!iObjParams) return;
	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);
	
	theHold.Begin();

	for (int i = 0; i < mcList.Count(); i++) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if (!meshData) continue;
		if (meshData->GetFlag(EMD_BEENDONE)) continue;
		meshData->BeginEdit(iObjParams->GetTime());
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());
		AdjFaceList *adj = meshData->TempData(this)->FaceAdjList(iObjParams->GetTime());

		// Start a restore object...
		FaceAttribRestore *rest = new FaceAttribRestore(meshData,this);		
		theHold.Put(rest);

		for (int j=0; j<mesh->getNumFaces(); j++) {
			for (int k=0; k<3; k++) {
				if (mesh->edgeSel[j*3+k]) {
					SetSingleEdgeVis(j,k,mesh,meshData,adj,vis);										
					}
				}
			}
		
		meshData->SetFlag(EMD_BEENDONE,TRUE);		
		}
	
	if (vis) {
		theHold.Accept(GetString(IDS_RB_EDGEVISIBLE));
	} else {
		theHold.Accept(GetString(IDS_RB_EDGEINVISIBLE));
		}

	nodes.DisposeTemporary();
	NotifyDependents(FOREVER, PART_TOPO, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}


void EditMeshMod::AutoEdge(float thresh)
	{
	ModContextList mcList;	
	INodeTab nodes;

	if (!iObjParams) return;
	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);
	
	theHold.Begin();

	for (int i = 0; i < mcList.Count(); i++) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if (!meshData) continue;
		if (meshData->GetFlag(EMD_BEENDONE)) continue;
		meshData->BeginEdit(iObjParams->GetTime());
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());
		AdjEdgeList *ae = meshData->TempData(this)->AdjList(iObjParams->GetTime());
		AdjFaceList *af = meshData->TempData(this)->FaceAdjList(iObjParams->GetTime());
		Tab<MEdge> &edges = ae->edges;

		// Start a restore object...
		FaceAttribRestore *rest = new FaceAttribRestore(meshData,this);		
		theHold.Put(rest);

		for (int j=0; j<edges.Count(); j++) {						
			if (edges[j].Selected(mesh->faces,meshData->esel)) {
				BOOL vis = (thresh==0.0f) ||
					AngleBetweenFaces(mesh,edges[j].f[0],edges[j].f[1]) > thresh;
				

				if (edges[j].f[0]!=UNDEFINED) {
					int e = GetEdgeIndex(mesh->faces[edges[j].f[0]],edges[j].v[0],edges[j].v[1]);
					SetSingleEdgeVis(edges[j].f[0],e,mesh,meshData,af,vis);
				} else
				if (edges[j].f[1]!=UNDEFINED) {
					int e = GetEdgeIndex(mesh->faces[edges[j].f[1]],edges[j].v[0],edges[j].v[1]);
					SetSingleEdgeVis(edges[j].f[1],e,mesh,meshData,af,vis);
					}
				}			
			}
		
		meshData->SetFlag(EMD_BEENDONE,TRUE);		
		}
		
	theHold.Accept(GetString(IDS_RB_AUTOEDGE));
	
	nodes.DisposeTemporary();
	NotifyDependents(FOREVER, PART_TOPO, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}

void EditMeshMod::DivideSelEdges()
	{
	ModContextList mcList;	
	INodeTab nodes;

	if (!iObjParams) return;
	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);
	
	theHold.Begin();

	for (int i = 0; i < mcList.Count(); i++) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if (!meshData) continue;
		if (meshData->GetFlag(EMD_BEENDONE)) continue;
		meshData->BeginEdit(iObjParams->GetTime());
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());

		// Start a restore object...
		TopoEditRestore *rest = new TopoEditRestore(meshData,this);		
		theHold.Put(rest);

		meshData->DivideEdges(mesh, 
			meshData->TempData(this)->AdjList(iObjParams->GetTime()),
			meshData->esel);
		meshData->SetFlag(EMD_BEENDONE,TRUE);
		meshData->Invalidate(PART_GEOM|PART_TOPO);
		}
	
	theHold.Accept(GetString(IDS_RB_EDGEDIVIDE));

	nodes.DisposeTemporary();
	NotifyDependents(FOREVER, PART_TOPO, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}



void EditMeshMod::TurnSelEdges()
	{
	ModContextList mcList;	
	INodeTab nodes;

	if (!iObjParams) return;
	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);
	
	theHold.Begin();

	for (int i = 0; i < mcList.Count(); i++) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if (!meshData) continue;
		if (meshData->GetFlag(EMD_BEENDONE)) continue;
		meshData->BeginEdit(iObjParams->GetTime());
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());

		// Start a restore object...
		TopoEditRestore *rest = new TopoEditRestore(meshData,this);		
		theHold.Put(rest);

		meshData->TurnEdges(mesh, 
			meshData->TempData(this)->AdjList(iObjParams->GetTime()),
			meshData->esel);
		meshData->SetFlag(EMD_BEENDONE,TRUE);
		meshData->Invalidate(PART_GEOM|PART_TOPO);
		}
	
	theHold.Accept(GetString(IDS_RB_EDGETURN));

	nodes.DisposeTemporary();
	NotifyDependents(FOREVER, PART_TOPO, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}


void EditMeshMod::CollapseSelEdges()
	{
	ModContextList mcList;	
	INodeTab nodes;

	if (!iObjParams) return;
	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);
	
	theHold.Begin();

	for (int i = 0; i < mcList.Count(); i++) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if (!meshData) continue;
		if (meshData->GetFlag(EMD_BEENDONE)) continue;
		meshData->BeginEdit(iObjParams->GetTime());
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());

		// Start a restore object...
		TopoEditRestore *rest = new TopoEditRestore(meshData,this);		
		theHold.Put(rest);

		meshData->CollapseEdges(mesh, 
			meshData->TempData(this)->AdjList(iObjParams->GetTime()));
		meshData->SetFlag(EMD_BEENDONE,TRUE);
		meshData->Invalidate(PART_GEOM|PART_TOPO);
		}
	
	theHold.Accept(GetString(IDS_RB_COLLAPSEEDGE));

	nodes.DisposeTemporary();
	NotifyDependents(FOREVER, PART_TOPO, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}


void EditMeshMod::CollapseSelFaces()
	{
	ModContextList mcList;	
	INodeTab nodes;

	if (!iObjParams) return;
	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);
	
	theHold.Begin();

	for (int i = 0; i < mcList.Count(); i++) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if (!meshData) continue;
		if (meshData->GetFlag(EMD_BEENDONE)) continue;
		meshData->BeginEdit(iObjParams->GetTime());
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());

		// Start a restore object...
		TopoEditRestore *rest = new TopoEditRestore(meshData,this);		
		theHold.Put(rest);
						
		// Just weld verts of selected faces.
		BitArray set;
		set.SetSize(mesh->getNumVerts());
		for (int k=0; k<mesh->getNumFaces(); k++) {
			if (mesh->faceSel[k]) {
				set.Set(mesh->faces[k].v[0]);
				set.Set(mesh->faces[k].v[1]);
				set.Set(mesh->faces[k].v[2]);
				}
			}
		meshData->WeldVertSet(mesh,set);
		meshData->SetFlag(EMD_BEENDONE,TRUE);
		meshData->Invalidate(PART_GEOM|PART_TOPO);
		}
	
	theHold.Accept(GetString(IDS_RB_FACECOLLAPSE));

	nodes.DisposeTemporary();
	NotifyDependents(FOREVER, PART_TOPO|PART_GEOM, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}

void EditMeshMod::MakePlanar()
	{
	ModContextList mcList;	
	INodeTab nodes;

	if (!iObjParams) return;
	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);
	
	theHold.Begin();

	for (int i = 0; i < mcList.Count(); i++) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if (!meshData) continue;
		if (meshData->GetFlag(EMD_BEENDONE)) continue;
		meshData->BeginEdit(iObjParams->GetTime());
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());

		// Start a restore object...
		VertexEditRestore *rest = new VertexEditRestore(meshData,this);		
		theHold.Put(rest);
						
		meshData->MakeSelPlanar(mesh);
		meshData->SetFlag(EMD_BEENDONE,TRUE);
		meshData->Invalidate(PART_GEOM);
		}
	
	theHold.Accept(GetString(IDS_RB_MAKEPLANAR));

	nodes.DisposeTemporary();
	NotifyDependents(FOREVER, PART_GEOM, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}

void EditMeshMod::HideSelFaces()
	{
	ModContextList mcList;	
	INodeTab nodes;

	if (!iObjParams) return;
	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);
	
	theHold.Begin();

	for (int i = 0; i < mcList.Count(); i++) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if (!meshData) continue;
		if (meshData->GetFlag(EMD_BEENDONE)) continue;
		meshData->BeginEdit(iObjParams->GetTime());
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());		

		// Start a restore object...
		FaceAttribRestore *rest = new FaceAttribRestore(meshData,this);		
		theHold.Put(rest);

		for (int j=0; j<mesh->getNumFaces(); j++) {
			if (mesh->faceSel[j]) {
				mesh->faces[j].Hide();
				meshData->tdelta.SetFaceVisibility((DWORD)j,FALSE);
				}
			}
		
		// Reset vertex hide flags
		SetupHideFlags(*mesh,meshData->vdelta.hide);

		meshData->SetFlag(EMD_BEENDONE,TRUE);		
		}
		
	theHold.Accept(GetString(IDS_RB_HIDEFACE));
	
	nodes.DisposeTemporary();
	NotifyDependents(FOREVER, PART_TOPO, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}

void EditMeshMod::UnhideAllFaces()
	{
	ModContextList mcList;	
	INodeTab nodes;

	if (!iObjParams) return;
	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);
	
	theHold.Begin();

	for (int i = 0; i < mcList.Count(); i++) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if (!meshData) continue;
		if (meshData->GetFlag(EMD_BEENDONE)) continue;
		meshData->BeginEdit(iObjParams->GetTime());
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());		

		// Start a restore object...
		FaceAttribRestore *rest = new FaceAttribRestore(meshData,this);		
		theHold.Put(rest);

		for (int j=0; j<mesh->getNumFaces(); j++) {			
			mesh->faces[j].Show();
			meshData->tdelta.SetFaceVisibility((DWORD)j,TRUE);			
			}
		
		// Reset vertex hide flags
		SetupHideFlags(*mesh,meshData->vdelta.hide);

		meshData->SetFlag(EMD_BEENDONE,TRUE);		
		}
		
	theHold.Accept(GetString(IDS_RB_HIDEFACE));
	
	nodes.DisposeTemporary();
	NotifyDependents(FOREVER, PART_TOPO, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}

void EditMeshMod::HideSelVerts()
	{
	ModContextList mcList;	
	INodeTab nodes;

	if (!iObjParams) return;
	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);
	
	theHold.Begin();

	for (int i = 0; i < mcList.Count(); i++) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if (!meshData) continue;
		if (meshData->GetFlag(EMD_BEENDONE)) continue;
		meshData->BeginEdit(iObjParams->GetTime());
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());		

		// Start a restore object...
		VertexHideRestore *rest = new VertexHideRestore(meshData,this);		
		theHold.Put(rest);

		for (int j=0; j<mesh->getNumVerts(); j++) {
			if (mesh->vertSel[j]) {
				mesh->vertHide.Set(j,TRUE);				
				meshData->vdelta.hide.Set(j,TRUE);
				}
			}		

		meshData->SetFlag(EMD_BEENDONE,TRUE);		
		}
		
 	theHold.Accept(GetString(IDS_RB_HIDEVERT));
	
	nodes.DisposeTemporary();
	NotifyDependents(FOREVER, PART_TOPO, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}

void EditMeshMod::UnhideAllVerts()
	{
	ModContextList mcList;	
	INodeTab nodes;

	if (!iObjParams) return;
	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);
	
	theHold.Begin();

	for (int i = 0; i < mcList.Count(); i++) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if (!meshData) continue;
		if (meshData->GetFlag(EMD_BEENDONE)) continue;
		meshData->BeginEdit(iObjParams->GetTime());
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());		

		// Start a restore object...
		VertexHideRestore *rest = new VertexHideRestore(meshData,this);		
		theHold.Put(rest);

		for (int j=0; j<mesh->getNumVerts(); j++) {			
			mesh->vertHide.Set(j,FALSE);				
			meshData->vdelta.hide.Set(j,FALSE);
			}		

		meshData->SetFlag(EMD_BEENDONE,TRUE);		
		}
		
	theHold.Accept(GetString(IDS_RB_HIDEVERT));
	
	nodes.DisposeTemporary();
	NotifyDependents(FOREVER, PART_TOPO, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}

void EditMeshMod::ExplodeFaces(float thresh,BOOL objs,TSTR &name)
	{
	ModContextList mcList;	
	INodeTab nodes;

	if (!iObjParams) return;
	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);
	
	theHold.Begin();

	for (int i = 0; i < mcList.Count(); i++) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if (!meshData) continue;
		if (meshData->GetFlag(EMD_BEENDONE)) continue;
		meshData->BeginEdit(iObjParams->GetTime());
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());
		
		// Start a restore object...
		TopoEditRestore *rest = new TopoEditRestore(meshData,this);		
		theHold.Put(rest);
						
		if (!objs) {
			meshData->ElemExplodeFaces(mesh,thresh,
				meshData->TempData(this)->FaceAdjList(iObjParams->GetTime()));
		} else {
			meshData->ObjExplodeFaces(
				nodes[i],name,iObjParams,
				mesh,thresh,
				meshData->TempData(this)->FaceAdjList(iObjParams->GetTime()));
			}
	
		meshData->SetFlag(EMD_BEENDONE,TRUE);
		meshData->Invalidate(PART_GEOM|PART_TOPO);
		}
	
	theHold.Accept(GetString(IDS_RB_EXPLODE));

	nodes.DisposeTemporary();
	NotifyDependents(FOREVER, PART_TOPO|PART_GEOM, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}

void EditMeshMod::TesselateFaces(float tens,BOOL edge)
	{	
	ModContextList mcList;	
	INodeTab nodes;

	if (!iObjParams) return;
	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);
	
	theHold.Begin();

	for (int i = 0; i < mcList.Count(); i++) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if (!meshData) continue;
		if (meshData->GetFlag(EMD_BEENDONE)) continue;
		meshData->BeginEdit(iObjParams->GetTime());
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());

		// Start a restore object...
		TopoEditRestore *rest = new TopoEditRestore(meshData,this);		
		theHold.Put(rest);
						
		if (!edge) {
			meshData->FaceCenterTessellate(mesh);
		} else {
			meshData->EdgeTessellate(mesh, 
				meshData->TempData(this)->AdjList(iObjParams->GetTime()),
				meshData->TempData(this)->FaceAdjList(iObjParams->GetTime()),
				tens);
			}
	
		meshData->SetFlag(EMD_BEENDONE,TRUE);
		meshData->Invalidate(PART_GEOM|PART_TOPO);
		}
	
	theHold.Accept(GetString(IDS_RB_TESSELLATE));

	nodes.DisposeTemporary();
	NotifyDependents(FOREVER, PART_TOPO|PART_GEOM, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}	


//--- Surface --------------------------------------------------

DWORD EditMeshMod::GetUsedSmoothBits()
	{
	ModContextList mcList;	
	INodeTab nodes;
	BOOL first = 1;
	DWORD bits = 0;	

	if (!iObjParams) return 0;
	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);

	for (int i = 0; i < mcList.Count(); i++) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if (!meshData) continue;
		if (meshData->GetFlag(EMD_BEENDONE)) continue;
		meshData->BeginEdit(iObjParams->GetTime());
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());
		
		for (int j=0; j<mesh->getNumFaces(); j++) {			
			bits |= mesh->faces[j].smGroup;			
			}
		}
	nodes.DisposeTemporary();
	return bits;
	}


DWORD EditMeshMod::GetSelSmoothBits(DWORD &invalid)
	{
	ModContextList mcList;	
	INodeTab nodes;
	BOOL first = 1;
	DWORD bits = 0;
	invalid = 0;

	if (!iObjParams) return 0;
	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);

	for (int i = 0; i < mcList.Count(); i++) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if (!meshData) continue;
		if (meshData->GetFlag(EMD_BEENDONE)) continue;
		meshData->BeginEdit(iObjParams->GetTime());
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());
		
		for (int j=0; j<mesh->getNumFaces(); j++) {
			if (mesh->faceSel[j]) {
				if (first) {
					first = FALSE;
					bits  = mesh->faces[j].smGroup;					
				} else {
					if (mesh->faces[j].smGroup != bits) {
						invalid |= mesh->faces[j].smGroup^bits;
						}
					}
				}
			}
		}
	nodes.DisposeTemporary();
	return bits;
	}

void EditMeshMod::SetSelSmoothBits(DWORD bits,DWORD which)
	{
	ModContextList mcList;	
	INodeTab nodes;

	if (!iObjParams) return;
	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);
	
	theHold.Begin();

	for (int i = 0; i < mcList.Count(); i++) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if (!meshData) continue;
		if (meshData->GetFlag(EMD_BEENDONE)) continue;
		meshData->BeginEdit(iObjParams->GetTime());
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());		

		// Start a restore object...
		FaceSmoothGroupRestore *rest = new FaceSmoothGroupRestore(meshData,this);		
		theHold.Put(rest);

		for (int j=0; j<mesh->getNumFaces(); j++) {			
			if (mesh->faceSel[j]) {
				mesh->faces[j].smGroup &= ~which;
				mesh->faces[j].smGroup |= bits&which;
				meshData->tdelta.SetSmoothGroup((DWORD)j,bits,which);
				}
			}
		
		meshData->SetFlag(EMD_BEENDONE,TRUE);		
		}
		
	theHold.Accept(GetString(IDS_RB_SETSMOOTHGROUP));
	
	nodes.DisposeTemporary();
	InvalidateSurfaceUI();
	NotifyDependents(FOREVER, PART_TOPO, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}

int EditMeshMod::GetSelMatIndex()
	{
	ModContextList mcList;	
	INodeTab nodes;
	BOOL first = 1;
	int mat=-1;

	if (!iObjParams) return -1;
	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);

	for (int i = 0; i < mcList.Count(); i++) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if (!meshData) continue;
		if (meshData->GetFlag(EMD_BEENDONE)) continue;
		meshData->BeginEdit(iObjParams->GetTime());
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());
		
		for (int j=0; j<mesh->getNumFaces(); j++) {
			if (mesh->faceSel[j]) {
				if (first) {
					first = FALSE;
					mat   = (int)mesh->getFaceMtlIndex(j);					
				} else {
					if ((int)mesh->getFaceMtlIndex(j) != mat) {
						return -1;
						}
					}
				}
			}
		}
	
	nodes.DisposeTemporary();
	return mat;
	}

void EditMeshMod::SetSelMatIndex(int index)
	{
	ModContextList mcList;	
	INodeTab nodes;

	if (!iObjParams) return;
	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);	

	for (int i = 0; i < mcList.Count(); i++) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if (!meshData) continue;
		if (meshData->GetFlag(EMD_BEENDONE)) continue;
		meshData->BeginEdit(iObjParams->GetTime());
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());		

		if (theHold.Holding() && !meshData->GetFlag(EMD_HELD)) {
			// Start a restore object...
			FaceAttribRestore *rest = new FaceAttribRestore(meshData,this);
			theHold.Put(rest);
			meshData->SetFlag(EMD_HELD,TRUE);
			}

		for (int j=0; j<mesh->getNumFaces(); j++) {			
			if (mesh->faceSel[j]) {
				mesh->setFaceMtlIndex(j,(MtlID)index);			
				meshData->tdelta.SetMatID((DWORD)j,index);
				}
			}
		
		meshData->SetFlag(EMD_BEENDONE,TRUE);		
		}	
	
	nodes.DisposeTemporary();
	InvalidateSurfaceUI();
	NotifyDependents(FOREVER, PART_TOPO, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}


void EditMeshMod::ShowNormals()
	{
	NotifyDependents(FOREVER, PART_DISPLAY, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}

void EditMeshMod::AutoSmooth(float thresh)
	{
	ModContextList mcList;	
	INodeTab nodes;

	if (!iObjParams) return;
	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);	

	theHold.Begin();

	for (int i = 0; i < mcList.Count(); i++) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if (!meshData) continue;
		if (meshData->GetFlag(EMD_BEENDONE)) continue;
		meshData->BeginEdit(iObjParams->GetTime());
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());		
		AdjFaceList *af = meshData->TempData(this)->FaceAdjList(iObjParams->GetTime());
		AdjEdgeList *ae = meshData->TempData(this)->AdjList(iObjParams->GetTime());
		FaceAttribRestore *rest = new FaceAttribRestore(meshData,this);
		theHold.Put(rest);		
		meshData->AutoSmooth(mesh,*af,*ae,thresh);

		meshData->SetFlag(EMD_BEENDONE,TRUE);		
		}	
	
	theHold.Accept(GetString(IDS_RB_AUTOSMOOTH));

	nodes.DisposeTemporary();
	InvalidateSurfaceUI();
	NotifyDependents(FOREVER, PART_TOPO, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}

void EditMeshMod::SelectBySmoothGroup(DWORD bits,BOOL clear)
	{
	ModContextList mcList;	
	INodeTab nodes;

	if (!iObjParams) return;
	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);
	
	theHold.Begin();

	for (int i = 0; i < mcList.Count(); i++) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if (!meshData) continue;
		if (meshData->GetFlag(EMD_BEENDONE)) continue;
		meshData->BeginEdit(iObjParams->GetTime());
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());		

		// Start a restore object...
		theHold.Put(new FaceSelRestore(meshData,this));		
		
		if (clear) {
			mesh->faceSel.ClearAll();
			//meshData->fsel.ClearAll();
			}
		for (int j=0; j<mesh->getNumFaces(); j++) {			
			if (mesh->faces[j].smGroup & bits) {
				mesh->faceSel.Set(j);
				//meshData->fsel.Set(j);
				}
			}
		
		DeselectHiddenFaces(*mesh);
		meshData->fsel = mesh->faceSel;
			
		meshData->SetFlag(EMD_BEENDONE,TRUE);
		if (meshData->tempData) {
			meshData->TempData(this)->Invalidate(PART_SELECT);
			}
		}
		
	theHold.Accept(GetString(IDS_RB_SELECTBYSMOOTH));
	
	nodes.DisposeTemporary();
	InvalidateSurfaceUI();
	NotifyDependents(FOREVER, PART_SELECT, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}

void EditMeshMod::SelectByMat(int index,BOOL clear)
	{
	ModContextList mcList;	
	INodeTab nodes;

	if (!iObjParams) return;
	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);
	
	theHold.Begin();

	for (int i = 0; i < mcList.Count(); i++) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if (!meshData) continue;
		if (meshData->GetFlag(EMD_BEENDONE)) continue;
		meshData->BeginEdit(iObjParams->GetTime());
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());		

		// Start a restore object...
		theHold.Put(new FaceSelRestore(meshData,this));		
		
		if (clear) {
			mesh->faceSel.ClearAll();
			meshData->fsel.ClearAll();
			}
		for (int j=0; j<mesh->getNumFaces(); j++) {			
			if (mesh->getFaceMtlIndex(j)==index) {
				mesh->faceSel.Set(j);
				//meshData->fsel.Set(j);
				}
			}
		
		DeselectHiddenFaces(*mesh);
		meshData->fsel = mesh->faceSel;

		meshData->SetFlag(EMD_BEENDONE,TRUE);
		if (meshData->tempData) {
			meshData->TempData(this)->Invalidate(PART_SELECT);
			}
		}
		
	theHold.Accept(GetString(IDS_RB_SELECTBYMATID));
	
	nodes.DisposeTemporary();
	InvalidateSurfaceUI();
	NotifyDependents(FOREVER, PART_SELECT, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}

void EditMeshMod::FlipSelNormals()
	{
	ModContextList mcList;	
	INodeTab nodes;

	if (!iObjParams) return;
	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);
	
	theHold.Begin();

	for (int i = 0; i < mcList.Count(); i++) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if (!meshData) continue;
		if (meshData->GetFlag(EMD_BEENDONE)) continue;
		meshData->BeginEdit(iObjParams->GetTime());
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());		

		// Start a restore object...
		TopoEditRestore *rest = new TopoEditRestore(meshData,this);		
		theHold.Put(rest);		
		
		for (int j=0; j<mesh->getNumFaces(); j++) {			
			if (mesh->faceSel[j]) {
				meshData->FlipNormal(mesh,(DWORD)j);				
				}
			}
		
		meshData->SetFlag(EMD_BEENDONE,TRUE);		
		}
		
	theHold.Accept(GetString(IDS_RB_FLIPNORMALS));
	
	nodes.DisposeTemporary();
	NotifyDependents(FOREVER, PART_TOPO, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}

void EditMeshMod::UnifySelNormals()
	{
	ModContextList mcList;	
	INodeTab nodes;

	if (!iObjParams) return;
	iObjParams->GetModContexts(mcList,nodes);
	ClearMeshDataFlag(mcList,EMD_BEENDONE);	

	theHold.Begin();

	for (int i = 0; i < mcList.Count(); i++) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if (!meshData) continue;
		if (meshData->GetFlag(EMD_BEENDONE)) continue;
		meshData->BeginEdit(iObjParams->GetTime());
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());		
		AdjFaceList *af = meshData->TempData(this)->FaceAdjList(iObjParams->GetTime());

		TopoEditRestore *rest = new TopoEditRestore(meshData,this);		
		theHold.Put(rest);
		meshData->UnifyNormals(mesh,*af);

		meshData->SetFlag(EMD_BEENDONE,TRUE);		
		}	
	
	theHold.Accept(GetString(IDS_RB_UNIFYNORMALS));

	nodes.DisposeTemporary();
	InvalidateSurfaceUI();
	NotifyDependents(FOREVER, PART_TOPO, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}

//----------------------------------------------------------



// This could be a bit array method
/*
static void DeleteBit(BitArray &b,int index,int num=1)
	{
	for (int i=index; i<b.GetSize()-num; i++) {		
		b.Set(i,b[i+num]);		
		}
	}
*/

void EditMeshData::DeleteFaceSet(Mesh *mesh,BitArray &set,BitArray *isoVert)
	{
	// Flag the verts used by faces about to be deleted.
	if (isoVert) {
		for (int i=0; i<mesh->getNumFaces(); i++) {
			if (set[i]) {
				isoVert->Set(mesh->faces[i].v[0]);
				isoVert->Set(mesh->faces[i].v[1]);
				isoVert->Set(mesh->faces[i].v[2]);
				}
			}
		}

	// Zip through the faces and delete any face that is selected
	/*
	for (int i=0; i<mesh->getNumFaces(); i++) {
		if (set[i]) {			
			DWORD del = (DWORD)i;
			MeshDeleteFace(*mesh,i);
			DeleteBit(fsel,i);			
			DeleteBit(esel,i,3);
			if (&fsel!=&set) DeleteBit(set,i);	
			tdelta.RemoveFace(del);			
			i--;
			}
		}
	*/
	for (int i=0,j=0; i<mesh->getNumFaces(); i++) {
		if (set[i]) {
			tdelta.RemoveFace((DWORD)j);
		} else {
			j++;
			}
		}
	MeshDeleteFaces(*mesh,set);
	fselSet.DeleteSetElements(set);
	eselSet.DeleteSetElements(set,3);
	BitArrayDeleteSet(esel,set,3);
	BitArrayDeleteSet(fsel,set);		

	// Take any verts still being referenced out of the isolated list.
	if (isoVert) {
		for (int i=0; i<mesh->getNumFaces(); i++) {
			isoVert->Clear(mesh->faces[i].v[0]);
			isoVert->Clear(mesh->faces[i].v[1]);
			isoVert->Clear(mesh->faces[i].v[2]);
			}
		}

	fsel.SetSize(mesh->getNumFaces(),TRUE);	
	esel.SetSize(mesh->getNumFaces()*3,TRUE);
	mesh->faceSel = fsel;
	mesh->edgeSel = esel;
	
	mesh->InvalidateEdgeList();
	mesh->InvalidateGeomCache();
	}

void EditMeshData::DeleteVertSet(Mesh *mesh,BitArray &set)
	{		

	// Zip through the faces and delete any face that has a
	// selected vertex.
	/*
	for (int i=0; i<mesh->getNumFaces(); i++) {
		if (set[mesh->faces[i].v[0]] ||
			set[mesh->faces[i].v[1]] ||
			set[mesh->faces[i].v[2]]) {
			
			DWORD del = (DWORD)i;
			MeshDeleteFace(*mesh,i);
			DeleteBit(fsel,i);
			DeleteBit(esel,i,3);
			tdelta.RemoveFace(del);			
			i--;
			}
		}		
	*/
	BitArray delSet;
	delSet.SetSize(mesh->getNumFaces());
	for (int i=0; i<mesh->getNumFaces(); i++) {
		if (set[mesh->faces[i].v[0]] ||
			set[mesh->faces[i].v[1]] ||
			set[mesh->faces[i].v[2]]) delSet.Set(i);
		}
	int j=0;
	for (i=0; i<mesh->getNumFaces(); i++) {
		if (delSet[i]) {
			tdelta.RemoveFace((DWORD)j);
		} else {
			j++;
			}
		}
	MeshDeleteFaces(*mesh,delSet);	
	fselSet.DeleteSetElements(delSet);
	eselSet.DeleteSetElements(delSet,3);
	BitArrayDeleteSet(esel,delSet,3);	
	BitArrayDeleteSet(fsel,delSet);	

	mesh->faceSel = fsel;
	mesh->edgeSel = esel;

	// Build the delMap. This is a table with one entry per vertex. The
	// entry is the number of vertices deleted before this vertex.
	DWORDTab delMap;
	DWORD deleted = 0;
	delMap.SetCount(mesh->getNumVerts());
	for (i=0; i<mesh->getNumVerts(); i++) {
		delMap[i] = deleted;
		if (set[i]) {
			deleted++;
			}
		}

	// Now remap the faces using the delMap
	for (i=0; i<mesh->getNumFaces(); i++) {
		DWORD v[3];
		v[0] = mesh->faces[i].v[0] - delMap[mesh->faces[i].v[0]];
		v[1] = mesh->faces[i].v[1] - delMap[mesh->faces[i].v[1]];
		v[2] = mesh->faces[i].v[2] - delMap[mesh->faces[i].v[2]];		
		mesh->faces[i].v[0] = v[0];
		mesh->faces[i].v[1] = v[1];
		mesh->faces[i].v[2] = v[2];
		tdelta.ModifyFace((DWORD)i,v);
		}

	// Zip through the vertices and delete selected verts
	/*
	for (i=0; i<mesh->getNumVerts(); i++) {
		if (set[i]) {			
			DWORD del = (DWORD)i;
			MeshDeleteVertex(*mesh,i);
			DeleteBit(vsel,i);			
			if (&vsel != &set) DeleteBit(set,i);
			tdelta.RemoveVertex(del);
			vdelta.deltas.Delete(i,1);
			i--;
			}
		}
	*/		
	j=0;
	for (i=0; i<mesh->getNumVerts(); i++) {
		if (set[i]) {
			tdelta.RemoveVertex((DWORD)j);			
		} else {
			j++;
			}
		}
	MeshDeleteVertices(*mesh,set);
	Point3TabDeleteSet(vdelta.deltas,set);
	vselSet.DeleteSetElements(set);
	BitArrayDeleteSet(vdelta.hide,set);	
	BitArrayDeleteSet(vsel,set);	

	vsel.SetSize(mesh->getNumVerts(),TRUE);	
	fsel.SetSize(mesh->getNumFaces(),TRUE);
	esel.SetSize(mesh->getNumFaces()*3,TRUE);
	mesh->vertSel = vsel;	
	mesh->faceSel.SetSize(mesh->getNumFaces(),TRUE);
	mesh->edgeSel.SetSize(mesh->getNumFaces()*3,TRUE);
	
	mesh->InvalidateEdgeList();
	mesh->InvalidateGeomCache();
	}


void EditMeshData::WeldVertSet(Mesh *mesh,BitArray &set,Point3 *weldPoint)
	{
	int i;
	int firstSel = -1, numSel=0;
	Point3 pt(0,0,0), dt;

	// Find the first selected vert and find the average of all
	// selected verts
	for (i=0; i<set.GetSize(); i++) {
		if (set[i]) {
			if (firstSel<0) firstSel = i;
			numSel++;
			pt += mesh->verts[i];
			}
		}
	if (firstSel < 0) return;
	if (weldPoint) {
		pt = *weldPoint;
	} else {
		pt /= float(numSel);
		}

	// Move the first point to the average position of all verts
	// getting welded.
	dt = pt-mesh->verts[firstSel];
	mesh->verts[firstSel] = pt;
	vdelta.Move(0,firstSel,dt);

	// Go through each face. If exactly one of the faces vertices is
	// going to be welded, Then remap it. If more than one is going to
	// be welded then delete the face.
	/*
	for (i=0; i<mesh->getNumFaces(); i++) {
		int ct = 0;
		for (int j=0; j<3; j++) {
			if (set[mesh->faces[i].v[j]]) ct++;			
			}
		
		if (ct > 1) {
			// Delete it.
			DWORD del = (DWORD)i;
			MeshDeleteFace(*mesh,i);
			DeleteBit(fsel,i);
			DeleteBit(esel,i,3);
			tdelta.RemoveFace(del);			
			i--;
			}
		else if (ct == 1) {
			// Remap it.
			DWORD v[3] = {mesh->faces[i].v[0],mesh->faces[i].v[1],mesh->faces[i].v[2]};
			for (int j=0; j<3; j++) {
				if (set[mesh->faces[i].v[j]]) {
					v[j] = firstSel;
					mesh->faces[i].v[j] = firstSel;
					tdelta.ModifyFace((DWORD)i,v);		
					break;
					}
				}
			}
		}
	*/	
	BitArray delSet;
	delSet.SetSize(mesh->getNumFaces());
	for (i=0; i<mesh->getNumFaces(); i++) {
		int ct = 0;
		for (int j=0; j<3; j++) {
			if (set[mesh->faces[i].v[j]]) ct++;			
			}

		if (ct > 1) delSet.Set(i); // Delete it
		else if (ct == 1) {
			// Remap it.
			DWORD v[3] = {mesh->faces[i].v[0],mesh->faces[i].v[1],mesh->faces[i].v[2]};
			for (int j=0; j<3; j++) {
				if (set[mesh->faces[i].v[j]]) {
					v[j] = firstSel;
					mesh->faces[i].v[j] = firstSel;
					tdelta.ModifyFace((DWORD)i,v);		
					break;
					}
				}
			}
		}
	int j=0;
	for (i=0; i<mesh->getNumFaces(); i++) {
		if (delSet[i]) {
			tdelta.RemoveFace((DWORD)j);
		} else {
			j++;
			}
		}
	MeshDeleteFaces(*mesh,delSet);	
	fselSet.DeleteSetElements(delSet);
	eselSet.DeleteSetElements(delSet,3);
	BitArrayDeleteSet(esel,delSet,3);	
	BitArrayDeleteSet(fsel,delSet);	
	
	// Build the delMap. This is a table with one entry per vertex. The
	// entry is the number of vertices deleted before this vertex.
	DWORDTab delMap;
	DWORD deleted = 0;
	delMap.SetCount(mesh->getNumVerts());
	for (i=0; i<mesh->getNumVerts(); i++) {
		delMap[i] = deleted;
		if (set[i] && i!=firstSel) {
			deleted++;
			}
		}

	// Now remap the faces using the delMap
	for (i=0; i<mesh->getNumFaces(); i++) {
		DWORD v[3];
		v[0] = mesh->faces[i].v[0] - delMap[mesh->faces[i].v[0]];
		v[1] = mesh->faces[i].v[1] - delMap[mesh->faces[i].v[1]];
		v[2] = mesh->faces[i].v[2] - delMap[mesh->faces[i].v[2]];		
		mesh->faces[i].v[0] = v[0];
		mesh->faces[i].v[1] = v[1];
		mesh->faces[i].v[2] = v[2];
		tdelta.ModifyFace((DWORD)i,v);
		}

	// Go through all selected vertices, except the first and delete them.	
	/*
	for (i=firstSel+1; i<mesh->getNumVerts(); i++) {
		if (set[i]) {	
			DWORD del = (DWORD)i;
			MeshDeleteVertex(*mesh,i);
			DeleteBit(vsel,i);						
			if (&set!=&vsel) DeleteBit(set,i);
			tdelta.RemoveVertex(del);
			vdelta.deltas.Delete(i,1);
			i--;
			}
		}
	*/
	int remapFirstSel = firstSel-delMap[firstSel];
	set.Set(firstSel,FALSE);
	j=firstSel+1;
	for (i=firstSel+1; i<mesh->getNumVerts(); i++) {
		if (set[i]) {
			tdelta.RemoveVertex((DWORD)j);			
		} else {
			j++;
			}
		}
	MeshDeleteVertices(*mesh,set);
	Point3TabDeleteSet(vdelta.deltas,set);
	vselSet.DeleteSetElements(set);
	BitArrayDeleteSet(vsel,set);	
	BitArrayDeleteSet(vdelta.hide,set);	

	vsel.Set(remapFirstSel);
	vsel.SetSize(mesh->getNumVerts(),TRUE);
	fsel.SetSize(mesh->getNumFaces(),TRUE);
	esel.SetSize(mesh->getNumFaces()*3,TRUE);
	mesh->vertSel = vsel;	
	mesh->faceSel = fsel;
	mesh->edgeSel = esel;
	
	mesh->InvalidateEdgeList();
	mesh->InvalidateGeomCache();	
	}


BOOL EditMeshData::WeldVerts(Mesh *mesh,float thresh,int type,Point3 weldPoint)
	{
	int i, j;	
	if (type==WELD_COLLAPSE) {		
		// Weld selected verts to each other.
		WeldVertSet(mesh,vsel);
	} else if (type==WELD_TOVERT) {
		WeldVertSet(mesh,vsel,&weldPoint);
	} else {
		// So we can compare length squared
		thresh = thresh * thresh;
		BOOL found = 0;

		for (i=0; i<mesh->getNumVerts(); i++) {
			if (!vsel[i]) continue;

			// We'll flag verts that are within the threshold for the ith vert
			BitArray set;
			set.SetSize(mesh->getNumVerts());			
			set.Set(i);			
			BOOL f = 0;

			for (j=0; j<mesh->getNumVerts(); j++) {
				if (i==j) continue;
				if (!vsel[j]) continue;

				if (LengthSquared(mesh->verts[i]-mesh->verts[j]) < thresh) {
					set.Set(j);
					f = TRUE;
					}
				}
			
			// If we found any to weld, weld away!
			if (f) {
				WeldVertSet(mesh,set);
				found = TRUE;
				}
			}
		
		return found;
		}
	return TRUE;
	}

static int GetFaceVertIndex(Face *f,DWORD v)
	{
	for (int i=0; i<3; i++) {
		if (f->v[i]==v) return i;
		}
	assert(0);
	return -1;
	}

inline SameFaceDir(Face *f,DWORD v0,DWORD v1)
	{
	int i0 = GetFaceVertIndex(f,v0);
	int i1 = GetFaceVertIndex(f,v1);
	return i1==(i0+1)%3;
	}

void EditMeshData::DivideEdges(Mesh *mesh,AdjEdgeList *ae,BitArray &sel)
	{
	Tab<MEdge> &edges = ae->edges;
	Point3Tab nverts;
	Point3Tab ntverts;
	Tab<Face> nfaces;
	Tab<TVFace> ntvfaces;
	int numv = mesh->getNumVerts();
	int numf = mesh->getNumFaces();
	int nsel = 0;
	BOOL uvs = (BOOL)mesh->tvFace;

	for (int i=0; i<edges.Count(); i++) {
		if (edges[i].Selected(mesh->faces,sel)) {
			nsel++;

			// Add the new middle vert
			Point3 c = edges[i].Center(mesh->verts);
			nverts.Append(1,&c,10);
			
			// Split each face
			if (edges[i].f[0]!=UNDEFINED) {
				Face *face = &mesh->faces[edges[i].f[0]];
				int v0 = GetFaceVertIndex(face,edges[i].v[0]);
				int v1 = GetFaceVertIndex(face,edges[i].v[1]);
				int oi = GetOtherIndex(face,face->v[v0],face->v[v1]);
				int ci = numv + nverts.Count()-1;

				// We're pulling this face off one of it's edges
				int openEdge = ae->FindEdge(face->v[v0],oi);
				if (edges[openEdge].f[0] == edges[i].f[0]) {
					edges[openEdge].f[0] = UNDEFINED;
					}
				if (edges[openEdge].f[1] == edges[i].f[0]) {
					edges[openEdge].f[1] = UNDEFINED;
					}		

				// Remap the exisitng face
				face->v[v0] = ci;
				tdelta.ModifyFace(edges[i].f[0],*face);
				
				// Create a new face with the same normal direction
				Face f;
				BOOL parity;
				if (SameFaceDir(face,ci,oi)) {
					f.setVerts(oi,ci,edges[i].v[0]);
					parity = 0;
				} else {
					f.setVerts(ci,oi,edges[i].v[0]);
					parity = 1;
					}
				f.smGroup = 0;
				f.flags   =	EDGE_ALL;
				nfaces.Append(1,&f,10);
				
				if (uvs) {
					// Create new UV vert
					TVFace *tvf = &mesh->tvFace[edges[i].f[0]];
					Point3 tc = (mesh->tVerts[tvf->t[v0]] + mesh->tVerts[tvf->t[v1]])/2.0f;
					ntverts.Append(1,&tc,10);
					DWORD tci = mesh->numTVerts + ntverts.Count()-1;
					DWORD toi = (0!=v0&&0!=v1) ? 0 : ((1!=v0&&1!=v1)?1:2);
					 
					// Remap existing tv face
					DWORD old  = tvf->t[v0];
					tvf->t[v0] = tci;
					tdelta.ModifyTVFace(edges[i].f[0],*tvf);

					// Create a new face
					TVFace ntvf(parity?tci:tvf->t[toi],(!parity)?tci:tvf->t[toi], old);
					ntvfaces.Append(1,&ntvf,10);
					}
				}
			if (edges[i].f[1]!=UNDEFINED) {
				Face *face = &mesh->faces[edges[i].f[1]];
				int v0 = GetFaceVertIndex(face,edges[i].v[0]);
				int v1 = GetFaceVertIndex(face,edges[i].v[1]);
				int oi = GetOtherIndex(face,face->v[v0],face->v[v1]);
				int ci = numv + nverts.Count()-1;

				// We're pulling this face off one of it's edges
				int openEdge = ae->FindEdge(face->v[v0],oi);
				if (edges[openEdge].f[0] == edges[i].f[1]) {
					edges[openEdge].f[0] = UNDEFINED;
					}
				if (edges[openEdge].f[1] == edges[i].f[1]) {
					edges[openEdge].f[1] = UNDEFINED;
					}		

				// Remap the exisitng face
				face->v[v0] = ci;
				tdelta.ModifyFace(edges[i].f[1],*face);
				
				// Create a new face with the same normal direction
				Face f;
				BOOL parity;
				if (SameFaceDir(face,oi,ci)) {
					f.setVerts(ci,oi,edges[i].v[0]);
					parity = 0;
				} else {
					f.setVerts(oi,ci,edges[i].v[0]);
					parity = 1;
					}
				f.smGroup = 0;
				f.flags   =	EDGE_ALL;
				nfaces.Append(1,&f,10);
				
				if (uvs) {
					// Create new UV vert
					TVFace *tvf = &mesh->tvFace[edges[i].f[1]];
					Point3 tc = (mesh->tVerts[tvf->t[v0]] + mesh->tVerts[tvf->t[v1]])/2.0f;
					ntverts.Append(1,&tc,10);
					DWORD tci = mesh->numTVerts + ntverts.Count()-1;
					DWORD toi = (0!=v0&&0!=v1) ? 0 : ((1!=v0&&1!=v1)?1:2);
					 
					// Remap existing tv face
					DWORD old  = tvf->t[v0];
					tvf->t[v0] = tci;
					tdelta.ModifyTVFace(edges[i].f[1],*tvf);

					// Create a new face
					TVFace ntvf((!parity)?tci:tvf->t[toi],parity?tci:tvf->t[toi], old);
					ntvfaces.Append(1,&ntvf,10);
					}
				}			
			}
		}
	if (!nsel) return;
	
	// Add the new verts and faces
	Point3 *verts = new Point3[numv + nverts.Count()];
	for (i=0; i<numv; i++) {
		verts[i] = mesh->verts[i];
		}
	for (; i<numv + nverts.Count(); i++) {
		verts[i] = nverts[i-numv];
		}	
	delete[] mesh->verts;
	mesh->verts = verts;
	mesh->numVerts = numv + nverts.Count();
	mesh->vertSel.SetSize(mesh->numVerts,TRUE);
	vdelta.SetSize(mesh->numVerts,FALSE);
	tdelta.nverts.Resize(tdelta.nverts.Count() + nverts.Count());
	for (i=0; i<nverts.Count(); i++) tdelta.nverts.Append(1,&nverts[i]);
	nverts.Resize(0);

	if (ntverts.Count()) {		
		Point3 *tverts = new Point3[mesh->numTVerts + ntverts.Count()];
		for (i=0; i<mesh->numTVerts; i++) {
			tverts[i] = mesh->tVerts[i];
			}
		for (; i<mesh->numTVerts + ntverts.Count(); i++) {
			tverts[i] = ntverts[i-mesh->numTVerts];
			}	
		delete[] mesh->tVerts;
		mesh->tVerts = tverts;
		mesh->numTVerts = mesh->numTVerts + ntverts.Count();		
		tdelta.nTverts.Resize(tdelta.nTverts.Count() + ntverts.Count());
		for (i=0; i<ntverts.Count(); i++) tdelta.nTverts.Append(1,&ntverts[i]);
		ntverts.Resize(0);
		}

	Face *faces = new Face[numf	+ nfaces.Count()];
	for (i=0; i<numf; i++) {
		faces[i] = mesh->faces[i];
		}
	for (; i<numf + nfaces.Count(); i++) {
		faces[i] = nfaces[i-numf];
		}
	delete[] mesh->faces;
	mesh->faces = faces;
	mesh->numFaces = numf + nfaces.Count();
	mesh->faceSel.SetSize(mesh->numFaces,TRUE);
	mesh->edgeSel.SetSize(mesh->numFaces*3,TRUE);
	tdelta.nfaces.Resize(tdelta.nfaces.Count() + nfaces.Count());
	for (i=0; i<nfaces.Count(); i++) tdelta.nfaces.Append(1,&nfaces[i]);
	nfaces.Resize(0);

	if (ntvfaces.Count()) {
		TVFace *tvfaces = new TVFace[numf + ntvfaces.Count()];
		for (i=0; i<numf; i++) {
			tvfaces[i] = mesh->tvFace[i];
			}
		for (; i<numf + ntvfaces.Count(); i++) {
			tvfaces[i] = ntvfaces[i-numf];
			}
		delete[] mesh->tvFace;
		mesh->tvFace = tvfaces;		
		tdelta.nTVfaces.Resize(tdelta.nTVfaces.Count() + ntvfaces.Count());
		for (i=0; i<ntvfaces.Count(); i++) tdelta.nTVfaces.Append(1,&ntvfaces[i]);
		ntvfaces.Resize(0);
		}

	mesh->InvalidateEdgeList();
	mesh->InvalidateGeomCache();

	vsel = mesh->vertSel;
	fsel = mesh->faceSel;
	esel = mesh->edgeSel;
	}

#define DEBUGVMAP

static DWORD VMapLookUp(DWORDTab &vmap,DWORD v)
	{
#ifdef DEBUGVMAP
	int i=0, s=v;
#endif
	while (vmap[v]!=v) {
		v = vmap[v];
#ifdef DEBUGVMAP		
		i++;
		if (i>10000) assert(0);
		}
#endif
	//vmap[s]=v;
	return v;
	}

void EditMeshData::CollapseEdges(Mesh *mesh,AdjEdgeList *ae)
	{
	Tab<MEdge> &edges = ae->edges;
	DWORDTab vmap;
	BitArray fdel;
	int numv = mesh->getNumVerts();
	int numf = mesh->getNumFaces();
	int i;

	vmap.SetCount(numv);
	fdel.SetSize(numf);
	for (i=0; i<numv; i++) vmap[i] = i;
	
	// Remap verts and mark deleted faces.
	for (i=0; i<edges.Count(); i++) {
		if (edges[i].Selected(mesh->faces,esel)) {
			
			int vFrom = VMapLookUp(vmap,edges[i].v[0]);
			int vTo = VMapLookUp(vmap,edges[i].v[1]);
			if (vFrom==vTo) continue;

			// Collapse to the vertex with the least adjacent edges.
			if ((*ae)[vFrom].Count() > (*ae)[vTo].Count()) {
				vmap[vFrom] = vmap[vTo];
			} else {
				vmap[vTo] = vmap[vFrom];
				}
			 
			if (edges[i].f[0]!=UNDEFINED) {
				fdel.Set(edges[i].f[0]);
				}
			if (edges[i].f[1]!=UNDEFINED) {
				fdel.Set(edges[i].f[1]);
				}
			}
		}
	
	// Delete the marked faces.
	DeleteFaceSet(mesh,fdel);
	
	// Build the delMap. This is a table with one entry per vertex. The
	// entry is the number of vertices deleted before this vertex.
	DWORDTab delMap;
	DWORD deleted = 0;
	delMap.SetCount(mesh->getNumVerts());
	for (i=0; i<mesh->getNumVerts(); i++) {
		delMap[i] = deleted;
		if (vmap[i]!=(DWORD)i) {
			deleted++;
			}
		}

	// Now remap the faces using the delMap and the vmap
	for (i=0; i<mesh->getNumFaces(); i++) {
		DWORD v[3];
		v[0]  = VMapLookUp(vmap,mesh->faces[i].v[0]);
		v[0] -= delMap[v[0]];
		v[1]  = VMapLookUp(vmap,mesh->faces[i].v[1]);
		v[1] -= delMap[v[1]];
		v[2]  = VMapLookUp(vmap,mesh->faces[i].v[2]);
		v[2] -= delMap[v[2]];		
		mesh->faces[i].v[0] = v[0];
		mesh->faces[i].v[1] = v[1];
		mesh->faces[i].v[2] = v[2];
		tdelta.ModifyFace((DWORD)i,v);
		}

	// Zip through the vertices deleting verts that have been mapped.
	/*
	int ct=0;
	for (i=0; i<mesh->getNumVerts(); i++) {
		if (vmap[ct]!=(DWORD)ct) {			
			DWORD del = (DWORD)i;
			MeshDeleteVertex(*mesh,i);
			DeleteBit(vsel,i);			
			tdelta.RemoveVertex(del);
			vdelta.deltas.Delete(i,1);
			i--;
			}
		ct++;
		}
	*/
	BitArray delSet;
	delSet.SetSize(mesh->getNumVerts());
	int j=0;
	for (i=0; i< mesh->getNumVerts(); i++) {
		if (vmap[i]!=(DWORD)i) delSet.Set(i);
		}
	for (i=0; i<mesh->getNumVerts(); i++) {
		if (delSet[i]) {
			tdelta.RemoveVertex((DWORD)j);			
		} else {
			j++;
			}
		}
	MeshDeleteVertices(*mesh,delSet);
	Point3TabDeleteSet(vdelta.deltas,delSet);
	vselSet.DeleteSetElements(delSet);
	BitArrayDeleteSet(vsel,delSet);
	BitArrayDeleteSet(vdelta.hide,delSet);	

	vsel.SetSize(mesh->getNumVerts(),TRUE);		
	mesh->vertSel = vsel;		
	esel.ClearAll();
	mesh->edgeSel = esel;

	mesh->InvalidateEdgeList();
	mesh->InvalidateGeomCache();
	}


void EditMeshData::TurnEdges(Mesh *mesh,AdjEdgeList *ae,BitArray &sel)
	{
	Tab<MEdge> &edges = ae->edges;
	int i;

	for (i=0; i<edges.Count(); i++) {
		if (edges[i].Selected(mesh->faces,sel)) {
			// Get the two faces, both must be present
			Face *f0, *f1;
			DWORD fi0, fi1;
			if ((fi0=edges[i].f[0])!=UNDEFINED) {
				f0 = &mesh->faces[fi0];
			} else continue;
			if ((fi1=edges[i].f[1])!=UNDEFINED) {
				f1 = &mesh->faces[fi1];
			} else continue;

			// Compute indices into the indices where fv[0] and fv[1] are edge.v[0] and edge.v[1]
			int fv0[3], fv1[3];

			fv0[0] = GetFaceVertIndex(f0,edges[i].v[0]);
			fv0[1] = GetFaceVertIndex(f0,edges[i].v[1]);
			fv0[2] = fv0[0]+fv0[1]==1?2:(fv0[0]+fv0[1]==2?1:0);

			fv1[0] = GetFaceVertIndex(f1,edges[i].v[0]);
			fv1[1] = GetFaceVertIndex(f1,edges[i].v[1]);
			fv1[2] = fv1[0]+fv1[1]==1?2:(fv1[0]+fv1[1]==2?1:0);

			// Fix up the edge list
			int e0, e1;
			e0 = ae->FindEdge(f0->v[fv0[0]],f0->v[fv0[2]]);
			assert(e0!=-1);
			if (edges[e0].f[0]==fi0) {
				edges[e0].f[0] = fi1;	
			} else {
				edges[e0].f[1] = fi1;
				}
			ae->RemoveEdgeFromVertex(edges[i].v[0],i);
			ae->AddEdgeToVertex(f0->v[fv0[2]],i);

			e1 = ae->FindEdge(f1->v[fv1[1]],f1->v[fv1[2]]);
			assert(e1!=-1);
			if (edges[e1].f[0]==fi1) {
				edges[e1].f[0] = fi0;	
			} else {
				edges[e1].f[1] = fi0;
				}
			ae->RemoveEdgeFromVertex(edges[i].v[1],i);
			ae->AddEdgeToVertex(f1->v[fv1[2]],i);

			// Rotate 'em
			f0->v[fv0[1]] = f1->v[fv1[2]];
			f0->v[fv0[0]] = f0->v[fv0[2]];
			f0->v[fv0[2]] = f1->v[fv1[1]];
			
			f1->v[fv1[2]] = f1->v[fv1[0]];
			f1->v[fv1[0]] = f0->v[fv0[0]];
			f1->v[fv1[1]] = f0->v[fv0[1]];
			
			// Note the change in the tdelta.
			tdelta.ModifyFace(fi0,*f0);
			tdelta.ModifyFace(fi1,*f1);
			
			// Do TV faces
			if (mesh->tvFace) {
				TVFace *tf0 = &mesh->tvFace[fi0];
				TVFace *tf1 = &mesh->tvFace[fi1];
				
				tf0->t[fv0[1]] = tf1->t[fv1[2]];
				tf0->t[fv0[0]] = tf0->t[fv0[2]];
				tf0->t[fv0[2]] = tf1->t[fv1[1]];
			
				tf1->t[fv1[2]] = tf1->t[fv1[0]];
				tf1->t[fv1[0]] = tf0->t[fv0[0]];
				tf1->t[fv1[1]] = tf0->t[fv0[1]];
				
				tdelta.ModifyTVFace(fi0,*tf0);
				tdelta.ModifyTVFace(fi1,*tf1);
				}
			}
		}
	}

void EditMeshData::CloneSelVerts(Mesh *mesh)
	{	
	Point3Tab nverts;	

	// Clone the verts
	for (int i=0; i<mesh->getNumVerts(); i++) {
		if (mesh->vertSel[i]) {
			nverts.Append(1,&mesh->verts[i],10);			
			tdelta.nverts.Append(1,&mesh->verts[i],10);
			}
		}
	tdelta.nverts.Shrink();
	
	// Copy in the verts selecting the new verts.
	mesh->vertSel.ClearAll();
	mesh->vertSel.SetSize(mesh->getNumVerts()+nverts.Count());
	Point3 *verts = new Point3[mesh->getNumVerts() + nverts.Count()];
	for (i=0; i<mesh->getNumVerts(); i++) {
		verts[i] = mesh->verts[i];
		}
	for (; i<mesh->getNumVerts() + nverts.Count(); i++) {
		mesh->vertSel.Set(i);
		verts[i] = nverts[i-mesh->getNumVerts()];
		}
	delete[] mesh->verts;
	mesh->verts = verts;
	mesh->numVerts += nverts.Count();
	vsel = mesh->vertSel;
	vdelta.SetSize(mesh->numVerts,FALSE);

	mesh->InvalidateEdgeList();
	mesh->InvalidateGeomCache();
	}

void EditMeshData::CloneSelFaces(Mesh *mesh)
	{
	DWORDTab vclones;
	int numv = mesh->getNumVerts();
	int numf = mesh->getNumFaces();
	Point3Tab nverts;	
	Tab<Face> nfaces;
	Tab<TVFace> ntvfaces;

	// Init the map
	vclones.SetCount(numv);
	for (int i=0; i<numv; i++) {
		vclones[i] = i;
		}

	for (i=0; i<numf; i++) {
		Face *f = &mesh->faces[i];
		if (fsel[i]) {
			// Clone the verts if they haven't already been cloned.
			if (vclones[f->v[0]]==f->v[0]) {
				vclones[f->v[0]] = numv + nverts.Count();
				nverts.Append(1,&mesh->verts[f->v[0]],10);
				tdelta.nverts.Append(1,&mesh->verts[f->v[0]],10);
				}
			if (vclones[f->v[1]]==f->v[1]) {
				vclones[f->v[1]] = numv + nverts.Count();
				nverts.Append(1,&mesh->verts[f->v[1]],10);
				tdelta.nverts.Append(1,&mesh->verts[f->v[1]],10);
				}
			if (vclones[f->v[2]]==f->v[2]) {
				vclones[f->v[2]] = numv + nverts.Count();
				nverts.Append(1,&mesh->verts[f->v[2]],10);
				tdelta.nverts.Append(1,&mesh->verts[f->v[2]],10);
				}

			// Clone the face
			Face nf = *f;
			nf.v[0] = vclones[nf.v[0]];
			nf.v[1] = vclones[nf.v[1]];
			nf.v[2] = vclones[nf.v[2]];
			nfaces.Append(1,&nf,10);
			tdelta.nfaces.Append(1,&nf,10);
			
			// Clone the TV face
			if (mesh->tvFace) {
				TVFace tvf = mesh->tvFace[i];
				tdelta.nTVfaces.Append(1,&tvf,10);
				ntvfaces.Append(1,&tvf,10);
				}
			}
		}
	tdelta.nfaces.Shrink();
	tdelta.nverts.Shrink();
	tdelta.nTVfaces.Shrink();

	// Copy in the new verts	
	mesh->vertSel.SetSize(numv+nverts.Count());
	Point3 *verts = new Point3[numv + nverts.Count()];
	for (i=0; i<numv; i++) {
		verts[i] = mesh->verts[i];
		}
	for (; i<numv + nverts.Count(); i++) {		
		verts[i] = nverts[i-numv];
		}
	delete[] mesh->verts;
	mesh->verts = verts;
	mesh->numVerts += nverts.Count();	
	vdelta.SetSize(mesh->numVerts,FALSE);

	// Copy in new faces.
	mesh->faceSel.ClearAll();
	mesh->faceSel.SetSize(numf+nfaces.Count());
	mesh->edgeSel.SetSize((numf+nfaces.Count())*3);
	Face *faces = new Face[numf + nfaces.Count()];
	for (i=0; i<numf; i++) {
		faces[i] = mesh->faces[i];
		}
	for (;i<numf + nfaces.Count(); i++) {
		faces[i] = nfaces[i-numf];
		mesh->faceSel.Set(i);
		}
	delete[] mesh->faces;
	mesh->faces = faces;
	mesh->numFaces += nfaces.Count();

	if (mesh->tvFace) {
		TVFace *tvfaces = new TVFace[numf + ntvfaces.Count()];
		for (i=0; i<numf; i++) {
			tvfaces[i] = mesh->tvFace[i];
			}
		for (;i<numf + ntvfaces.Count(); i++) {
			tvfaces[i] = ntvfaces[i-numf];			
			}
		delete[] mesh->tvFace;
		mesh->tvFace = tvfaces;		
		}

	vsel = mesh->vertSel;
	esel = mesh->edgeSel;
	fsel = mesh->faceSel;

	mesh->InvalidateEdgeList();
	mesh->InvalidateGeomCache();
	}

void EditMeshData::MakeSelPlanar(Mesh *mesh)
	{
	int numf = mesh->getNumFaces();
	int numv = mesh->getNumVerts();
	Point3 norm(0,0,0);
	Point3 cent(0,0,0);
	float proj;
	int sel = 0;
	BitArray set;
	set.SetSize(numv);

	// Calculate the center of all faces and the average normal	
	for (int i=0; i<numf; i++) {
		if (mesh->faceSel[i]) {
			Face *f = &mesh->faces[i];
			Point3 v1 = mesh->verts[f->v[1]] - mesh->verts[f->v[0]];
			Point3 v2 = mesh->verts[f->v[2]] - mesh->verts[f->v[1]];
			norm += v1^v2;
			cent += mesh->verts[f->v[0]];
			cent += mesh->verts[f->v[1]];
			cent += mesh->verts[f->v[2]];
			set.Set(f->v[0]);
			set.Set(f->v[1]);
			set.Set(f->v[2]);
			sel++;
			}
		}
	if (!sel) return;
	cent /= float(sel*3);
	norm = Normalize(norm);

	// Project the center vector onto the normal
	proj = DotProd(norm,cent);

	// move the vertices onto the plane
	for (i=0; i<numv; i++) {
		if (set[i]) {
			Point3 delta = (proj - DotProd(mesh->verts[i],norm)) * norm;
			mesh->verts[i] += delta;
			vdelta.Move(0,i,delta);
			}
		}
	}


void EditMeshData::FaceCenterTessellate(Mesh *mesh)
	{
	int numf = mesh->getNumFaces();
	int numv = mesh->getNumVerts();
	Point3Tab	nverts;
	Point3Tab	ntverts;
	Tab<Face> 	nfaces;
	Tab<TVFace> ntvfaces;

	for (int i=0; i<numf; i++) {
		if (fsel[i]) {
			Face *f = &mesh->faces[i];
			Point3 pt;
			DWORD n = numv + nverts.Count();

			// Make the new point
			pt = (mesh->verts[f->v[0]] + 
			      mesh->verts[f->v[1]] +
				  mesh->verts[f->v[2]])/3.0f;
			nverts.Append(1,&pt,10);
			tdelta.nverts.Append(1,&pt,10);

			// Add two faces and modifiy the existing face
			Face nf = *f;			
			nf.v[1] = n;
			nf.setEdgeVisFlags(1,1,1);
			nfaces.Append(1,&nf,10);
			tdelta.nfaces.Append(1,&nf,10);

			nf = *f;			
			nf.v[0] = f->v[1];
			nf.v[1] = f->v[2];
			nf.v[2] = n;
			nf.setEdgeVisFlags(1,1,1);
			nfaces.Append(1,&nf,10);
			tdelta.nfaces.Append(1,&nf,10);
						
			f->setEdgeVisFlags(f->getEdgeVis(0),0,0);
			f->v[2] = n;
			tdelta.ModifyFace((DWORD)i,*f);
			
			// Do uvs
			if (mesh->tvFace) {
				DWORD tn = mesh->numTVerts + ntverts.Count();
				TVFace *tf = &mesh->tvFace[i];
				pt = (mesh->tVerts[tf->t[0]] +
					  mesh->tVerts[tf->t[1]] +
					  mesh->tVerts[tf->t[2]])/3.0f;
				ntverts.Append(1,&pt,10);
				tdelta.nTverts.Append(1,&pt,10);
				
				TVFace ntf1(tf->t[0],tn,tf->t[2]);
				ntvfaces.Append(1,&ntf1,10);
				tdelta.nTVfaces.Append(1,&ntf1,10);

				TVFace ntf2(tf->t[1],tf->t[2],tn);
				ntvfaces.Append(1,&ntf2,10);
				tdelta.nTVfaces.Append(1,&ntf2,10);

				tf->t[2] = tn;
				tdelta.ModifyTVFace((DWORD)i,*tf);
				}
			}
		}
	tdelta.nfaces.Shrink();
	tdelta.nTVfaces.Shrink();
	tdelta.nverts.Shrink();
	tdelta.nTverts.Shrink();

	// Copy in the new verts	
	mesh->vertSel.SetSize(numv+nverts.Count());
	Point3 *verts = new Point3[numv + nverts.Count()];
	for (i=0; i<numv; i++) {
		verts[i] = mesh->verts[i];
		}
	for (; i<numv + nverts.Count(); i++) {		
		verts[i] = nverts[i-numv];
		}
	delete[] mesh->verts;
	mesh->verts = verts;
	mesh->numVerts += nverts.Count();	
	vdelta.SetSize(mesh->numVerts,FALSE);

	// New tverts
	if (ntverts.Count()) {		
		Point3 *tverts = new Point3[mesh->numTVerts + ntverts.Count()];
		for (i=0; i<mesh->numTVerts; i++) {
			tverts[i] = mesh->tVerts[i];
			}
		for (; i<mesh->numTVerts + ntverts.Count(); i++) {
			tverts[i] = ntverts[i-mesh->numTVerts];
			}	
		delete[] mesh->tVerts;
		mesh->tVerts = tverts;
		mesh->numTVerts += ntverts.Count();		
		}

	// Copy in new faces.	
	mesh->faceSel.SetSize(numf+nfaces.Count());
	mesh->edgeSel.SetSize((numf+nfaces.Count())*3);
	Face *faces = new Face[numf + nfaces.Count()];
	for (i=0; i<numf; i++) {
		faces[i] = mesh->faces[i];
		}
	for (;i<numf + nfaces.Count(); i++) {
		faces[i] = nfaces[i-numf];
		mesh->faceSel.Set(i);
		}
	delete[] mesh->faces;
	mesh->faces = faces;
	mesh->numFaces += nfaces.Count();

	// new TV faces
	if (mesh->tvFace) {
		TVFace *tvfaces = new TVFace[numf + ntvfaces.Count()];
		for (i=0; i<numf; i++) {
			tvfaces[i] = mesh->tvFace[i];
			}
		for (;i<numf + ntvfaces.Count(); i++) {
			tvfaces[i] = ntvfaces[i-numf];			
			}
		delete[] mesh->tvFace;
		mesh->tvFace = tvfaces;		
		}

	vsel = mesh->vertSel;
	esel = mesh->edgeSel;
	fsel = mesh->faceSel;

	mesh->InvalidateEdgeList();
	mesh->InvalidateGeomCache();
	}



void EditMeshData::EdgeTessellate(Mesh *mesh, AdjEdgeList *ae, AdjFaceList *af, float tens)
	{
	int numf = mesh->getNumFaces();
	int numv = mesh->getNumVerts();
	Point3Tab	nverts;
	Point3Tab	ntverts;
	Tab<Face> 	nfaces;
	Tab<TVFace> ntvfaces;
	Tab<MEdge> &edges = ae->edges;
	DWORDTab edgeVerts;	
	BitArray newsel;

	newsel.SetSize(numf);
	edgeVerts.SetCount(edges.Count());
	
	// First subdivide all the edges of selected faces
	for (int i=0; i<edges.Count(); i++) {
		if (edges[i].AFaceSelected(fsel)) {
			// Make a new vert
			Point3 pt = edges[i].ButterFlySubdivide(mesh,af,tens);
			nverts.Append(1,&pt,10);
			tdelta.nverts.Append(1,&pt,10);
			
			// Record where this vert is in the final vert array
			edgeVerts[i] = numv + nverts.Count()-1;
		} else {
			edgeVerts[i] = UNDEFINED;
			}
		}	
	tdelta.nverts.Shrink();

	// Tessellate selected faces
	for (i=0; i<numf; i++) {
		Face *f = &mesh->faces[i];
		if (fsel[i]) {
			tessellate:
						
			int e0 = ae->FindEdge(f->v[0],f->v[1]);
			int e1 = ae->FindEdge(f->v[1],f->v[2]);
			int e2 = ae->FindEdge(f->v[2],f->v[0]);
			//assert(e0>=0 && e1>=0 && e2>=0);
			if (e0<0 || e1<0 || e2<0) continue;

			if (edgeVerts[e0]!=UNDEFINED) continue;
			if (edgeVerts[e1]!=UNDEFINED) continue;
			if (edgeVerts[e2]!=UNDEFINED) continue;

			// Add three new faces
			Face nf  = *f;
			nf.v[0]  = f->v[0];
			nf.v[1]  = edgeVerts[e0];
			nf.v[2]  = edgeVerts[e2];
			//nf.flags = EDGE_ALL;
			nf.setEdgeVisFlags(1,1,1);
			nfaces.Append(1,&nf,10);
			tdelta.nfaces.Append(1,&nf,10);

			nf  = *f;
			nf.v[0]  = f->v[1];
			nf.v[1]  = edgeVerts[e1];
			nf.v[2]  = edgeVerts[e0];
			//nf.flags = EDGE_ALL;
			nf.setEdgeVisFlags(1,1,1);
			nfaces.Append(1,&nf,10);
			tdelta.nfaces.Append(1,&nf,10);

			nf  = *f;
			nf.v[0]  = f->v[2];
			nf.v[1]  = edgeVerts[e2];
			nf.v[2]  = edgeVerts[e1];
			//nf.flags = EDGE_ALL;
			nf.setEdgeVisFlags(1,1,1);
			nfaces.Append(1,&nf,10);
			tdelta.nfaces.Append(1,&nf,10);

			// Make this face the center face
			f->v[0] = edgeVerts[e0];
			f->v[1] = edgeVerts[e1];
			f->v[2] = edgeVerts[e2];
			tdelta.ModifyFace((DWORD)i,*f);

			// Make new tv faces and tverts
			if (mesh->tvFace) {
				TVFace *tf = &mesh->tvFace[i];
				Point3 v0 = mesh->tVerts[tf->t[0]];
				Point3 v1 = mesh->tVerts[tf->t[1]];
				Point3 v2 = mesh->tVerts[tf->t[2]];
				int i0, i1, i2;

				// First generate three new verts
				Point3 uv = (v0+v1)/2.0f;
				ntverts.Append(1,&uv,10);
				tdelta.nTverts.Append(1,&uv,10);
				i0 = mesh->numTVerts + ntverts.Count()-1;

				uv = (v1+v2)/2.0f;
				ntverts.Append(1,&uv,10);
				tdelta.nTverts.Append(1,&uv,10);
				i1 = mesh->numTVerts + ntverts.Count()-1;

				uv = (v0+v2)/2.0f;
				ntverts.Append(1,&uv,10);
				tdelta.nTverts.Append(1,&uv,10);
				i2 = mesh->numTVerts + ntverts.Count()-1;

				// And three new faces
				TVFace ntf1(tf->t[0],i0,i2);
				ntvfaces.Append(1,&ntf1,10);
				tdelta.nTVfaces.Append(1,&ntf1,10);

				TVFace ntf2(tf->t[1],i1,i0);
				ntvfaces.Append(1,&ntf2,10);
				tdelta.nTVfaces.Append(1,&ntf2,10);

				TVFace ntf3(tf->t[2],i2,i1);
				ntvfaces.Append(1,&ntf3,10);
				tdelta.nTVfaces.Append(1,&ntf3,10);

				// Modify the orignal
				tf->t[0] = i0;
				tf->t[1] = i1;
				tf->t[2] = i2;
				tdelta.ModifyTVFace((DWORD)i,*tf);
				}

		} else {
			// See if it's adjacent to a selected face.
			DWORD *fa = (*af)[i].f;
			int ct=0;
			for (int j=0; j<3; j++) {
				if (fa[j] != UNDEFINED && fsel[fa[j]]) ct++;
				}
			if (!ct) continue;
				

			if (ct==1) {
				// One selected adjacent face
				for (int j=0; j<3; j++) if (fa[j] != UNDEFINED && fsel[fa[j]]) break;
				assert(j<3);								
				int e = ae->FindEdge(f->v[j],f->v[(j+1)%3]);				
				//assert(e>=0);
				if (e<0) continue;

				// Add a new face
				Face nf  = *f;
				nf.v[j]  = edgeVerts[e];
				//nf.flags = EDGE_ALL;
				nf.setEdgeVisFlags(1,1,1);
				nfaces.Append(1,&nf,10);
				tdelta.nfaces.Append(1,&nf,10);

				// Remap this face
				f->v[(j+1)%3] = edgeVerts[e];
				tdelta.ModifyFace((DWORD)i,*f);

				// Select this face
				newsel.Set(i);
				mesh->faceSel.Set(i);

				// Make new tv faces and tverts
				if (mesh->tvFace) {
					TVFace *tf = &mesh->tvFace[i];
					Point3 v0 = mesh->tVerts[tf->t[j]];
					Point3 v1 = mesh->tVerts[tf->t[(j+1)%3]];					
					
					// First generate a new vert
					Point3 uv = (v0+v1)/2.0f;
					ntverts.Append(1,&uv,10);
					tdelta.nTverts.Append(1,&uv,10);
					int ix = mesh->numTVerts + ntverts.Count()-1;
	
					// Add a new face
					TVFace ntf(tf->t[0],tf->t[1],tf->t[2]);
					ntf.t[j] = ix;
					ntvfaces.Append(1,&ntf,10);
					tdelta.nTVfaces.Append(1,&ntf,10);
					
					// Modify the orignal
					tf->t[(j+1)%3] = ix;					
					tdelta.ModifyTVFace((DWORD)i,*tf);
					}

			} else
			if (ct==2) {
				// Two selected adjacent faces
				int e[2], c=0, not;				
				for (int j=0; j<3; j++) {
					if (fa[j] != UNDEFINED && fsel[fa[j]]) {
						e[c] = ae->FindEdge(f->v[j],f->v[(j+1)%3]);
						c++;						
					} else {
						not = j;
						}
					}
				//assert(c==2 && e[0]>=0 && e[1]>=0);
				if (!(c==2 && e[0]>=0 && e[1]>=0)) continue;

				// If they're not consecutive, swap
				if (not==1) {
					DWORD temp = e[0];
					e[0] = e[1];
					e[1] = temp;
					}

				// Add two new faces
				Face nf  = *f;
				nf.v[0]  = f->v[not];
				nf.v[1]  = f->v[(not+1)%3];
				nf.v[2]  = edgeVerts[e[1]];
				//nf.flags = EDGE_ALL;
				nf.setEdgeVisFlags(1,1,1);
				nfaces.Append(1,&nf,10);
				tdelta.nfaces.Append(1,&nf,10);

				nf  = *f;
				nf.v[0]  = f->v[(not+1)%3];
				nf.v[1]  = edgeVerts[e[0]];
				nf.v[2]  = edgeVerts[e[1]];
				//nf.flags = EDGE_ALL;
				nf.setEdgeVisFlags(1,1,1);
				nfaces.Append(1,&nf,10);
				tdelta.nfaces.Append(1,&nf,10);

				// Remap this face
				f->v[not] = edgeVerts[e[1]];
				f->v[(not+1)%3] = edgeVerts[e[0]];
				tdelta.ModifyFace((DWORD)i,*f);

				// Select this face
				mesh->faceSel.Set(i);
				newsel.Set(i);

				// Make new tv faces and tverts
				if (mesh->tvFace) {
					TVFace *tf = &mesh->tvFace[i];
					Point3 v[] = {mesh->tVerts[tf->t[0]],
								  mesh->tVerts[tf->t[1]],
								  mesh->tVerts[tf->t[2]]};
					int i0, i1;

					// First generate two new verts
					Point3 uv = (v[(not+1)%3] + v[(not+2)%3])/2.0f;
					ntverts.Append(1,&uv,10);
					tdelta.nTverts.Append(1,&uv,10);
					i0 = mesh->numTVerts + ntverts.Count()-1;

					uv = (v[(not+2)%3] + v[not])/2.0f;
					ntverts.Append(1,&uv,10);
					tdelta.nTverts.Append(1,&uv,10);
					i1 = mesh->numTVerts + ntverts.Count()-1;

					// Add a two new faces
					TVFace ntf1(tf->t[not],tf->t[(not+1)%3],i1);					
					ntvfaces.Append(1,&ntf1,10);
					tdelta.nTVfaces.Append(1,&ntf1,10);
					
					TVFace ntf2(tf->t[(not+1)%3],i0,i1);					
					ntvfaces.Append(1,&ntf2,10);
					tdelta.nTVfaces.Append(1,&ntf2,10);
					
					// Modify the orignal
					tf->t[not] = i1;
					tf->t[(not+1)%3] = i0;
					tdelta.ModifyTVFace((DWORD)i,*tf);
					}
						
			} else { 
				// Select this face
				mesh->faceSel.Set(i);
				newsel.Set(i);
				goto tessellate;
				}
			}
		}
	fsel |= newsel;	
	tdelta.nfaces.Shrink();	
	edgeVerts.Resize(0);

	// Copy in the new verts	
	mesh->vertSel.SetSize(numv+nverts.Count());
	Point3 *verts = new Point3[numv + nverts.Count()];
	for (i=0; i<numv; i++) {
		verts[i] = mesh->verts[i];
		}
	for (; i<numv + nverts.Count(); i++) {		
		verts[i] = nverts[i-numv];
		}
	delete[] mesh->verts;
	mesh->verts = verts;
	mesh->numVerts += nverts.Count();	
	vdelta.SetSize(mesh->numVerts,FALSE);

	// New tverts
	if (ntverts.Count()) {		
		Point3 *tverts = new Point3[mesh->numTVerts + ntverts.Count()];
		for (i=0; i<mesh->numTVerts; i++) {
			tverts[i] = mesh->tVerts[i];
			}
		for (; i<mesh->numTVerts + ntverts.Count(); i++) {
			tverts[i] = ntverts[i-mesh->numTVerts];
			}	
		delete[] mesh->tVerts;
		mesh->tVerts = tverts;
		mesh->numTVerts += ntverts.Count();		
		}

	// Copy in new faces.	
	mesh->faceSel.SetSize(numf+nfaces.Count());
	mesh->edgeSel.SetSize((numf+nfaces.Count())*3);
	Face *faces = new Face[numf + nfaces.Count()];
	for (i=0; i<numf; i++) {
		faces[i] = mesh->faces[i];
		}
	for (;i<numf + nfaces.Count(); i++) {
		faces[i] = nfaces[i-numf];
		mesh->faceSel.Set(i);
		}
	delete[] mesh->faces;
	mesh->faces = faces;
	mesh->numFaces += nfaces.Count();

	// new TV faces
	if (mesh->tvFace) {
		TVFace *tvfaces = new TVFace[numf + ntvfaces.Count()];
		for (i=0; i<numf; i++) {
			tvfaces[i] = mesh->tvFace[i];
			}
		for (;i<numf + ntvfaces.Count(); i++) {
			tvfaces[i] = ntvfaces[i-numf];			
			}
		delete[] mesh->tvFace;
		mesh->tvFace = tvfaces;		
		}

	vsel = mesh->vertSel;
	esel = mesh->edgeSel;
	fsel = mesh->faceSel;

	mesh->InvalidateEdgeList();
	mesh->InvalidateGeomCache();
	}

void EditMeshData::ElemExplodeFaces(Mesh *mesh,float thresh,AdjFaceList *af)
	{
	FaceClusterList clust(mesh,*af,thresh);
	int numv = mesh->getNumVerts();
	int numf = mesh->getNumFaces();
	Point3Tab nverts;
	DWORDTab vmap;
	BitArray set;

	set.SetSize(numv);
	vmap.SetCount(numv);	

	for (DWORD c=0; c<clust.count; c++) {

		// Clear the map and vert tag list.
		for (int i=0; i<numv; i++) vmap[i] = i;
		set.ClearAll();

		// Mark all verts that need to be cloned.
		for (i=0; i<numf; i++) {
			if (clust[i]==c) {
				set.Set(mesh->faces[i].v[0]);
				set.Set(mesh->faces[i].v[1]);
				set.Set(mesh->faces[i].v[2]);
				}
			}

		// Clone marked verts
		for (i=0; i<numv; i++) {
			if (set[i]) {
				vmap[i] = numv + nverts.Count();
				nverts.Append(1,&mesh->verts[i],10);
				tdelta.nverts.Append(1,&mesh->verts[i],10);
				}
			}

		// Remap faces.
		for (i=0; i<numf; i++) {
			if (clust[i]==c) {
				Face *f = &mesh->faces[i];
				f->v[0] = vmap[f->v[0]];
				f->v[1] = vmap[f->v[1]];
				f->v[2] = vmap[f->v[2]];
				tdelta.ModifyFace((DWORD)i,*f);
				}
			}
		}
	
	// Copy in the new verts	
	mesh->vertSel.SetSize(numv+nverts.Count());
	Point3 *verts = new Point3[numv + nverts.Count()];
	for (int i=0; i<numv; i++) {
		verts[i] = mesh->verts[i];
		}
	for (; i<numv + nverts.Count(); i++) {		
		verts[i] = nverts[i-numv];
		}
	delete[] mesh->verts;
	mesh->verts = verts;
	mesh->numVerts += nverts.Count();	
	vdelta.SetSize(mesh->numVerts,FALSE);
	vsel     = mesh->vertSel;	
	mesh->vertHide.SetSize(mesh->numVerts,1);
	mesh->InvalidateEdgeList();
	mesh->InvalidateGeomCache();
	}

void EditMeshData::ObjExplodeFaces(INode *node,TSTR &name,IObjParam *ip,
			Mesh *mesh,float thresh,AdjFaceList *af)
	{
	FaceClusterList clust(mesh,*af,thresh);
	int numv  = mesh->getNumVerts();
	int numtv = mesh->numTVerts;
	int numf  = mesh->getNumFaces();
	Point3Tab nverts;	
	Point3Tab ntverts;
	Tab<Face> nfaces;
	Tab<TVFace> ntvfaces;
	BitArray set, tset;
	DWORDTab vmap, tvmap;

	set.SetSize(numv);
	vmap.SetCount(numv);
	if (mesh->tvFace) {
		tset.SetSize(numtv);
		tvmap.SetCount(numtv);
		}

	for (DWORD c=0; c<clust.count; c++) {
		nverts.Resize(0);
		nfaces.Resize(0);
		ntvfaces.Resize(0);

		// Mark all verts that need to be cloned.
		set.ClearAll();
		//for (int i=0; i<numv; i++) vmap[i] = i;
		for (int i=0; i<numv; i++) vmap[i] = 0xffffffff;
		
		if (mesh->tvFace) {
			tset.ClearAll();
			//for (int i=0; i<numtv; i++) tvmap[i] = i;
			for (int i=0; i<numtv; i++) tvmap[i] = 0xffffffff;
			}

		for (i=0; i<numf; i++) {
			if (clust[i]==c) {
				set.Set(mesh->faces[i].v[0]);
				set.Set(mesh->faces[i].v[1]);
				set.Set(mesh->faces[i].v[2]);
				if (mesh->tvFace) {									
					tset.Set(mesh->tvFace[i].t[0]);
					tset.Set(mesh->tvFace[i].t[1]);
					tset.Set(mesh->tvFace[i].t[2]);
					}
				}
			}

		// Clone marked verts
		for (i=0; i<numv; i++) {
			if (set[i]) {				
				vmap[i] = nverts.Count();
				nverts.Append(1,&mesh->verts[i],10);
				}
			}
		if (mesh->tvFace) {
			for (i=0; i<numtv; i++) {
				if (tset[i]) {				
					tvmap[i] = ntverts.Count();
					ntverts.Append(1,&mesh->tVerts[i],10);
					}
				}
			}

		// Create faces.
		for (i=0; i<numf; i++) {
			if (clust[i]==c) {
				Face f = mesh->faces[i];
				f.v[0] = vmap[f.v[0]];
				f.v[1] = vmap[f.v[1]];
				f.v[2] = vmap[f.v[2]];
				nfaces.Append(1,&f,10);				
				if (mesh->tvFace) {
					TVFace tf = mesh->tvFace[i];
					tf.t[0] = tvmap[tf.t[0]];
					tf.t[1] = tvmap[tf.t[1]];
					tf.t[2] = tvmap[tf.t[2]];
					ntvfaces.Append(1,&tf,10);
					}
				}
			}
		
		TriObject *newObj = CreateNewTriObject();
		Mesh *newMesh = &newObj->mesh;
		newMesh->setNumVerts(nverts.Count());
		newMesh->setNumFaces(nfaces.Count());
		if (mesh->tvFace) {
			newMesh->setNumTVerts(ntverts.Count());
			newMesh->setNumTVFaces(ntvfaces.Count());
			for (i=0; i<ntverts.Count(); i++) {
				newMesh->tVerts[i] = ntverts[i];
				}
			for (i=0; i<ntvfaces.Count(); i++) {
				newMesh->tvFace[i] = ntvfaces[i];
				}
			}

		for (i=0; i<nverts.Count(); i++) {
			newMesh->verts[i] = nverts[i];
			}
		for (i=0; i<nfaces.Count(); i++) {
			newMesh->faces[i] = nfaces[i];
			}
		newMesh->InvalidateGeomCache();

		// Add the object to the scene. Give it the given name
		// and set its transform to ours.
		INode *nnode = ip->CreateObjectNode(newObj);
		Matrix3 ntm = node->GetNodeTM(ip->GetTime());
		TSTR uname = name;
		ip->MakeNameUnique(uname);
		nnode->SetName(uname);
		nnode->SetNodeTM(ip->GetTime(),ntm);
		nnode->FlagForeground(ip->GetTime(),FALSE);
		}
	
	// Save verts and faces not in a cluster.
	BitArray fset, vset;
	fset.SetSize(numf);
	vset.SetSize(numv);
	for (int i=0; i<numf; i++) {
		if (clust[i]==UNDEFINED) {
			fset.Set(i);
			vset.Set(mesh->faces[i].v[0]);
			vset.Set(mesh->faces[i].v[1]);
			vset.Set(mesh->faces[i].v[2]);
			}
		}
	
	// Invert the sets. These are the verts and faces we can delete.	
	fset = ~fset;
	vset = ~vset;
	DeleteFaceSet(mesh,fset);
	DeleteVertSet(mesh,vset);
	}


void EditMeshData::AutoSmooth(
		Mesh *mesh,
		AdjFaceList &af,
		AdjEdgeList &ae,
		float angle)
	{
	FaceClusterList clust(mesh,af,angle);
	int *grp = new int[clust.count];
	
	// For each cluster, pick the first unused bit
	for (DWORD i=0; i<clust.count; i++) {
		int bit = 0;
		 DWORD used = 0;
		
		// Go through the faces in this cluster and see which
		// bits are used		
		for (int j=0; j<mesh->getNumFaces(); j++) {
			if (!mesh->faceSel[j]) continue;

			if (clust[j]==i) {				
				int c;				
							
				for (int k=0; k<3; k++) {					
 					/*
					if (af[j].f[k]!=UNDEFINED) c = clust[af[j].f[k]];
					else continue;
 					if (c!=UNDEFINED && (DWORD)c<i) {
						used |= 1<<(grp[c]);
						}
					*/
					// New way: We gotta look at all faces that share
					// a vertex even if they are not adjacent across an edge.
					DWORD v = mesh->faces[j].v[k];
					for (int l=0; l<ae[v].Count(); l++) {
						MEdge edge = ae.edges[ae[v][l]];
						if (edge.f[0]!=UNDEFINED && edge.f[0]!=(DWORD)j) {
							c = clust[edge.f[0]];
							if (c!=UNDEFINED && (DWORD)c<i) {
								used |= 1<<(grp[c]);
								}
							}
						if (edge.f[1]!=UNDEFINED && edge.f[1]!=(DWORD)j) {
							c = clust[edge.f[1]];
							if (c!=UNDEFINED && (DWORD)c<i) {
								used |= 1<<(grp[c]);
								}
							}
						}
					}				
				}			
			}

		// Pick the first free bit
		for (j=0; j<32; j++) {
			if (!((1<<j) & used)) {
				bit = j;
				break;
				}
			}

		// We've got a smooth bit for this cluster!
		grp[i] = bit;
		}
	
	// Now we've got bits for each cluster ID. Assign the faces.
	for (int j=0; j<mesh->getNumFaces(); j++) {
		if (!mesh->faceSel[j]) continue;
		
		assert(clust[j]!=UNDEFINED);		
		mesh->faces[j].smGroup = (1<<grp[clust[j]]);
		tdelta.SetSmoothGroup((DWORD)j,(1<<grp[clust[j]]),0xffffffff);				
		}
	
	delete[] grp;
	}


void EditMeshData::FlipNormal(Mesh *mesh,DWORD face)
	{
	DWORD vis  = 0;
	if (mesh->faces[face].flags&EDGE_A) vis |= EDGE_A;
	if (mesh->faces[face].flags&EDGE_B) vis |= EDGE_C;
	if (mesh->faces[face].flags&EDGE_C) vis |= EDGE_B;
	DWORD temp = mesh->faces[face].v[0];
	mesh->faces[face].v[0] = mesh->faces[face].v[1];
	mesh->faces[face].v[1] = temp;				
	mesh->faces[face].flags &= ~EDGE_ALL;
	mesh->faces[face].flags |= vis;
	tdelta.ModifyFace((DWORD)face,mesh->faces[face]);
	tdelta.SetEdgeVisibility((DWORD)face,vis);
	if (mesh->tvFace) {		
		temp = mesh->tvFace[face].t[0];
		mesh->tvFace[face].t[0] = mesh->tvFace[face].t[1];
		mesh->tvFace[face].t[1] = temp;
		tdelta.ModifyTVFace((DWORD)face,mesh->tvFace[face]);		
		}
	}


#define POINTS_IN		1
#define POINTS_OUT		2
#define POINTS_MIXED	3

static int WhichSide(Mesh &mesh,int f1, int f2)
	{
	Point3 v1[3], v2[3], norm;
	float dir;
	int res;

	for (int i=0; i<3; i++) {
		v1[i] = mesh.verts[ mesh.faces[f1].v[i] ];
		v2[i] = mesh.verts[ mesh.faces[f2].v[i] ];
		}

	norm = Normalize((v1[1]-v1[0])^(v1[2]-v1[1]));
	for (i=0; i<3; i++) {
		dir = DotProd(norm,v2[i]-v1[0]);
		if (!i) {
			if (dir<0.01f) res = POINTS_IN;
			else res = POINTS_OUT;
		} else {
			if (dir<0 && res==POINTS_OUT) return POINTS_MIXED;
			if (dir>0 && res==POINTS_IN) return POINTS_MIXED;
			}
		}

	return res;
	}

int UnifyTestFace(Mesh &mesh,int face,int e,FaceElementList &elem)
	{
	int res=-1;

	for (int i=0; i<mesh.getNumFaces(); i++) {
		if (i==face) continue;
		if (elem[i]==(DWORD)e) {
			if (res==-1) res = WhichSide(mesh,face,i);
			else if (WhichSide(mesh,face,i)!=res) return POINTS_MIXED;
			}
		}
	
	return res;
	}

int UnifySimpleFaceTest(Mesh &mesh,int face,int e,FaceElementList &elem)
	{
	Point3 cent(0,0,0);
	int count = 0;
	for (int i=0; i<mesh.getNumFaces(); i++) {
		if (elem[i]==(DWORD)i) {
			for (int k=0; k<3; k++) cent += mesh.verts[mesh.faces[i].v[k]];
			count += 3;
			}
		}	
	if (count) cent /= (float)count;

	Point3 v0, v1, v2, norm;
	v0 = mesh.verts[mesh.faces[face].v[0]];
	v1 = mesh.verts[mesh.faces[face].v[1]];
	v2 = mesh.verts[mesh.faces[face].v[2]];
	norm = Normalize((v1-v0)^(v2-v1));
	float dir = DotProd(norm,cent-v0);
	if (dir<0) return POINTS_IN;
	else return POINTS_OUT;	
	}

static int Direction(Face &face,DWORD v1, DWORD v2) 
	{
	int i1, i2;
	for (int i=0; i<3; i++) {
		if (face.v[i]==v1) i1=i;
		if (face.v[i]==v2) i2=i;
		}
	return (i1+1)%3==i2;	
	}

BOOL ShouldIFlip(Mesh &mesh,DWORD f1,DWORD f2)
	{
	int v1[3], v2[3];
	int c=0;
	for (int i=0; i<3; i++) {
		for (int j=0; j<3; j++) {
			if (mesh.faces[f1].v[i]==mesh.faces[f2].v[j]) {
				v1[c]=i;
				v2[c]=j;
				c++;
				if (c>=3) return FALSE;
				}
			}
		}
	if (c==2) { // should always be the case
		if (Direction(mesh.faces[f1],mesh.faces[f1].v[v1[0]],mesh.faces[f1].v[v1[1]]) ==
			Direction(mesh.faces[f2],mesh.faces[f2].v[v2[0]],mesh.faces[f2].v[v2[1]])) {
			return TRUE;
		} else {
			return FALSE;
			}
		}
	return FALSE;
	}

void EditMeshData::PropogateFacing(
		Mesh &mesh,int face,AdjFaceList &af,
		BitArray &done,BOOL bias)
	{		
	Tab<int> stack1;
	Tab<BOOL> stack2;
	stack1.Append(1,&face);
	stack2.Append(1,&bias);

	while (stack1.Count()) {
		BOOL b=1;
		// Pop the top of the stack
		face = stack1[stack1.Count()-1];
		bias = stack2[stack2.Count()-1];
		stack1.Delete(stack1.Count()-1,1);
		stack2.Delete(stack2.Count()-1,1);

		Face f1 = mesh.faces[face];
		for (int k=0; k<3; k++) {
			if (af[face].f[k]!=UNDEFINED && !done[af[face].f[k]]) {			
				done.Set(af[face].f[k]);
				if (bias==ShouldIFlip(mesh,face,af[face].f[k])) {
					if (mesh.faceSel[af[face].f[k]]) {
						mesh.FlipNormal(af[face].f[k]);
					} else {
						b=0;
						}
					}
				
				// Push adjacent face on the stack
				int aaf = af[face].f[k];
				stack1.Append(1,&aaf,10);
				stack2.Append(1,&b,10);
				}
			}
		}

	/*
	BOOL b=1;
	Face f1 = mesh.faces[face];
	for (int k=0; k<3; k++) {
		if (af[face].f[k]!=UNDEFINED && !done[af[face].f[k]]) {			
			done.Set(af[face].f[k]);
			if (bias==ShouldIFlip(mesh,face,af[face].f[k])) {
				if (mesh.faceSel[af[face].f[k]]) {
					FlipNormal(&mesh,af[face].f[k]);
				} else {
					b=0;
					}
				}
			PropogateFacing(mesh,af[face].f[k],af,done,b);
			}
		}
	*/
	}

void EditMeshData::UnifyNormals(Mesh *mesh,AdjFaceList &af)
	{
	FaceElementList elem(*mesh,af);
	int tried;
	BitArray done;
	done.SetSize(mesh->getNumFaces());	
	
	for (int i=0; (DWORD)i<elem.count; i++) {
		tried = -1;		
		for (int j=0; j<mesh->getNumFaces(); j++) {
			if (elem[j]!=(DWORD)i) continue;
			if (!mesh->faceSel[j]) continue;
			tried = j;
			switch (UnifyTestFace(*mesh,j,i,elem)) {
				case POINTS_IN:					
					PropogateFacing(*mesh,j,af,done);
					goto nextElem;

				case POINTS_OUT:					
					FlipNormal(mesh,(DWORD)j);
					PropogateFacing(*mesh,j,af,done);					
					goto nextElem;
				}
			}
		
		// If we didn't find a facing and we tried, do it another way
		if (tried!=-1) {
			switch (UnifySimpleFaceTest(*mesh,tried,i,elem)) {
				case POINTS_IN:
					PropogateFacing(*mesh,tried,af,done);
					break;
				case POINTS_OUT:					
					FlipNormal(mesh,(DWORD)tried);
					PropogateFacing(*mesh,tried,af,done);					
					break;
				}
			}

		nextElem:;
		}	
	}


//-------------------------------------------------------------------------

void MeshDeleteVertices(Mesh &mesh,BitArray& set)
	{
	int j = 0;
	for (int i=0; i<mesh.getNumVerts(); i++) {
		if (set[i]) continue;		
		mesh.verts[j] = mesh.verts[i];		
		j++;
		}
	if (j!=mesh.getNumVerts()) {
		mesh.setNumVerts(j,TRUE);
		}
	}

void MeshDeleteFaces(Mesh &mesh,BitArray& set)
	{
	int j = 0;
	for (int i=0; i<mesh.getNumFaces(); i++) {
		if (set[i]) continue;		
		mesh.faces[j] = mesh.faces[i];		
		if (mesh.tvFace) {
			mesh.tvFace[j] = mesh.tvFace[i];
			}
		j++;
		}
	if (j!=mesh.getNumFaces()) {
		mesh.setNumFaces(j,TRUE);
		}
	}

void Point3TabDeleteSet(Point3Tab& tab,BitArray &set)
	{
	int j = 0;
	for (int i=0; i<tab.Count(); i++) {
		if (set[i]) continue;
		tab[j] = tab[i];
		j++;
		}
	if (j!=tab.Count()) tab.Resize(j);
	}

void BitArrayDeleteSet(BitArray &ba,BitArray &set,int m)
	{
	int j = 0;
	int s1 = set.GetSize(), s2 = ba.GetSize();
	int size = s1<s2 ? s1 : s2;

	for (int i=0; i<size; i++) {
		if (set[i]) continue;
		
		for (int k=0; k<m; k++) ba.Set(j*m+k,ba[i*m+k]);
		j++;
		}
	if (j*m!=ba.GetSize()) ba.SetSize(j*m,TRUE);
	}

DWORD GetFaceVertIndex(Face &f,DWORD v)
	{
	if (v==f.v[0]) return 0;
	if (v==f.v[1]) return 1;
	if (v==f.v[2]) return 2;
	return 0;
	}
