/**********************************************************************
 *<
	FILE: editmesh.cpp

	DESCRIPTION:  Edit Mesh OSM

	CREATED BY: Dan Silva & Rolf Berteig

	HISTORY: created 18 March, 1995

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

// in mods.cpp
extern HINSTANCE hInstance;

HWND                EditMeshMod::hEditMeshParams = NULL;
HWND                EditMeshMod::hParam2         = NULL;
IObjParam*          EditMeshMod::iObjParams      = NULL;
MoveModBoxCMode*    EditMeshMod::moveMode        = NULL;
RotateModBoxCMode*  EditMeshMod::rotMode 	     = NULL;
UScaleModBoxCMode*  EditMeshMod::uscaleMode      = NULL;
NUScaleModBoxCMode* EditMeshMod::nuscaleMode     = NULL;
SquashModBoxCMode*  EditMeshMod::squashMode      = NULL;
SelectModBoxCMode*  EditMeshMod::selectMode      = NULL;
ExtrudeCMode*       EditMeshMod::extrudeMode     = NULL;
CreateVertCMode*	EditMeshMod::createVertMode  = NULL;
WeldVertCMode*		EditMeshMod::weldVertMode    = NULL;
ObjAttachCMode*		EditMeshMod::attachObjMode   = NULL;
AttachPickMode*		EditMeshMod::attachPickMode  = NULL;
BuildFaceCMode*		EditMeshMod::buildFaceMode   = NULL;
TurnEdgeCMode*		EditMeshMod::turnEdgeMode    = NULL;
DivideEdgeCMode*	EditMeshMod::divideEdgeMode  = NULL;
BOOL               	EditMeshMod::inNormalMove    = FALSE;
BOOL               	EditMeshMod::inBuildFace     = FALSE;
BOOL               	EditMeshMod::faceUIValid     = FALSE;
float               EditMeshMod::normScale       = -1.0f;
ISpinnerControl*	EditMeshMod::amountSpin      = NULL;
ISpinnerControl*	EditMeshMod::tensSpin        = NULL;
ISpinnerControl*	EditMeshMod::planarSpin      = NULL;
ICustButton *		EditMeshMod::iSingle         = NULL;
ICustButton *		EditMeshMod::iPoly           = NULL;
ICustButton *		EditMeshMod::iElement        = NULL;
int                 EditMeshMod::selType         = SELTYPE_POLY;
float 				EditMeshMod::falloff         = 20.0f;
float 				EditMeshMod::pinch           = 0.0f;
float 				EditMeshMod::bubble          = 0.0f;
BOOL				EditMeshMod::selByVert       = FALSE;

static EditMeshClassDesc editMeshDesc;
extern ClassDesc* GetEditMeshModDesc() { return &editMeshDesc; }

void EditMeshClassDesc::ResetClassParams(BOOL fileReset)
	{
	EditMeshMod::normScale       = -1.0f;
	EditMeshMod::selType         = SELTYPE_POLY;
	EditMeshMod::falloff         = 20.0f;
	EditMeshMod::pinch           = 0.0f;
	EditMeshMod::bubble          = 0.0f;
	EditMeshMod::selByVert       = FALSE;
	ResetEditMeshUI();
	}

EditMeshDeleteEvent delEvent;


/*-------------------------------------------------------------------*/
// 
// Vertex deltas
//

void VertexDelta::SetSize(int size, BOOL clear)
	{
	if (clear) {
		deltas.Delete(0,deltas.Count());
		}
	if (deltas.Count() > size) {
		int diff = deltas.Count() - size;
		deltas.Delete( deltas.Count()-diff, diff );		
		}
	if (deltas.Count() < size) {
		int diff = size - deltas.Count();
		deltas.Resize( size );
		Point3 zero(Point3(0,0,0));
		for ( int i = 0; i < diff; i++ ) {
			deltas.Append(1,&zero);
			}		
		}
	if (size!=hide.GetSize()) hide.SetSize(size,!clear);	
	}


void VertexDelta::Move(TimeValue t,int i, const Point3& p) 
	{ 
	deltas[i] += p; 
	}

void VertexDelta::Set(TimeValue t,int i, Point3& p) 
	{ 
	deltas[i] = p; 
	}

Point3 VertexDelta::operator[](int i) 
	{ 
	return deltas[i]; 
	}


void SetupHideFlags(Mesh &mesh, BitArray &hide)
	{
	// We need to hide any vertices that are only used by hidden faces.
	// To do this, first we turn on hide flags for the three vertices of
	// hidden faces, and then turn off hide flags for the three vertices
	// of visible faces.
	mesh.vertHide.SetSize(mesh.getNumVerts());
	mesh.vertHide.ClearAll();
	for (int i=0; i<mesh.getNumFaces(); i++) {
		if (mesh.faces[i].Hidden()) {
			for (int j=0; j<3; j++)
				mesh.vertHide.Set(mesh.faces[i].v[j],TRUE);
			}
		}
	for (i=0; i<mesh.getNumFaces(); i++) {
		if (!mesh.faces[i].Hidden()) {
			for (int j=0; j<3; j++)
				mesh.vertHide.Set(mesh.faces[i].v[j],FALSE);
			}
		}
	
	// Apply hide bits.
	if (mesh.vertHide.GetSize()!=hide.GetSize())
		mesh.vertHide.SetSize(hide.GetSize());	
	for (i=0; i<hide.GetSize(); i++) {
		if (hide[i]) mesh.vertHide.Set(i,TRUE);
		}
	}

void VertexDelta::Apply(TimeValue t,Mesh& mesh)
	{
	// This does nothing if the number of verts hasn't changed in the mesh.
	SetSize(mesh.getNumVerts(),FALSE);

	// Apply the deltas
	for ( int i = 0; i < deltas.Count(); i++ ) {
		mesh.setVert(i,mesh.getVert(i)+deltas[i]);
		}	
	
	// Set hide flags
	SetupHideFlags(mesh,hide);
	}

void VertexDelta::Scale(float f)
	{
	for (int i=0; i < deltas.Count(); i++) {
		deltas[i] *= f;
		}
	}

VertexDelta& VertexDelta::operator=(VertexDelta& from) 
	{ 
	deltas = from.deltas; 
	hide   = from.hide;
	return *this; 
	}

VertexDelta::~VertexDelta()
	{
	}

VertexDelta::VertexDelta()
	{	
	}


/*-------------------------------------------------------------------*/		
//
// Topology deltas
//


void MeshDeleteVertex(Mesh &mesh,DWORD index)
	{
	assert(index<(DWORD)mesh.getNumVerts());
	for (int i=index; i<mesh.getNumVerts()-1; i++) {
		mesh.verts[i] = mesh.verts[i+1];
		}
	mesh.numVerts--;
	}

void MeshDeleteFace(Mesh &mesh,DWORD index)
	{
	assert(index<(DWORD)mesh.getNumFaces());
	for (int i=index; i<mesh.getNumFaces()-1; i++) {
		mesh.faces[i] = mesh.faces[i+1];
		if (mesh.tvFace) {
			mesh.tvFace[i] = mesh.tvFace[i+1];
			}
		}
	mesh.numFaces--;
	}


void TopoDelta::Apply(Mesh& mesh)
	{
	SetSize(mesh.getNumFaces(),mesh.getNumVerts(), mesh.tvFace?TRUE:FALSE, FALSE);
	int ov, nv, tv;

	// Original vert count
	ov = mesh.getNumVerts();

	// Number of verts + cloned verts
	nv = cverts.Count() + ov;
	
	// Number of verts + cloned verts + added verts
	tv = nv + nverts.Count();
	
	// Allocate enough for all vertices (including ones that are deleted)
	if (tv != mesh.getNumVerts()) {
		
		Point3 *verts = new Point3[tv];
	
		// Copy in existing verts
		for (int i=0; i<mesh.getNumVerts(); i++) {
			verts[i] = mesh.verts[i];
			}		
		
		delete[] mesh.verts;
		mesh.verts = verts;
		}
	
	// Clone vertices to the end of the vert list.
	if (cverts.Count()) {
		for ( int i=ov; i<nv; i++) {
			if (cverts[i-ov] < (DWORD)i) {
				mesh.verts[i] = mesh.verts[cverts[i-ov]];
				}
			}		
		mesh.numVerts = nv;		
		}

	// Delete vertices.	
	if (dverts.Count()) {
				
		for (int i=0; i<dverts.Count(); i++) {
			if (dverts[i]<(DWORD)mesh.numVerts) {
				MeshDeleteVertex(mesh,dverts[i]);
				}
			}		
		
		/*
		BitArray delSet;
		delSet.SetSize(mesh.getNumVerts());
		DWORDTab dmap;
		dmap.SetCount(mesh.getNumVerts());
		for (int i=0; i<mesh.getNumVerts(); i++) dmap[i]=0;
		for (i=0; i<dverts.Count(); i++) {
			dmap[dverts[i]]++;
			}
		int j=0;
		for (i=0; i<dmap.Count(); i++) {
			if (dmap[i]) {
				for (int k=0; k<(int)dmap[i]; k++) delSet.Set(j++);
			} else {
				j++;
				}
			}		
		MeshDeleteVertices(mesh,delSet);
		*/
		}
	
	// Add verts
	if (nverts.Count()) {
		int ct = mesh.getNumVerts();
		for (int i=0; i<nverts.Count(); i++) {
			mesh.verts[i+ct] = nverts[i];
			}
		mesh.numVerts += nverts.Count();
		}

	// Add texture verts
	if (nTverts.Count()) {
		int ct = mesh.numTVerts + nTverts.Count();
		Point3 *tverts = new Point3[ct];
		for (int i=0; i<mesh.numTVerts; i++) {
			tverts[i] = mesh.tVerts[i];
			}
		for (i=0; i<nTverts.Count(); i++) {
			tverts[mesh.numTVerts+i] = nTverts[i];
			}
		delete[] mesh.tVerts;
		mesh.tVerts = tverts;
		mesh.numTVerts = ct;
		}

	// Re-alloc the verts to fit the result
	mesh.vertSel.SetSize(mesh.getNumVerts(),TRUE);
	if (mesh.getNumVerts() != tv) {
		Point3 *verts = new Point3[mesh.getNumVerts()];
		for (int i=0; i<mesh.getNumVerts(); i++) {
			verts[i] = mesh.verts[i];
			}
		delete[] mesh.verts;
		mesh.verts = verts;		
		}
	
	// Delete faces
	if (dfaces.Count()) {
				
		for (int i=0; i<dfaces.Count(); i++) {
			if (dfaces[i]<(DWORD)mesh.getNumFaces()) {
				MeshDeleteFace(mesh,dfaces[i]);
				}
			}
		Face *faces = new Face[mesh.getNumFaces()];
		for (i=0; i<mesh.getNumFaces(); i++) {
			faces[i] = mesh.faces[i];
			}
		delete[] mesh.faces;
		mesh.faces = faces;		
						
		/*
		BitArray delSet;
		delSet.SetSize(mesh.getNumFaces());
		DWORDTab dmap;
		dmap.SetCount(mesh.getNumFaces());
		for (int i=0; i<mesh.getNumFaces(); i++) dmap[i]=0;
		for (i=0; i<dfaces.Count(); i++) {
			dmap[dfaces[i]]++;
			}
		int j=0;
		for (i=0; i<dmap.Count(); i++) {
			if (dmap[i]) {
				for (int k=0; k<(int)dmap[i]; k++) delSet.Set(j++);
			} else {
				j++;
				}
			}
		MeshDeleteFaces(mesh,delSet);
		*/
		
		mesh.faceSel.SetSize(mesh.getNumFaces(),TRUE);
		}

	// Add new faces and remap faces
	if (nfaces.Count()) {
		int nf = nfaces.Count() + mesh.getNumFaces();
		Face *faces = new Face[nf];
		TVFace *tvfaces = NULL;
		if (mesh.tvFace || nTVfaces.Count()) {
			tvfaces = new TVFace[nf];
			}

		// Copy existing faces, remap and apply attribs
		for (int i = 0; i < mesh.getNumFaces(); i++) {
			faces[i] = mesh.faces[i];
			if (tvfaces) {
				if (mesh.tvFace) tvfaces[i] = mesh.tvFace[i];
				else tvfaces[i] = TVFace(0,0,0);
				if (tmap.Count()) {
					for (int j = 0; j < 3; j++) {
						if (tmap[i].v[j] != UNDEFINED) {
							tvfaces[i].t[j] = tmap[i].v[j];
							}
						}
					}
				}

			for (int j = 0; j < 3; j++) {
				if ( map[i].v[j] != UNDEFINED ) {
					faces[i].v[j] = map[i].v[j];
					}
				}
			if (attribs[i] & ATTRIB_APPLY_EDGE) {
				faces[i].flags &= ~EDGE_ALL;
				faces[i].flags |= EDGE_ALL & attribs[i];
				}
			if (attribs[i] & ATTRIB_APPLY_FACE) {
				faces[i].flags &= ~FACE_HIDDEN;
				faces[i].flags |= FACE_HIDDEN & attribs[i];
				}
			if (attribs[i] & ATTRIB_APPLY_MATID) {
				faces[i].setMatID((MtlID)(attribs[i]>>ATTRIB_MATID_SHIFT)&ATTRIB_MATID_MASK);
				}
			if (attribs[i] & ATTRIB_APPLY_SMGROUP) {
				faces[i].smGroup = smgroups[i];
				}
			}
		
		// Add in new faces
		for ( ; i < nf; i++ ) {
			faces[i] = nfaces[i-mesh.getNumFaces()];
			if (tvfaces) {
				if (i-mesh.getNumFaces() < nTVfaces.Count()) {				
					tvfaces[i] = nTVfaces[i-mesh.getNumFaces()];
				} else {
					tvfaces[i] = TVFace(0,0,0);
					}
				}
			}

		delete[] mesh.faces;
		mesh.faces    = faces;
		mesh.numFaces = nf;
		mesh.faceSel.SetSize(nf,TRUE);
		if (tvfaces) {
			delete[] mesh.tvFace;
			mesh.tvFace = tvfaces;
			}
	} else {
		// Remap faces and apply attribs.
		for (int i = 0; i < mesh.getNumFaces(); i++) {
			if (i<map.Count()) {
				for (int j = 0; j < 3; j++) {
					if (map[i].v[j] != UNDEFINED) {
						mesh.faces[i].v[j] = map[i].v[j];
						}
					}
				}

			// Remap tv faces
			if (mesh.tvFace && i<tmap.Count()) {			
				for (int j = 0; j < 3; j++) {
					if (tmap[i].v[j] != UNDEFINED) {
						mesh.tvFace[i].t[j] = tmap[i].v[j];
						}
					}			
				}

			if (i<attribs.Count()) {
				if (attribs[i] & ATTRIB_APPLY_EDGE) {
					mesh.faces[i].flags &= ~EDGE_ALL;
					mesh.faces[i].flags |= EDGE_ALL & attribs[i];
					}
				if (attribs[i] & ATTRIB_APPLY_FACE) {
					mesh.faces[i].flags &= ~FACE_HIDDEN;
					mesh.faces[i].flags |= FACE_HIDDEN & attribs[i];
					}
				if (attribs[i] & ATTRIB_APPLY_MATID) {
					mesh.faces[i].setMatID((MtlID)(attribs[i]>>ATTRIB_MATID_SHIFT)&ATTRIB_MATID_MASK);
					}
				if (attribs[i] & ATTRIB_APPLY_SMGROUP) {
					mesh.faces[i].smGroup = smgroups[i];
					}
				}
			}		
		}	
	
	// verify all face indices are in range. If incomming topology
	// has been modified then faces could be remapped out of range.
	for (int i = 0; i < mesh.getNumFaces(); i++) {
		for (int j=0; j<3; j++) {
			if (mesh.faces[i].v[j] >= (DWORD)mesh.numVerts) {
				mesh.faces[i].v[j] = 0;
				}
			if (mesh.tvFace) {
				if (mesh.tvFace[i].t[j] >= (DWORD)mesh.numTVerts) {
					mesh.tvFace[i].t[j] = 0;
					}				
				}			
			}
		}
	}

TopoDelta& TopoDelta::operator=(TopoDelta& td)
	{
	cverts     = td.cverts;
	nverts     = td.nverts;
	nTverts    = td.nTverts;
	dverts     = td.dverts;
	dfaces     = td.dfaces;
	nfaces     = td.nfaces;
	nTVfaces   = td.nTVfaces;
	map        = td.map;
	tmap       = td.tmap;
	attribs    = td.attribs;
	smgroups   = td.smgroups;
	inputFaces = td.inputFaces;
	inputVerts = td.inputVerts;
	return *this;
	}

void TopoDelta::ModifyFace(DWORD index,Face& face)
	{
	ModifyFace(index,face.v);
	}

void TopoDelta::ModifyFace(DWORD index,DWORD *v)
	{
	// Either we're modifying an original face or an added face.
	if (index < (DWORD)map.Count()) {
		for ( int i = 0; i < 3; i++ ) {
			map[index].v[i] = v[i];
			}
	} else {
		for ( int i = 0; i < 3; i++ ) {
			nfaces[index-map.Count()].v[i] = v[i];
			}
		}
	}

void TopoDelta::ModifyTVFace(DWORD index,TVFace& face)
	{
	ModifyTVFace(index,face.t);
	}

void TopoDelta::ModifyTVFace(DWORD index,DWORD* v)
	{
	// Either we're modifying an original face or an added face.
	if (index < (DWORD)tmap.Count()) {
		for ( int i = 0; i < 3; i++ ) {
			tmap[index].v[i] = v[i];
			}
	} else {
		for ( int i = 0; i < 3; i++ ) {
			nTVfaces[index-tmap.Count()].t[i] = v[i];
			}
		}
	}

void TopoDelta::SetEdgeVisibility(DWORD index,DWORD vis)
	{
	// Either we're modifying an original face or an added face.
	if (index < (DWORD)attribs.Count()) {
		attribs[index] = vis|ATTRIB_APPLY_EDGE;
	} else {		
		nfaces[index-attribs.Count()].flags &= ~EDGE_ALL;
		nfaces[index-attribs.Count()].flags |= vis&EDGE_ALL;
		}
	}

void TopoDelta::SetFaceVisibility(DWORD index,BOOL vis)
	{
	// Either we're modifying an original face or an added face.
	if (index < (DWORD)attribs.Count()) {
		if (vis) attribs[index] &= ~FACE_HIDDEN;
		else attribs[index] |= FACE_HIDDEN;
		attribs[index] |= ATTRIB_APPLY_FACE;
	} else {		
		if (vis) nfaces[index-attribs.Count()].flags &= ~FACE_HIDDEN;
		else nfaces[index-attribs.Count()].flags |= FACE_HIDDEN;		
		}
	}

void TopoDelta::SetSmoothGroup(DWORD index,DWORD sg,DWORD mask)
	{
	if (index < (DWORD)attribs.Count()) {
		smgroups[index] &= ~mask;
		smgroups[index] |= sg;
		attribs[index] |= ATTRIB_APPLY_SMGROUP;
	} else {
		nfaces[index-attribs.Count()].smGroup &= ~mask;
		nfaces[index-attribs.Count()].smGroup |= sg;
		}
	}

void TopoDelta::SetMatID(DWORD index,int id)
	{
	if (index < (DWORD)attribs.Count()) {
		attribs[index] &= ~(ATTRIB_MATID_MASK<<ATTRIB_MATID_SHIFT);
		attribs[index] |= (DWORD)(id<<ATTRIB_MATID_SHIFT);
		attribs[index] |= ATTRIB_APPLY_MATID;
	} else {		
		//nfaces[index-attribs.Count()].flags &= ~(ATTRIB_MATID_MASK<<ATTRIB_MATID_SHIFT);
		//nfaces[index-attribs.Count()].flags |= (DWORD)(id<<ATTRIB_MATID_SHIFT);
		nfaces[index-attribs.Count()].setMatID((MtlID)id);
		}
	}

void TopoDelta::RemoveFace(DWORD index)
	{	
	if (index >= inputFaces - dfaces.Count()) {
		nfaces.Delete((int)index-inputFaces+dfaces.Count(),1);
		if (nTVfaces.Count()) nTVfaces.Delete((int)index-inputFaces+dfaces.Count(),1);
	} else {
		map.Delete(index,1);
		attribs.Delete(index,1);
		dfaces.Append(1,&index);
		if (tmap.Count()) tmap.Delete(index,1);
		}
	}

void TopoDelta::BuildInvCloneTable(DWORDTab &invVert)
	{
	// The total verts in the output mesh WITHOUT subtracting deleted verts
	invVert.SetCount(inputVerts+cverts.Count()+nverts.Count());
	
	// Start as the identity
	for (int i=0; i<invVert.Count(); i++) {
		invVert[i] = i;
		}
	
	// Delete entries
	for (i=0; i<dverts.Count(); i++) {
		invVert.Delete(dverts[i],1);
		}
	}

void TopoDelta::RemoveVertex(DWORD index)
	{
	if (index >= inputVerts-dverts.Count()+cverts.Count()) {
	 	// We're removing a vertex that was added.
		index -= inputVerts-dverts.Count()+cverts.Count();		
		nverts.Delete(index,1);		
	} else {
		// We're removing a vertex from the input mesh or one that was cloned
		dverts.Append(1,&index);
		}
	}

FaceMap TopoDelta::GetFaceMap(DWORD index)
	{
	if (index < (DWORD)map.Count()) {
		return map[index];
	} else {		
		return FaceMap(nfaces[index-map.Count()].v);
		}
	}

void TopoDelta::SetSize(int nface, int nvert, BOOL uvs, BOOL clear)
	{
	if (clear) {
		cverts.Delete(0,cverts.Count());
		nverts.Delete(0,nverts.Count());
		nfaces.Delete(0,nfaces.Count());
		map.Delete(0,map.Count());
		nTverts.Resize(0);
		nTVfaces.Resize(0);
		}
	int numFace = nface - dfaces.Count();
	if (numFace<0) numFace = 0;
	if (map.Count() > numFace) {				
		int diff = map.Count() - numFace;
		if (diff) map.Delete( map.Count()-diff, diff);
		}
	if ((tmap.Count()||uvs) && tmap.Count() > numFace) {		
		int diff = tmap.Count() - numFace;
		tmap.Delete( tmap.Count()-diff, diff);
		}
	if (attribs.Count() > numFace) {
		int diff = attribs.Count() - numFace;
		attribs.Delete(attribs.Count()-diff, diff);
		}
	if (smgroups.Count() > numFace) {
		int diff = smgroups.Count() - numFace;
		smgroups.Delete(smgroups.Count()-diff, diff);
		}
	if (map.Count() < numFace) {
		//int diff = nface - map.Count() + dfaces.Count();
		int diff = numFace - map.Count();
		map.Resize(numFace);
		FaceMap fm(UNDEFINED,UNDEFINED,UNDEFINED);
		for (int i = 0; i < diff; i++) {
			map.Append(1,&fm);
			}		
		}
	if ((tmap.Count()||uvs) && 
		tmap.Count() < numFace) {
		//int diff = nface - tmap.Count() + dfaces.Count();
		int diff = numFace - tmap.Count();
		tmap.Resize(numFace);
		FaceMap fm(UNDEFINED,UNDEFINED,UNDEFINED);
		for (int i = 0; i < diff; i++) {
			tmap.Append(1,&fm);
			}		
		}
	if (attribs.Count() < numFace) {
		//int diff = nface - attribs.Count() + dfaces.Count();
		int diff = numFace - attribs.Count();
		attribs.Resize(numFace);
		DWORD z=0;
		for (int i = 0; i < diff; i++) {
			attribs.Append(1,&z);
			}		
		}
	if (smgroups.Count() < numFace) {
		//int diff = nface - smgroups.Count() + dfaces.Count();
		int diff = numFace - smgroups.Count();
		smgroups.Resize(numFace);
		DWORD z=0;
		for (int i = 0; i < diff; i++) {
			smgroups.Append(1,&z);
			}		
		}

	inputFaces = nface;
	inputVerts = nvert;
	}


/*-------------------------------------------------------------------*/
//
// Per instance data
//

EditMeshData::EditMeshData()
	{
	flags = 0;
	tempData = NULL;
	}

EditMeshData::EditMeshData(EditMeshData& emc)
	{
	vdelta   = emc.vdelta;
	tdelta   = emc.tdelta;
	vsel     = emc.vsel;
	fsel     = emc.fsel;	
	esel     = emc.esel;
	vselSet  = emc.vselSet;
	fselSet  = emc.fselSet;
	eselSet  = emc.eselSet;
	flags    = emc.flags & EMD_HASDATA;
	tempData = NULL;
	}


void EditMeshData::Apply(
		TimeValue t,TriObject *triOb,int selLevel, int selType, 
		BOOL inBuildFace, float normScale)
	{	
	if (!GetFlag(EMD_UPDATING_CACHE) && tempData 
	      && tempData->MeshCached(t)) {
		triOb->mesh.DeepCopy( 
			tempData->GetMesh(t),
			PART_GEOM|SELECT_CHANNEL|PART_SUBSEL_TYPE|
			PART_DISPLAY|PART_TOPO|TEXMAP_CHANNEL);
		triOb->mesh.setNumVCFaces(0);
		triOb->mesh.setNumVertCol(0);
		triOb->PointsWereChanged();
		}	
	else if (GetFlag(EMD_HASDATA)) {
		tdelta.Apply(triOb->mesh);
		vdelta.Apply(t,triOb->mesh);	
		vsel.SetSize(triOb->mesh.getNumVerts(),TRUE);
		fsel.SetSize(triOb->mesh.getNumFaces(),TRUE);
		esel.SetSize(triOb->mesh.getNumFaces()*3,TRUE);
		vselSet.SetSize(triOb->mesh.getNumVerts());
		fselSet.SetSize(triOb->mesh.getNumFaces());
		eselSet.SetSize(triOb->mesh.getNumFaces()*3);
		triOb->mesh.vertSel = vsel;
		triOb->mesh.faceSel = fsel;
		triOb->mesh.edgeSel = esel;
		triOb->mesh.setNumVCFaces(0);
		triOb->mesh.setNumVertCol(0);
		triOb->PointsWereChanged();

		// Kind of a waste when there's no animation...		
		triOb->UpdateValidity(GEOM_CHAN_NUM,FOREVER);
		triOb->UpdateValidity(TOPO_CHAN_NUM,FOREVER);
		triOb->UpdateValidity(SELECT_CHAN_NUM,FOREVER);
		triOb->UpdateValidity(SUBSEL_TYPE_CHAN_NUM,FOREVER);
		triOb->UpdateValidity(DISP_ATTRIB_CHAN_NUM,FOREVER);		
		}
	
	triOb->mesh.dispFlags = 0;
	if (inBuildFace) {
		triOb->mesh.SetDispFlag(DISP_VERTTICKS);
		}
	switch (selLevel) {
		case EM_VERTEX:
			triOb->mesh.SetDispFlag(DISP_VERTTICKS|DISP_SELVERTS);
			break;
		case EM_FACE:
			if (selType == SELTYPE_POLY || selType == SELTYPE_ELEMENT) {
				triOb->mesh.SetDispFlag(DISP_SELPOLYS);
			} else {
				triOb->mesh.SetDispFlag(DISP_SELFACES);
				}
			if (normScale>0.0f) {
				triOb->mesh.displayNormals(TRUE,normScale);
				}
			break;
		case EM_EDGE:
			triOb->mesh.SetDispFlag(DISP_SELEDGES);
			triOb->mesh.displayAllEdges(TRUE);
			break;
		}
	triOb->mesh.selLevel = meshLevel[selLevel];
	
	// If we have got tvFaces then make sure the flag is
	// set for all faces.
	if (triOb->mesh.tvFace) {
		for (int i=0; i<triOb->mesh.getNumFaces(); i++) {
			triOb->mesh.faces[i].flags |= HAS_TVERTS;
			}
		}

	if (GetFlag(EMD_UPDATING_CACHE)) {
		assert(tempData);
		tempData->UpdateCache(triOb);
		SetFlag(EMD_UPDATING_CACHE,FALSE);
		}		
	}

void EditMeshData::Invalidate(PartID part,BOOL meshValid)
	{
	if ( tempData ) {
		tempData->Invalidate(part,meshValid);
		}
	}

void EditMeshData::BeginEdit(TimeValue t)
	{
	assert(tempData);
	if (!GetFlag(EMD_HASDATA)) {
		Mesh *mesh = tempData->GetMesh(t);

		tdelta.SetSize(mesh->getNumFaces(), mesh->getNumVerts(), mesh->tvFace?TRUE:FALSE, TRUE);
		vdelta.SetSize(mesh->getNumVerts(), TRUE);
		vsel = mesh->VertSel();
		fsel = mesh->FaceSel();	
		esel = mesh->edgeSel;
		
		// Initialize these
		for (int i=0; i<mesh->getNumFaces(); i++) {
			tdelta.attribs[i] |= mesh->faces[i].getMatID()<<ATTRIB_MATID_SHIFT;
			tdelta.smgroups[i] = mesh->faces[i].smGroup;
			}

		SetFlag(EMD_HASDATA,TRUE);
		}
	}

EMTempData *EditMeshData::TempData(EditMeshMod *mod)
	{
	if ( !tempData ) {
		assert(mod->iObjParams);
		tempData = new EMTempData(mod,this);
		}
	return tempData;
	}


/*-------------------------------------------------------------------*/		
//
// Edit Mesh Modifier
//


EditMeshMod::EditMeshMod()
	{
	if (GetSystemSetting(SYSSET_ENABLE_EDITMESHMOD)) {
		selLevel = EM_VERTEX;
		}	
	}

EditMeshMod::~EditMeshMod()
	{
	ClearSetNames();
	}

class ScaleVertDeltas : public ModContextEnumProc {
	public:
		float f;
		EditMeshMod *mod;
		ScaleVertDeltas(float ff,EditMeshMod *m) {f=ff;mod=m;}
		BOOL proc(ModContext *mc) {
			EditMeshData *emdata = (EditMeshData*)mc->localData;
			if (emdata) {
				if (theHold.Holding()) 
					theHold.Put(new VertexEditRestore(emdata,mod));
				emdata->vdelta.Scale(f);
				}
			return TRUE;
			}
	};

void EditMeshMod::RescaleWorldUnits(float f)
	{
	if (TestAFlag(A_WORK1))
		return;
	SetAFlag(A_WORK1);	
	ScaleVertDeltas svd(f,this);
	EnumModContexts(&svd);
	}

Interval EditMeshMod::LocalValidity(TimeValue t)
	{
	// Force a cache if being edited.
	// TH 12/20/95 -- Uncommented this code to test fix of failure to cache Extrusions/Lathes
	 if (TestAFlag(A_MOD_BEING_EDITED))  // DONT WANT THIS since we have our own Cache.
	 	return NEVER;  			   
	return FOREVER;
	}

RefTargetHandle EditMeshMod::Clone(RemapDir& remap) {
	EditMeshMod* newmod = new EditMeshMod();	
	newmod->selLevel = selLevel;
	return(newmod);
	}

BOOL EditMeshMod::DependOnTopology(ModContext &mc)
	{
	EditMeshData *meshData = (EditMeshData*)mc.localData;
	if (meshData) {
		if (meshData->GetFlag(EMD_HASDATA)) {
			return TRUE;
			}
		}
	return FALSE;
	}


void EditMeshMod::ModifyObject(TimeValue t, ModContext &mc, ObjectState * os, INode *node) 
	{		
	assert(os->obj->IsSubClassOf(triObjectClassID));
	
	TriObject *triOb = (TriObject *)os->obj;
	EditMeshData *meshData;

	if ( !mc.localData ) {
		mc.localData = new EditMeshData();
		meshData = (EditMeshData*)mc.localData;
	} else {
		meshData = (EditMeshData*)mc.localData;
		}
	
	meshData->Apply(t,triOb,selLevel,selType,inBuildFace,normScale);
	}


void EditMeshMod::NotifyInputChanged(Interval changeInt, PartID partID, RefMessage message, ModContext *mc)
	{
	if ( mc->localData ) {
		EditMeshData *meshData = (EditMeshData*)mc->localData;
		if ( meshData ) {
			// The FALSE parameter indicates the the mesh cache itself is
			// invalid in addition to any other caches that depend on the
			// mesh cache.
			meshData->Invalidate(partID,FALSE);
			}
		}
	}

// Converts a series of vertex hit records into face hit records.
HitRecord *EditMeshMod::VertHitsToFaceHits(
		HitRecord *hitRec,BOOL all)
	{	
	HitRecord *ptr = NULL;
	TimeValue t = iObjParams->GetTime();

	while (hitRec) {
		EditMeshData *meshData = (EditMeshData*)hitRec->modContext->localData;
		AdjEdgeList *el = meshData->TempData(this)->AdjList(t);
		Mesh *mesh = meshData->TempData(this)->GetMesh(t);
		
		// Add faces adjacent to this vertex
		DWORDTab &list = (*el)[hitRec->hitInfo];
		for (int i=0; i<list.Count(); i++) {
			if (el->edges[list[i]].f[0]!=UNDEFINED) {
				ptr = new HitRecord(			
					ptr,
					hitRec->nodeRef,
					hitRec->modContext,
					hitRec->distance,
					el->edges[list[i]].f[0],
					NULL);
				}
			if (el->edges[list[i]].f[1]!=UNDEFINED) {
				ptr = new HitRecord(				
					ptr,
					hitRec->nodeRef,
					hitRec->modContext,
					hitRec->distance,
					el->edges[list[i]].f[1],
					NULL);
				}
			}
		
		if (!all) break;
		hitRec = hitRec->Next();
		}
	return ptr;
	}


void EditMeshMod::SelectSubComponent(
		HitRecord *hitRec, BOOL selected, BOOL all, BOOL invert)
	{
	EditMeshData *meshData = (EditMeshData*)hitRec->modContext->localData;
	
	if ( !iObjParams ) return; 
	TimeValue t = iObjParams->GetTime();
	EndNormalMove(t);
	
	if (!meshData) {
		return;
		}
	Mesh *mesh = meshData->TempData(this)->GetMesh(t);
	meshData->BeginEdit(t);
	iObjParams->ClearCurNamedSelSet();

	switch (selLevel) {
		case EM_OBJECT:
			assert(0);
			return;

		case EM_VERTEX:
			if ( theHold.Holding() ) {
				theHold.Put(new VertexSelRestore(meshData,this));
				}
			if (all) {				
				if (invert) {
					while (hitRec) {
						meshData = (EditMeshData*)hitRec->modContext->localData;
						assert(meshData);
						mesh = meshData->TempData(this)->GetMesh(t);
						meshData->BeginEdit(t);

						if (mesh->vertSel[hitRec->hitInfo]) {
							mesh->vertSel.Clear(hitRec->hitInfo);
							meshData->vsel.Clear(hitRec->hitInfo);
						} else {
							mesh->vertSel.Set(hitRec->hitInfo);
							meshData->vsel.Set(hitRec->hitInfo);
							}
						hitRec = hitRec->Next();						
						}
				} else
				if (selected) {
					while (hitRec) {
						meshData = (EditMeshData*)hitRec->modContext->localData;
						assert(meshData);
						mesh = meshData->TempData(this)->GetMesh(t);
						meshData->BeginEdit(t);

						mesh->vertSel.Set(hitRec->hitInfo);
						meshData->vsel.Set(hitRec->hitInfo);
						hitRec = hitRec->Next();						
						}
				} else {
					while (hitRec) {
						meshData = (EditMeshData*)hitRec->modContext->localData;
						assert(meshData);
						mesh = meshData->TempData(this)->GetMesh(t);
						meshData->BeginEdit(t);

						mesh->vertSel.Clear(hitRec->hitInfo);
						meshData->vsel.Clear(hitRec->hitInfo);
						hitRec = hitRec->Next();
						}
					}
			} else {
				if ( selected ) {
					mesh->vertSel.Set(hitRec->hitInfo);
					meshData->vsel.Set(hitRec->hitInfo);
				} else {
					mesh->vertSel.Clear(hitRec->hitInfo);
					meshData->vsel.Clear(hitRec->hitInfo);
					}	
				}
			break;

		case EM_FACE: {
			HitRecord *faceHitRec = NULL;

			if ( theHold.Holding() ) {
				theHold.Put(new FaceSelRestore(meshData,this));
				}
			
			// If we're selecting verts by faces, convert the
			// vertex hits into face hits
			if (selByVert) {
				hitRec = faceHitRec = 
					VertHitsToFaceHits(hitRec,all);
				all = TRUE;
				if (!hitRec) break;
				}

			if (selType==SELTYPE_POLY && !(all&&invert)) {				
				// Select polygons
								
				DWORDTab sel;
				if (all) {
					while (hitRec) {
						meshData = (EditMeshData*)hitRec->modContext->localData;
						Mesh *mesh = meshData->TempData(this)->GetMesh(t);
						AdjFaceList *al = meshData->TempData(this)->FaceAdjList(t);
						meshData->BeginEdit(t);

						sel.Resize(0);
						PolyFromFace(*mesh,*al,hitRec->hitInfo,sel,planarSpin->GetFVal());
						for (int i = 0; i < sel.Count(); i++) {							
							if (selected) {
								mesh->faceSel.Set(sel[i]);
								//meshData->fsel.Set(sel[i]);
							} else {
								mesh->faceSel.Clear(sel[i]);
								//meshData->fsel.Clear(sel[i]);
								}	
							}
						hitRec = hitRec->Next();
						}
				} else {
					AdjFaceList *al = meshData->TempData(this)->FaceAdjList(t);
					PolyFromFace(*mesh,*al,hitRec->hitInfo,sel,planarSpin->GetFVal());
					for (int i = 0; i < sel.Count(); i++) {
						if (selected) {
							mesh->faceSel.Set(sel[i]);
							//meshData->fsel.Set(sel[i]);
						} else {
							mesh->faceSel.Clear(sel[i]);
							//meshData->fsel.Clear(sel[i]);
							}	
						}
					}
			
			} else if (selType==SELTYPE_ELEMENT && !(all&&invert)) {
				// Select elements
								
				BitArray set, fdone;
				AdjFaceList *al = meshData->TempData(this)->FaceAdjList(t);
				Mesh *mesh = meshData->TempData(this)->GetMesh(t);
				EditMeshData *oldMD = NULL;
				set.SetSize(mesh->getNumFaces());
				fdone.SetSize(mesh->getNumFaces());

				while (hitRec) {					
					// Select element from a face					
					SelectElementFromFace(
						*mesh,*al,hitRec->hitInfo,set,fdone);
					
					// Go to next hit record
					hitRec = hitRec->Next();

					oldMD = meshData;
					if (hitRec) {						
						meshData = (EditMeshData*)hitRec->modContext->localData;
						}
					if (meshData != oldMD || !all || !hitRec) {
						// Done with this meshData. Do the selction
						if (selected) {
							mesh->faceSel |= set;
							oldMD->fsel |= set;
						} else {
							mesh->faceSel &= ~set;
							oldMD->fsel &= ~set;
							}

						// We're just doing one face or we've reached the last face.
						if (!all) break;
						if (!hitRec) break;

						// Get the data for the next meshdata
						al = meshData->TempData(this)->FaceAdjList(t);
						mesh = meshData->TempData(this)->GetMesh(t);
						meshData->BeginEdit(t);
						set.SetSize(mesh->getNumFaces());
						fdone.SetSize(mesh->getNumFaces());
						}					
					}
												
			} else {				
				// Select single faces

				if (selected) {
					if (all) {
						while (hitRec) {
							meshData = (EditMeshData*)hitRec->modContext->localData;
							Mesh *mesh = meshData->TempData(this)->GetMesh(t);						
							meshData->BeginEdit(t);
							
							if (invert) {
								if (mesh->faceSel[hitRec->hitInfo]) {
									mesh->faceSel.Clear(hitRec->hitInfo);
									//meshData->fsel.Clear(hitRec->hitInfo);
								} else {
									mesh->faceSel.Set(hitRec->hitInfo);
									//meshData->fsel.Set(hitRec->hitInfo);
									}
							} else {
								mesh->faceSel.Set(hitRec->hitInfo);
								//meshData->fsel.Set(hitRec->hitInfo);
								}
							hitRec = hitRec->Next();
							}
					} else {
						mesh->faceSel.Set(hitRec->hitInfo);
						//meshData->fsel.Set(hitRec->hitInfo);
						}					
				} else {
					if (all) {
						while (hitRec) {
							meshData = (EditMeshData*)hitRec->modContext->localData;
							Mesh *mesh = meshData->TempData(this)->GetMesh(t);						
							meshData->BeginEdit(t);
							
							mesh->faceSel.Clear(hitRec->hitInfo);
							//meshData->fsel.Clear(hitRec->hitInfo);
							hitRec = hitRec->Next();
							}
					} else {
						mesh->faceSel.Clear(hitRec->hitInfo);
						//meshData->fsel.Clear(hitRec->hitInfo);
						}					
					}	
				}
			
			if (selByVert) {
				// Delete the hits
				HitRecord *ptr = faceHitRec;
				while (ptr) {
					faceHitRec = ptr;
					ptr = ptr->Next();
					delete faceHitRec;
					}				
				}
			
			// RB 6/19/97: Have to do this for all objects
			ModContextList mcList;
			INodeTab nodes;			
			iObjParams->GetModContexts(mcList,nodes);
			for (int i=0; i<mcList.Count(); i++) {
				meshData = (EditMeshData*)mcList[i]->localData;
				Mesh *mesh = meshData->TempData(this)->GetMesh(t);
				DeselectHiddenFaces(*mesh);
				meshData->fsel = mesh->faceSel;
				}
			nodes.DisposeTemporary();

			break;
			}

		case EM_EDGE:
			if (theHold.Holding()) {
				theHold.Put(new EdgeSelRestore(meshData,this));
				}

			while (hitRec) {
				meshData = (EditMeshData*)hitRec->modContext->localData;
				Mesh *mesh = meshData->TempData(this)->GetMesh(t);						
				meshData->BeginEdit(t);
			
				if (all & invert) {
					if (mesh->edgeSel[hitRec->hitInfo]) {
						mesh->edgeSel.Clear(hitRec->hitInfo);
						meshData->esel.Clear(hitRec->hitInfo);
					} else {
						mesh->edgeSel.Set(hitRec->hitInfo,selected);
						meshData->esel.Set(hitRec->hitInfo,selected);
						}
				} else {
					mesh->edgeSel.Set(hitRec->hitInfo,selected);
					meshData->esel.Set(hitRec->hitInfo,selected);
					}
				
				if (!all) break;
				hitRec = hitRec->Next();
				}
			break;
		}

	if (meshData->tempData) {
		meshData->tempData->Invalidate(PART_SELECT);
		}
	
	InvalidateSurfaceUI();
	NotifyDependents(FOREVER, PART_SELECT, REFMSG_CHANGE);
	}

void EditMeshMod::ClearSelection(int selLevel) 
	{
	ModContextList mcList;
	INodeTab nodes;

	if ( !iObjParams ) return;	
	EndNormalMove(iObjParams->GetTime());
	
	iObjParams->GetModContexts(mcList,nodes);
	iObjParams->ClearCurNamedSelSet();

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

		switch ( selLevel ) {
			case EM_OBJECT:
				assert(0);
				return;

			case EM_VERTEX:	
				if ( theHold.Holding() ) {
					theHold.Put(new VertexSelRestore(meshData,this));
					}
				mesh->vertSel.ClearAll();
				meshData->vsel.ClearAll();
				break;

			case EM_FACE:
				if ( theHold.Holding() ) {
					theHold.Put(new FaceSelRestore(meshData,this));
					}
				mesh->faceSel.ClearAll();
				meshData->fsel.ClearAll();
				break;
		
			case EM_EDGE:
				if (theHold.Holding()) {
					theHold.Put(new EdgeSelRestore(meshData,this));
					}
				mesh->edgeSel.ClearAll();
				meshData->esel.ClearAll();
				break;
			}
		
		if (meshData->tempData) {
			meshData->TempData(this)->Invalidate(PART_SELECT);
			}
		}
	
	nodes.DisposeTemporary();
	InvalidateSurfaceUI();
	NotifyDependents(FOREVER, PART_SELECT, REFMSG_CHANGE);
	}

void DeselectHiddenFaces(Mesh &mesh)
	{
	for (int i=0; i<mesh.getNumFaces(); i++) {
		if (mesh.faces[i].Hidden()) {
			mesh.faceSel.Clear(i);
			}
		}
	}

void EditMeshMod::SelectAll(int selLevel)
	{
	ModContextList mcList;
	INodeTab nodes;

	if ( !iObjParams ) return;	
	EndNormalMove(iObjParams->GetTime());
	
	iObjParams->GetModContexts(mcList,nodes);
	iObjParams->ClearCurNamedSelSet();

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

		switch ( selLevel ) {
			case EM_OBJECT:
				assert(0);
				return;

			case EM_VERTEX:	
				if (theHold.Holding()) {
					theHold.Put(new VertexSelRestore(meshData,this));
					}
				mesh->vertSel.SetAll();
				meshData->vsel.SetAll();
				mesh->vertSel &= ~mesh->vertHide;
				meshData->vsel = mesh->vertSel;
				break;

			case EM_FACE:
				if ( theHold.Holding() ) {
					theHold.Put(new FaceSelRestore(meshData,this));
					}
				mesh->faceSel.SetAll();
				DeselectHiddenFaces(*mesh);
				meshData->fsel = mesh->faceSel;
				break;
		
			case EM_EDGE:
				if (theHold.Holding()) {
					theHold.Put(new EdgeSelRestore(meshData,this));
					}
				mesh->edgeSel.SetAll();
				meshData->esel.SetAll();
				break;
			}
		
		if (meshData->tempData) {
			meshData->TempData(this)->Invalidate(PART_SELECT);
			}
		}
	
	nodes.DisposeTemporary();
	InvalidateSurfaceUI();
	NotifyDependents(FOREVER, PART_SELECT, REFMSG_CHANGE);
	}

void EditMeshMod::InvertSelection(int selLevel)
	{
	ModContextList mcList;
	INodeTab nodes;
	int j;

	if ( !iObjParams ) return;	
	EndNormalMove(iObjParams->GetTime());
	
	iObjParams->GetModContexts(mcList,nodes);
	iObjParams->ClearCurNamedSelSet();

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

		switch ( selLevel ) {
			case EM_OBJECT:
				assert(0);
				return;

			case EM_VERTEX:	
				if (theHold.Holding()) {
					theHold.Put(new VertexSelRestore(meshData,this));
					}
				for (j=0; j<mesh->vertSel.GetSize(); j++) {
					if (mesh->vertSel[j]) mesh->vertSel.Clear(j);
					else mesh->vertSel.Set(j);
					}				
				mesh->vertSel &= ~mesh->vertHide;				
				meshData->vsel = mesh->vertSel;
				break;

			case EM_FACE:
				if (theHold.Holding()) {
					theHold.Put(new FaceSelRestore(meshData,this));
					}
				for (j=0; j<mesh->faceSel.GetSize(); j++) {
					if (mesh->faceSel[j]) mesh->faceSel.Clear(j);
					else mesh->faceSel.Set(j);
					}
				DeselectHiddenFaces(*mesh);
				meshData->fsel = mesh->faceSel;
				break;
		
			case EM_EDGE:
				if (theHold.Holding()) {
					theHold.Put(new EdgeSelRestore(meshData,this));
					}
				for (j=0; j<mesh->edgeSel.GetSize(); j++) {
					if (mesh->edgeSel[j]) mesh->edgeSel.Clear(j);
					else mesh->edgeSel.Set(j);
					}
				meshData->esel = mesh->edgeSel;				
				break;
			}
		
		if (meshData->tempData) {
			meshData->TempData(this)->Invalidate(PART_SELECT);
			}
		}
	
	nodes.DisposeTemporary();
	InvalidateSurfaceUI();
	NotifyDependents(FOREVER, PART_SELECT, REFMSG_CHANGE);
	}


//--- Named sub-object selection sets --------------------------------

NamedSelSetList::~NamedSelSetList()
	{
	for (int i=0; i<sets.Count(); i++) {
		delete sets[i];
		}
	}

BitArray *NamedSelSetList::GetSet(DWORD id)
	{
	for (int i=0; i<sets.Count(); i++) {
		if (ids[i]==id) return sets[i];
		}
	return NULL;
	}

void NamedSelSetList::AppendSet(BitArray &nset,DWORD id)
	{
	BitArray *n = new BitArray(nset);
	sets.Append(1,&n);
	ids.Append(1,&id);
	}

void NamedSelSetList::DeleteSet(int i)
	{
	delete sets[i];
	sets.Delete(i,1);
	ids.Delete(i,1);
	}

void NamedSelSetList::RemoveSet(DWORD id)
	{
	for (int i=0; i<sets.Count(); i++) {
		if (ids[i]==id) {
			delete sets[i];
			sets.Delete(i,1);
			ids.Delete(i,1);
			return;
			}
		}
	}

void NamedSelSetList::SetSize(int size)
	{
	for (int i=0; i<sets.Count(); i++) {
		sets[i]->SetSize(size,TRUE);
		}
	}

NamedSelSetList& NamedSelSetList::operator=(NamedSelSetList& from)
	{
	for (int i=0; i<sets.Count(); i++) {
		delete sets[i];
		}
	sets.SetCount(0);
	ids.SetCount(0);

	for (i=0; i<from.Count(); i++) {
		AppendSet(*from.sets[i],from.ids[i]);
		}

	return *this;
	}

void NamedSelSetList::DeleteSetElements(BitArray &set,int m)
	{
	for (int i=0; i<sets.Count(); i++) {
		BitArrayDeleteSet((*this)[i],set,m);
		}
	}

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

void EditMeshMod::ClearSetNames()
	{
	for (int i=0; i<3; i++) {
		for (int j=0; j<namedSel[i].Count(); j++) {
			delete namedSel[i][j];
			namedSel[i][j] = NULL;
			}
		}
	}

int EditMeshMod::FindSet(TSTR &setName,int level)
	{	
	assert(level>0 && level<4);
	for (int i=0; i<namedSel[level-1].Count(); i++) {
		if (setName == *namedSel[level-1][i]) {
			return i;			
			}
		}
	return -1;
	}

DWORD EditMeshMod::AddSet(TSTR &setName,int level)
	{
	DWORD id = 0;
	assert(level>0 && level<4);
	TSTR *name = new TSTR(setName);
	namedSel[level-1].Append(1,&name);
	BOOL found = FALSE;
	while (!found) {
		found = TRUE;
		for (int i=0; i<ids[level-1].Count(); i++) {
			if (ids[level-1][i]==id) {
				id++;
				found = FALSE;
				break;
				}
			}
		}
	ids[level-1].Append(1,&id);
	return id;
	}

void EditMeshMod::RemoveSet(TSTR &setName,int level)
	{
	assert(level>0 && level<4);
	int i = FindSet(setName,level);
	if (i>=0) {
		delete namedSel[level-1][i];
		namedSel[level-1].Delete(i,1);
		ids[level-1].Delete(i,1);
		}
	}

void EditMeshMod::ActivateSubSelSet(TSTR &setName)
	{
	ModContextList mcList;
	INodeTab nodes;
	int index = FindSet(setName,selLevel);
	
	if (index<0 || !iObjParams) return;
	EndNormalMove(iObjParams->GetTime());	

	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(iObjParams->GetTime());
		meshData->BeginEdit(iObjParams->GetTime());

		BitArray *set = NULL;		

		switch (selLevel) {
			case EM_VERTEX:	
				if (theHold.Holding()) {
					theHold.Put(new VertexSelRestore(meshData,this));
					}
				
				set = meshData->vselSet.GetSet(ids[0][index]);
				if (set) {
					if (set->GetSize()!=mesh->getNumVerts()) {
						set->SetSize(mesh->getNumVerts(),TRUE);
						}
					mesh->vertSel  = *set;
					meshData->vsel = mesh->vertSel;
					}
				break;

			case EM_FACE:
				if (theHold.Holding()) {
					theHold.Put(new FaceSelRestore(meshData,this));
					}
				
				set = meshData->fselSet.GetSet(ids[1][index]);
				if (set) {
					if (set->GetSize()!=mesh->getNumFaces()) {
						set->SetSize(mesh->getNumFaces(),TRUE);
						}
					mesh->faceSel  = *set;
					meshData->fsel = mesh->faceSel;
					}
				break;

			case EM_EDGE:
				if (theHold.Holding()) {
					theHold.Put(new EdgeSelRestore(meshData,this));
					}
				
				set = meshData->eselSet.GetSet(ids[2][index]);
				if (set) {
					if (set->GetSize()!=mesh->getNumFaces()*3) {
						set->SetSize(mesh->getNumFaces()*3,TRUE);
						}
					mesh->edgeSel  = *set;
					meshData->esel = mesh->edgeSel;
					}
				break;
			}

		/*
		switch (selLevel) {
			case EM_VERTEX:	
				if (theHold.Holding()) {
					theHold.Put(new VertexSelRestore(meshData,this));
					}
				
				if (meshData->vselSet[index].GetSize()!=mesh->getNumVerts()) {
					meshData->vselSet[index].SetSize(mesh->getNumVerts(),TRUE);
					}
				mesh->vertSel  = meshData->vselSet[index];
				meshData->vsel = mesh->vertSel;
				break;

			case EM_FACE:
				if (theHold.Holding()) {
					theHold.Put(new FaceSelRestore(meshData,this));
					}
				
				if (meshData->fselSet[index].GetSize()!=mesh->getNumFaces()) {
					meshData->fselSet[index].SetSize(mesh->getNumFaces(),TRUE);
					}
				mesh->faceSel  = meshData->fselSet[index];
				meshData->fsel = mesh->faceSel;
				break;

			case EM_EDGE:
				if (theHold.Holding()) {
					theHold.Put(new EdgeSelRestore(meshData,this));
					}
				
				if (meshData->eselSet[index].GetSize()!=mesh->getNumFaces()*3) {
					meshData->eselSet[index].SetSize(mesh->getNumFaces()*3,TRUE);
					}
				mesh->edgeSel  = meshData->eselSet[index];
				meshData->esel = mesh->edgeSel;
				break;
			}
		*/

		if (meshData->tempData) {
			meshData->TempData(this)->Invalidate(PART_SELECT);
			}
		}
	
	nodes.DisposeTemporary();
	InvalidateSurfaceUI();
	NotifyDependents(FOREVER, PART_SELECT, REFMSG_CHANGE);
	iObjParams->RedrawViews(iObjParams->GetTime());
	}

void EditMeshMod::NewSetFromCurSel(TSTR &setName)
	{
	ModContextList mcList;
	INodeTab nodes;	
	DWORD id = -1;
	int index = FindSet(setName,selLevel);
	if (index<0) id = AddSet(setName,selLevel);
	else {
		switch (selLevel) {
			case EM_VERTEX: id = ids[0][index]; break;
			case EM_FACE:   id = ids[1][index]; break;
			case EM_EDGE:   id = ids[2][index]; break;
			}
		}

	if (!iObjParams) return;
	EndNormalMove(iObjParams->GetTime());
	
	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(iObjParams->GetTime());
		meshData->BeginEdit(iObjParams->GetTime());

		BitArray *set = NULL;
		
		switch (selLevel) {
			case EM_VERTEX:	
				if (index>=0 && (set = meshData->vselSet.GetSet(id))) {
					*set = meshData->vsel;
				} else {
					meshData->vselSet.AppendSet(meshData->vsel,id);
					}
				break;

			case EM_FACE:
				if (index>=0 && (set = meshData->fselSet.GetSet(id))) {
					*set = meshData->fsel;
				} else {
					meshData->fselSet.AppendSet(meshData->fsel,id);
					}
				break;

			case EM_EDGE:
				if (index>=0 && (set = meshData->eselSet.GetSet(id))) {
					*set = meshData->esel;
				} else {
					meshData->eselSet.AppendSet(meshData->esel,id);
					}
				break;
			}		
		}	
		
	nodes.DisposeTemporary();
	}

void EditMeshMod::RemoveSubSelSet(TSTR &setName)
	{
	ModContextList mcList;
	INodeTab nodes;
	int index = FindSet(setName,selLevel);

	if (index<0 || !iObjParams) return;
	EndNormalMove(iObjParams->GetTime());
	
	iObjParams->GetModContexts(mcList,nodes);

	DWORD id = -1;
	switch (selLevel) {
		case EM_VERTEX: id = ids[0][index]; break;
		case EM_FACE:   id = ids[1][index]; break;
		case EM_EDGE:   id = ids[2][index]; break;
		}

	for (int i = 0; i < mcList.Count(); i++) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if ( !meshData ) continue;		
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());
		meshData->BeginEdit(iObjParams->GetTime());
		
		switch (selLevel) {
			case EM_VERTEX:	
				meshData->vselSet.RemoveSet(id);
				break;

			case EM_FACE:
				meshData->fselSet.RemoveSet(id);
				break;

			case EM_EDGE:
				meshData->eselSet.RemoveSet(id);
				break;
			}

		if (meshData->tempData) {
			meshData->TempData(this)->Invalidate(PART_SELECT);
			}
		}
	RemoveSet(setName,selLevel);
	iObjParams->ClearCurNamedSelSet();
	nodes.DisposeTemporary();
	}

void EditMeshMod::ActivateSubobjSel(int level, XFormModes& modes )
	{	
	ModContextList mcList;
	INodeTab nodes;
	int old = selLevel;	
		
	if (!iObjParams) return;
	iObjParams->GetModContexts(mcList,nodes);

	// if we're in sub object selection then we want del key input.
	if (selLevel==0 && level!=0) {
		delEvent.SetEditMeshMod(this);
		iObjParams->RegisterDeleteUser(&delEvent);
		}
	if (selLevel!=0 && level==0) {
		iObjParams->UnRegisterDeleteUser(&delEvent);

		// RB 3-26-96: If we're going into object level selection
		// then we want to make sure any object level modes are
		// removed from the command stack.
		iObjParams->DeleteMode(moveMode);
		iObjParams->DeleteMode(rotMode);
		iObjParams->DeleteMode(uscaleMode);
		iObjParams->DeleteMode(nuscaleMode);
		iObjParams->DeleteMode(squashMode);
		iObjParams->DeleteMode(selectMode);
		}

	selLevel = level;
	
	switch (level) {
		case EM_OBJECT:
			break;

		case EM_EDGE:
		case EM_VERTEX:
		case EM_FACE:
			modes = XFormModes(moveMode,rotMode,nuscaleMode,uscaleMode,squashMode,selectMode);
			break;		
		}	
	
	if (iObjParams) {
		SetRollupPage(iObjParams);
		}	

	if (selLevel != old) {
		// Modify the caches to reflect the new sel level.
		for ( int i = 0; i < mcList.Count(); i++ ) {
			EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
			if ( !meshData ) continue;		
		
			if ( meshData->tempData && meshData->TempData(this)->MeshCached(iObjParams->GetTime()) ) {
				Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());
				mesh->dispFlags = 0;
				mesh->SetDispFlag(levelDispFlags[selLevel]);
				mesh->selLevel = meshLevel[selLevel];
				}			
			meshData->Invalidate(PART_SELECT);
			}			

		NotifyDependents(
			FOREVER, 
			PART_SUBSEL_TYPE|PART_DISPLAY, 
			REFMSG_CHANGE);	
		
		iObjParams->PipeSelLevelChanged();
		}
	
	// Setup named selection sets
	if (selLevel != EM_OBJECT) {
		iObjParams->ClearSubObjectNamedSelSets();
		for (int i=0; i<namedSel[selLevel-1].Count(); i++) {
			iObjParams->AppendSubObjectNamedSelSet(*namedSel[selLevel-1][i]);
			}
		}	
	nodes.DisposeTemporary();
	}


int EditMeshMod::SubObjectIndex(HitRecord *hitRec)
	{
	EditMeshData *meshData = (EditMeshData*)hitRec->modContext->localData;
	
	if ( !meshData ) return 0;
	if ( !iObjParams ) return 0;
	TimeValue t = iObjParams->GetTime();

	switch ( selLevel ) {
		case EM_VERTEX: {
			// Changed SubObjectIndex to mean the actual index of the vertice,
			// not the nth selected.
			return hitRec->hitInfo;
			}
		
		case EM_FACE: {
			FaceClusterList *clust =
				meshData->TempData(this)->FaceCluster(t);
			DWORD id = (*clust)[hitRec->hitInfo];
			if (id!=UNDEFINED) {
				return id;
			} else {
				return 0;
				}			
			}

		case EM_EDGE: {			
			EdgeClusterList *clust =
				meshData->TempData(this)->EdgeCluster(t);
			DWORD id = (*clust)[hitRec->hitInfo];
			if (id!=UNDEFINED) {
				return id;
			} else {
				return 0;
				}
			}

		default:
			return 0;
		}
	}


void EditMeshMod::GetSubObjectCenters(
		SubObjAxisCallback *cb,TimeValue t,INode *node,ModContext *mc)
	{
	Interval valid;
	Matrix3 tm = node->GetObjectTM(t);	
	
	assert(iObjParams);

	if (mc->localData) {	
		EditMeshData *meshData = (EditMeshData*)mc->localData;		
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());
		
		switch (selLevel) {
			case EM_VERTEX: {				
				BitArray sel = mesh->VertexTempSel();
				Box3 box;
				for ( int i = 0; i < mesh->getNumVerts(); i++ ) {
					if ( sel[i] ) {
						box += mesh->getVert(i) * tm;
						}
					}
				cb->Center(box.Center(),0);
				break;
				}

			case EM_EDGE:
			case EM_FACE: {
				Point3Tab *centers =
					meshData->TempData(this)->ClustCenters(t);				
				for ( int i = 0; i < centers->Count(); i++ ) {
					cb->Center((*centers)[i]*tm,i);
					}				
				break;
				}

			default:
				cb->Center(tm.GetTrans(),0);
				break;
			}		
		} 
	}


void EditMeshMod::GetSubObjectTMs(
		SubObjAxisCallback *cb,TimeValue t,INode *node,ModContext *mc)
	{
	Matrix3 tm;	
	Interval valid;
	
	if ( mc->localData ) {
		EditMeshData *meshData = (EditMeshData*)mc->localData;
		Mesh *mesh = meshData->TempData(this)->GetMesh(t);

		switch (selLevel) {
			case EM_VERTEX: {
				BitArray sel = mesh->VertexTempSel();
				Point3Tab *norms = 
					meshData->TempData(this)->VertNormals(t);
				Matrix3 otm = node->GetObjectTM(t);
				
				for (int i=0; i<sel.GetSize(); i++) {
					if (sel[i]) {														
						Point3 n = VectorTransform(otm,(*norms)[i]);
						n = Normalize(n);
						MatrixFromNormal(n,tm);
						tm.SetTrans(mesh->verts[i]*otm);
						cb->TM(tm,i);
						}
					}
				break;
				}

			case EM_EDGE:
			case EM_FACE: {
				Point3Tab *norms =
					meshData->TempData(this)->ClustNormals(t);
				Point3Tab *centers =
					meshData->TempData(this)->ClustCenters(t);
				Matrix3 otm = node->GetObjectTM(t);
				for (int i=0; i<norms->Count(); i++) {
					Point3 n = VectorTransform(otm,(*norms)[i]);
					n = Normalize(n);
					MatrixFromNormal(n,tm);
					tm.SetTrans((*centers)[i]*otm);
					cb->TM(tm,i);
					}
				break;
				}			
			}
		}
	}


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

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

	for ( int i = 0; i < mcList.Count(); i++ ) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if ( !meshData ) continue;				
		if ( meshData->tempData ) {
			delete meshData->tempData;
			}
		meshData->tempData = NULL;
		}
	nodes.DisposeTemporary();
	}


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

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

	for ( int i = 0; i < mcList.Count(); i++ ) {
		EditMeshData *meshData = (EditMeshData*)mcList[i]->localData;
		if ( !meshData ) continue;				
		if ( !meshData->tempData ) {
			meshData->tempData = new EMTempData(this,meshData);
			}		
		}
	
	nodes.DisposeTemporary();
	}

int EditMeshMod::HitTest(TimeValue t, INode* inode, int type, int crossing, 
		int flags, IPoint2 *p, ViewExp *vpt, ModContext* mc) 
	{
	Interval valid;
	int savedLimits,res = 0;
	GraphicsWindow *gw = vpt->getGW();
	HitRegion hr;
	MakeHitRegion(hr,type, crossing,4,p);
	gw->setHitRegion(&hr);
	Matrix3 mat = inode->GetObjectTM(t);
	gw->setTransform(mat);	
	gw->setRndLimits(((savedLimits = gw->getRndLimits()) | GW_PICK) & ~(GW_ILLUM|GW_BACKCULL));
	gw->clearHitCode();
	
	if ( mc->localData ) {		
		EditMeshData *meshData = (EditMeshData*)mc->localData;
		Mesh *mesh = meshData->TempData(this)->GetMesh(iObjParams->GetTime());
		SubObjHitList hitList;
		MeshSubHitRec *rec;
		
		if (inBuildFace) {
			res = mesh->SubObjectHitTest( gw, gw->getMaterial(), &hr,
				flags|hitLevel[EM_VERTEX], hitList );
		} else if (selLevel==EM_FACE && selByVert) {
			res = mesh->SubObjectHitTest( gw, gw->getMaterial(), &hr,
				flags|hitLevel[EM_VERTEX]|SUBHIT_USEFACESEL, hitList );
		} else {
			res = mesh->SubObjectHitTest( gw, gw->getMaterial(), &hr,
				flags|hitLevel[selLevel], hitList );
			}

		rec = hitList.First();
		while (rec) {
			vpt->LogHit(inode,mc,rec->dist,rec->index,NULL);
			rec = rec->Next();
			}
		}

	gw->setRndLimits(savedLimits);	
	return res;
	}

int EditMeshMod::Display(TimeValue t, INode* inode, ViewExp *vpt, int flags, ModContext *mc) {	
	return 0;	
	}

void EditMeshMod::GetWorldBoundBox(TimeValue t,INode* inode, ViewExp *vpt, Box3& box, ModContext *mc) {
	box.Init();
	}



//---------------------------------------------------------------------
// IO

#define SEL_LEVEL_CHUNK 			0x2800
#define NAMEDVSEL_NAMES_CHUNK		0x2805
#define NAMEDFSEL_NAMES_CHUNK		0x2806
#define NAMEDESEL_NAMES_CHUNK		0x2807
#define NAMEDSEL_STRING_CHUNK		0x2809
#define NAMEDSEL_ID_CHUNK			0x2810

static int namedSelID[] = {NAMEDVSEL_NAMES_CHUNK,NAMEDFSEL_NAMES_CHUNK,NAMEDESEL_NAMES_CHUNK};

IOResult EditMeshMod::Save(ISave *isave) {
	Modifier::Save(isave);
	Interval valid;
	ULONG nb;	
	short sl = selLevel;
	isave->BeginChunk(SEL_LEVEL_CHUNK);
	isave->Write(&sl,sizeof(short),&nb);
	isave->EndChunk();	
	
	for (int j=0; j<3; j++) {
		if (namedSel[j].Count()) {
			isave->BeginChunk(namedSelID[j]);			
			for (int i=0; i<namedSel[j].Count(); i++) {
				isave->BeginChunk(NAMEDSEL_STRING_CHUNK);
				isave->WriteWString(*namedSel[j][i]);
				isave->EndChunk();

				isave->BeginChunk(NAMEDSEL_ID_CHUNK);
				isave->Write(&ids[j][i],sizeof(DWORD),&nb);
				isave->EndChunk();
				}
			isave->EndChunk();
			}
		}
	return IO_OK;
	}

IOResult EditMeshMod::LoadNamedSelChunk(ILoad *iload,int level)
	{	
	IOResult res;
	DWORD ix=0;
	ULONG nb;
	
	while (IO_OK==(res=iload->OpenChunk())) {
		switch(iload->CurChunkID())  {
			case NAMEDSEL_STRING_CHUNK: {
				TCHAR *name;
				res = iload->ReadWStringChunk(&name);
				//AddSet(TSTR(name),level+1);
				TSTR *newName = new TSTR(name);
				namedSel[level].Append(1,&newName);				
				ids[level].Append(1,&ix);
				ix++;
				break;
				}
			case NAMEDSEL_ID_CHUNK:
				iload->Read(&ids[level][ids[level].Count()-1],sizeof(DWORD), &nb);
				break;
			}
		iload->CloseChunk();
		if (res!=IO_OK) 
			return res;
		}
	return IO_OK;
	}

IOResult EditMeshMod::Load(ILoad *iload) {
	Modifier::Load(iload);
	IOResult res;
	ULONG nb;
	int level = -1;

	while (IO_OK==(res=iload->OpenChunk())) {
		switch(iload->CurChunkID())  {
			case NAMEDVSEL_NAMES_CHUNK: {				
				res = LoadNamedSelChunk(iload,0);
				break;
				}

			case NAMEDFSEL_NAMES_CHUNK: {
				res = LoadNamedSelChunk(iload,1);
				break;
				}

			case NAMEDESEL_NAMES_CHUNK: {
				res = LoadNamedSelChunk(iload,2);
				break;
				}

			case SEL_LEVEL_CHUNK: {
				short sl;
				res = iload->Read(&sl,sizeof(short),&nb);
				selLevel = sl;
				}
				break;
			}
		iload->CloseChunk();
		if (res!=IO_OK) 
			return res;
		}
	return IO_OK;
	}


#define FLAGS_CHUNK 			0x2740
#define VSEL_CHUNK 				0x2750
#define FSEL_CHUNK	 			0x2755
#define ESEL_CHUNK				0x2756
#define VERT_DELTA_CHUNK 		0x2760
#define VERT_HIDE_CHUNK			0x2761
#define TOPO_DELTA_CHUNK 		0x2765
#define TOPO_NVERTS_CHUNK 		0x2771
#define TOPO_NTVERTS_CHUNK		0x2772
#define TOPO_CVERTS_CHUNK 		0x2770
#define TOPO_NFACES_CHUNK 		0x2775
#define TOPO_NTVFACES_CHUNK 	0x2776
#define TOPO_FACEMAP_CHUNK 		0x2780
#define TOPO_TVFACEMAP_CHUNK 	0x2781
#define TOPO_DVERTS_CHUNK 		0x2790
#define TOPO_DFACES_CHUNK 		0x2800
#define TOPO_INPUTFACES_CHUNK	0x2810
#define TOPO_INPUTVERTS_CHUNK	0x2820
#define TOPO_ATTRIBS_CHUNK		0x2830
#define TOPO_SMGROUPS_CHUNK		0x2840
#define VSELSET_CHUNK			0x2845
#define FSELSET_CHUNK			0x2846
#define ESELSET_CHUNK			0x2847
#define SELSET_SET_CHUNK		0x2849
#define SELSET_SETID_CHUNK		0x2850
#define VERT_DELTAZERO_CHUNK   	0x3000
#define TOPO_FACEMAPUNDEF_CHUNK 0x3010
#define TOPO_TVFACEMAPUNDEF_CHUNK 0x3020
#define TOPO_ATTRIBSZERO_CHUNK 0x3030
#define TOPO_SMGROUPSSAME_CHUNK 0x3040

IOResult NamedSelSetList::Load(ILoad *iload)
	{
	IOResult res;
	DWORD ix=0;
	ULONG nb;
		
	while (IO_OK==(res=iload->OpenChunk())) {
		switch(iload->CurChunkID())  {
			case SELSET_SET_CHUNK: {
				BitArray set;
				res = set.Load(iload);
				//AppendSet(set);
				BitArray *n = new BitArray(set);
				sets.Append(1,&n);
				ids.Append(1,&ix);
				ix++;
				break;
				}

			case SELSET_SETID_CHUNK:
				iload->Read(&ids[ids.Count()-1],sizeof(DWORD),&nb);
				break;				
			}
		iload->CloseChunk();
		if (res!=IO_OK) 
			return res;
		}
	return IO_OK;
	}

IOResult NamedSelSetList::Save(ISave *isave)
	{
	ULONG nb;
	for (int i=0; i<sets.Count(); i++) {
		isave->BeginChunk(SELSET_SET_CHUNK);
		sets[i]->Save(isave);
		isave->EndChunk();

		isave->BeginChunk(SELSET_SETID_CHUNK);
		isave->Write(&ids[i],sizeof(DWORD),&nb);
		isave->EndChunk();
		}
	return IO_OK;
	}

IOResult EditMeshMod::SaveLocalData(ISave *isave, LocalModData *ld) {
	ULONG nb;
	int n;
	EditMeshData *em = (EditMeshData *)ld;

	isave->BeginChunk(FLAGS_CHUNK);
	isave->Write(&em->flags,sizeof(DWORD), &nb);
	isave->EndChunk();

	if (em->vsel.NumberSet()>0) {
		isave->BeginChunk(VSEL_CHUNK);
		em->vsel.Save(isave);
		isave->EndChunk();
		}
	
	if (em->fsel.NumberSet()>0) {
		isave->BeginChunk(FSEL_CHUNK);
		em->fsel.Save(isave);
		isave->EndChunk();
		}

	if (em->esel.NumberSet()>0) {
		isave->BeginChunk(ESEL_CHUNK);
		em->esel.Save(isave);
		isave->EndChunk();
		}

	if (em->vselSet.Count()) {
		isave->BeginChunk(VSELSET_CHUNK);
		em->vselSet.Save(isave);
		isave->EndChunk();
		}
	if (em->eselSet.Count()) {
		isave->BeginChunk(ESELSET_CHUNK);
		em->eselSet.Save(isave);
		isave->EndChunk();
		}
	if (em->fselSet.Count()) {
		isave->BeginChunk(FSELSET_CHUNK);
		em->fselSet.Save(isave);
		isave->EndChunk();
		}

	if ((n=em->vdelta.deltas.Count())>0) {
		Point3 *pt  = em->vdelta.deltas.Addr(0);
		Point3 pzero(0,0,0);
		BOOL allz=TRUE;
		for (int i=0; i<n; i++) {
			if (!(pt[i]==pzero)) { allz = FALSE; break; }
			}
		if (allz) {
			isave->BeginChunk(VERT_DELTAZERO_CHUNK);
			isave->Write(&n,sizeof(n),&nb);
			isave->EndChunk();
			}
		else {
			isave->BeginChunk(VERT_DELTA_CHUNK);
			isave->Write(em->vdelta.deltas.Addr(0),n*sizeof(AnimPoint3), &nb);
			isave->EndChunk();
			}
		}

	if (em->vdelta.hide.NumberSet()>0) {
		isave->BeginChunk(VERT_HIDE_CHUNK);
		em->vdelta.hide.Save(isave);
		isave->EndChunk();
		}
	
	isave->BeginChunk(TOPO_DELTA_CHUNK);

	if ((n=em->tdelta.nverts.Count())>0) {
		isave->BeginChunk(TOPO_NVERTS_CHUNK);
		isave->Write(em->tdelta.nverts.Addr(0),n*sizeof(Point3),&nb);
		isave->EndChunk();
		}

	if ((n=em->tdelta.nTverts.Count())>0) {
		isave->BeginChunk(TOPO_NTVERTS_CHUNK);
		isave->Write(em->tdelta.nTverts.Addr(0),n*sizeof(Point3),&nb);
		isave->EndChunk();
		}

	if ((n=em->tdelta.cverts.Count())>0) {
		isave->BeginChunk(TOPO_CVERTS_CHUNK);
		isave->Write(em->tdelta.cverts.Addr(0),n*sizeof(DWORD),&nb);
		isave->EndChunk();
		}

	if ((n=em->tdelta.nfaces.Count())>0) {
		isave->BeginChunk(TOPO_NFACES_CHUNK);
		isave->Write(em->tdelta.nfaces.Addr(0),n*sizeof(Face),&nb);
		isave->EndChunk();
		}

	if ((n=em->tdelta.nTVfaces.Count())>0) {
		isave->BeginChunk(TOPO_NTVFACES_CHUNK);
		isave->Write(em->tdelta.nTVfaces.Addr(0),n*sizeof(TVFace),&nb);
		isave->EndChunk();
		}

	if ((n=em->tdelta.map.Count())>0) {
		FaceMap *fm  = em->tdelta.map.Addr(0);
		BOOL allz=TRUE;
		for (int i=0; i<n; i++) {
			if (fm[i].v[0]!=UNDEFINED || fm[i].v[1]!=UNDEFINED || fm[i].v[2]!=UNDEFINED) { 
				allz = FALSE; 
				break; 
				}
			}
		if (allz) {
			isave->BeginChunk(TOPO_FACEMAPUNDEF_CHUNK);
			isave->Write(&n,sizeof(n),&nb);
			isave->EndChunk();
			}
		else {		
			isave->BeginChunk(TOPO_FACEMAP_CHUNK);
			isave->Write(em->tdelta.map.Addr(0)->v,n*sizeof(FaceMap),&nb);
			isave->EndChunk();
			}
		}

	if ((n=em->tdelta.tmap.Count())>0) {
		FaceMap *fm  = em->tdelta.tmap.Addr(0);
		BOOL allz=TRUE;
		for (int i=0; i<n; i++) {
			if (fm[i].v[0]!=UNDEFINED || fm[i].v[1]!=UNDEFINED || fm[i].v[2]!=UNDEFINED) { 
				allz = FALSE; 
				break; 
				}
			}
		if (allz) {
			isave->BeginChunk(TOPO_TVFACEMAPUNDEF_CHUNK);
			isave->Write(&n,sizeof(n),&nb);
			isave->EndChunk();
			}
		else {
			isave->BeginChunk(TOPO_TVFACEMAP_CHUNK);
			isave->Write(em->tdelta.tmap.Addr(0)->v,n*sizeof(FaceMap),&nb);
			isave->EndChunk();
			}
		}

	if ((n=em->tdelta.dverts.Count())>0) {
		isave->BeginChunk(TOPO_DVERTS_CHUNK);
		isave->Write(em->tdelta.dverts.Addr(0),n*sizeof(DWORD),&nb);
		isave->EndChunk();
		}

	if ((n=em->tdelta.dfaces.Count())>0) {
		isave->BeginChunk(TOPO_DFACES_CHUNK);
		isave->Write(em->tdelta.dfaces.Addr(0),n*sizeof(DWORD),&nb);
		isave->EndChunk();
		}

	if ((n=em->tdelta.attribs.Count())>0) {
		DWORD *dw  = em->tdelta.attribs.Addr(0);
		BOOL allz=TRUE;
		for (int i=0; i<n; i++) {
			if (dw[i]!=0) { allz = FALSE; 	break; 		}
			}
		if (allz) {
			isave->BeginChunk(TOPO_ATTRIBSZERO_CHUNK);
			isave->Write(&n,sizeof(n),&nb);
			isave->EndChunk();
			}
		else {
			isave->BeginChunk(TOPO_ATTRIBS_CHUNK);
			isave->Write(em->tdelta.attribs.Addr(0),n*sizeof(DWORD),&nb);
			isave->EndChunk();
			}
		}

	if ((n=em->tdelta.smgroups.Count())>0) {
		DWORD *dw  = em->tdelta.smgroups.Addr(0);
		BOOL allz=TRUE;
		DWORD sg0 = dw[0];
		for (int i=0; i<n; i++) {
			if (dw[i]!=sg0) { allz = FALSE; break; 	}
			}
		if (allz) {
			isave->BeginChunk(TOPO_SMGROUPSSAME_CHUNK);
			isave->Write(&n,sizeof(n),&nb);
			isave->Write(&sg0,sizeof(sg0),&nb);
			isave->EndChunk();
			}
		else {
			isave->BeginChunk(TOPO_SMGROUPS_CHUNK);
			isave->Write(em->tdelta.smgroups.Addr(0),n*sizeof(DWORD),&nb);
			isave->EndChunk();
			}
		}

	isave->BeginChunk(TOPO_INPUTFACES_CHUNK);
	isave->Write(&em->tdelta.inputFaces,sizeof(DWORD), &nb);
	isave->EndChunk();

	isave->BeginChunk(TOPO_INPUTVERTS_CHUNK);
	isave->Write(&em->tdelta.inputVerts,sizeof(DWORD), &nb);
	isave->EndChunk();

	isave->EndChunk();

	return IO_OK;
	}

IOResult EditMeshMod::LoadLocalData(ILoad *iload, LocalModData **pld) {
	ULONG nb;
	int n;
	IOResult res;
	EditMeshData *em;
	if (*pld==NULL) {
		*pld =(LocalModData *) new EditMeshData();
		}
	em = (EditMeshData *)*pld;

	while (IO_OK==(res=iload->OpenChunk())) {
		switch(iload->CurChunkID())  {
			case FLAGS_CHUNK:
				res = iload->Read(&em->flags,sizeof(DWORD), &nb);
				break;
			case VSEL_CHUNK:
			   	res = em->vsel.Load(iload);
				break;
			case FSEL_CHUNK:
			   	res = em->fsel.Load(iload);
				break;
			case ESEL_CHUNK:
			   	res = em->esel.Load(iload);
				break;
			
			case VSELSET_CHUNK:
				res = em->vselSet.Load(iload);
				break;
			case FSELSET_CHUNK:
				res = em->fselSet.Load(iload);
				break;
			case ESELSET_CHUNK:
				res = em->eselSet.Load(iload);
				break;

			case VERT_HIDE_CHUNK:
				res = em->vdelta.hide.Load(iload);
				break;
			case VERT_DELTA_CHUNK:
				n = iload->CurChunkLength()/sizeof(Point3);
				em->vdelta.deltas.SetCount(n);
				res = iload->Read(em->vdelta.deltas.Addr(0),n*sizeof(AnimPoint3),&nb);
				break;
			case VERT_DELTAZERO_CHUNK: {
				res = iload->Read(&n,sizeof(n),&nb);
				em->vdelta.deltas.SetCount(n);
				Point3 *pt  = em->vdelta.deltas.Addr(0);
				Point3 pzero(0,0,0);
				for (int i=0; i<n; i++) pt[i] = pzero;
				}
				break;
			case TOPO_DELTA_CHUNK:
				while (IO_OK==(res=iload->OpenChunk())) {
					switch(iload->CurChunkID())  {
						case TOPO_NVERTS_CHUNK:
							n = iload->CurChunkLength()/sizeof(Point3);
							em->tdelta.nverts.SetCount(n);
							res = iload->Read(em->tdelta.nverts.Addr(0),n*sizeof(Point3),&nb);
							break;
						case TOPO_NTVERTS_CHUNK:
							n = iload->CurChunkLength()/sizeof(Point3);
							em->tdelta.nTverts.SetCount(n);
							res = iload->Read(em->tdelta.nTverts.Addr(0),n*sizeof(Point3),&nb);
							break;
						case TOPO_CVERTS_CHUNK:
							n = iload->CurChunkLength()/sizeof(DWORD);
							em->tdelta.cverts.SetCount(n);
							res = iload->Read(em->tdelta.cverts.Addr(0),n*sizeof(DWORD),&nb);
							break;
						case TOPO_NFACES_CHUNK:
							n = iload->CurChunkLength()/sizeof(Face);
							em->tdelta.nfaces.SetCount(n);
							res = iload->Read(em->tdelta.nfaces.Addr(0),n*sizeof(Face),&nb);
							break;
						case TOPO_NTVFACES_CHUNK:
							n = iload->CurChunkLength()/sizeof(TVFace);
							em->tdelta.nTVfaces.SetCount(n);
							res = iload->Read(em->tdelta.nTVfaces.Addr(0),n*sizeof(TVFace),&nb);
							break;
						case TOPO_FACEMAP_CHUNK:
							n = iload->CurChunkLength()/sizeof(FaceMap);
							em->tdelta.map.SetCount(n);
							res = iload->Read(em->tdelta.map.Addr(0),n*sizeof(FaceMap),&nb);
							break;
						case TOPO_FACEMAPUNDEF_CHUNK:  {
							res = iload->Read(&n,sizeof(n),&nb);
							em->tdelta.map.SetCount(n);
							FaceMap *fm  = em->tdelta.map.Addr(0);
							for (int i=0; i<n; i++) {
								fm[i].v[0] = fm[i].v[1] = fm[i].v[2] = UNDEFINED;
								}
							}
							break;

						case TOPO_TVFACEMAP_CHUNK:
							n = iload->CurChunkLength()/sizeof(FaceMap);
							em->tdelta.tmap.SetCount(n);
							res = iload->Read(em->tdelta.tmap.Addr(0),n*sizeof(FaceMap),&nb);
							break;

						case TOPO_TVFACEMAPUNDEF_CHUNK:  {
							res = iload->Read(&n,sizeof(n),&nb);
							em->tdelta.tmap.SetCount(n);
							FaceMap *fm  = em->tdelta.tmap.Addr(0);
							for (int i=0; i<n; i++) {
								fm[i].v[0] = fm[i].v[1] = fm[i].v[2] = UNDEFINED;
								}
							}
							break;

						case TOPO_DVERTS_CHUNK:
							n = iload->CurChunkLength()/sizeof(DWORD);
							em->tdelta.dverts.SetCount(n);
							res = iload->Read(em->tdelta.dverts.Addr(0),n*sizeof(DWORD),&nb);
							break;
						case TOPO_DFACES_CHUNK:
							n = iload->CurChunkLength()/sizeof(DWORD);
							em->tdelta.dfaces.SetCount(n);
							res = iload->Read(em->tdelta.dfaces.Addr(0),n*sizeof(DWORD),&nb);
							break;
						case TOPO_ATTRIBS_CHUNK:
							n = iload->CurChunkLength()/sizeof(DWORD);
							em->tdelta.attribs.SetCount(n);
							res = iload->Read(em->tdelta.attribs.Addr(0),n*sizeof(DWORD),&nb);
							break;
						case TOPO_ATTRIBSZERO_CHUNK:
							{
							res = iload->Read(&n,sizeof(n),&nb);
							em->tdelta.attribs.SetCount(n);
							DWORD *dw  = em->tdelta.attribs.Addr(0);
							for (int i=0; i<n; i++)  dw[i] = 0;
							}
							break;
						case TOPO_SMGROUPS_CHUNK:
							n = iload->CurChunkLength()/sizeof(DWORD);
							em->tdelta.smgroups.SetCount(n);
							res = iload->Read(em->tdelta.smgroups.Addr(0),n*sizeof(DWORD),&nb);
							break;
						case TOPO_SMGROUPSSAME_CHUNK:
							{
							DWORD sg;
							res = iload->Read(&n,sizeof(n),&nb);
							res = iload->Read(&sg,sizeof(sg),&nb);
							em->tdelta.smgroups.SetCount(n);
							DWORD *dw  = em->tdelta.smgroups.Addr(0);
							for (int i=0; i<n; i++)  dw[i] = sg;
							}
							break;
						case TOPO_INPUTVERTS_CHUNK:
							res = iload->Read(&em->tdelta.inputVerts,sizeof(DWORD), &nb);
							break;	
						case TOPO_INPUTFACES_CHUNK:
							res = iload->Read(&em->tdelta.inputFaces,sizeof(DWORD), &nb);
							break;	
						}
					iload->CloseChunk();
					if ( res!=IO_OK) 
					return res;
					}
				if (res==IO_ERROR) return res;
				res = IO_OK;
				break;
			}
		iload->CloseChunk();
		if (res!=IO_OK) 
			return res;
		}
	return IO_OK;
	}





