//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Structured Solid (CSSolid) implementation.
//
// Method of identifying different parts of solid (vertices/edges/faces) is 
// a unique-id system. The AddFace/AddEdge/AddVertex functions assign each
// new "part" an id using GetNewID(). External objects referencing the CSSolid
// do not have to worry about keeping track of indices into the private 
// arrays, since an ID is valid only if the part still exists. To get 
// information about an ID, use the GetHandleInfo() function -> it returns
// FALSE if the given ID is no longer valid.
//
//=============================================================================

#include "stdafx.h"
#include "BrushOps.h"
#include "GameConfig.h"
#include "MapSolid.h"
#include "MapWorld.h"
#include "SSolid.h"
#include "StockSolids.h"
#include "Options.h"
#include "WorldSize.h"
#include "mapdisp.h"

// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>

BOOL CheckFace(Vector *Points, int nPoints, Vector* pNormal, float dist, CCheckFaceInfo *pInfo)
{
	int		 j;
	float	d, edgedist;
	Vector	dir, edgenormal;

	if(!pInfo)
	{
		static CCheckFaceInfo dummyinfo;
		pInfo = &dummyinfo;
		pInfo->iPoint = -1;	// make sure it's reset to default
	}

	if(pInfo->iPoint == -2)
		return TRUE;	// stop!!!!!

	// do we need to create a normal?
	if(!pNormal)
	{
		static Vector _normal;
		pNormal = &_normal;

		// calc a plane from the points
		Vector t1, t2, t3;

		for(int i = 0; i < 3; i++)
		{
			t1[i] = Points[0][i] - Points[1][i];
			t2[i] = Points[2][i] - Points[1][i];
			t3[i] = Points[1][i];
		}

		CrossProduct(t1, t2, *pNormal);
		VectorNormalize(*pNormal);
		dist = DotProduct(t3, *pNormal);
	}

	if(!nPoints)
	{
		strcpy(pInfo->szDescription, "no points");
		pInfo->iPoint = -2;
		return FALSE;
	}

	if(nPoints < 3)
	{
		strcpy(pInfo->szDescription, "fewer than three points");
		pInfo->iPoint = -2;
		return FALSE;
	}

	for(int i = pInfo->iPoint + 1; i < nPoints; i++ )
	{
		pInfo->iPoint = i;

		Vector& p1 = Points[i];

		for (j=0 ; j<3 ; j++)
		{
			if (p1[j] > MAX_COORD_INTEGER || p1[j] < MIN_COORD_INTEGER)
			{
				strcpy(pInfo->szDescription, "out of range");
				return FALSE;
			}
		}

		// check the point is on the face plane
		d = DotProduct (p1, *pNormal) - dist;
		if (d < -ON_PLANE_EPSILON || d > ON_PLANE_EPSILON)
		{
			strcpy(pInfo->szDescription, "point off plane");
			return FALSE;
		}

		// check the edge isn't degenerate
		Vector& p2 = Points[i+1 == nPoints ? 0 : i+1];	// (next point)
		VectorSubtract (p2, p1, dir);

		if (VectorLength (dir) < MIN_EDGE_LENGTH_EPSILON)
		{
			strcpy(pInfo->szDescription, "edge is too small");
			return FALSE;
		}

		CrossProduct(*pNormal, dir, edgenormal);
		VectorNormalize (edgenormal);
		edgedist = DotProduct(p1, edgenormal);
		edgedist += ON_PLANE_EPSILON;

		// all other points must be on front side
		for (j=0 ; j< nPoints; j++)
		{
			if (j == i)
				continue;
			d = DotProduct (Points[j], edgenormal);
			if (d > edgedist)
			{
				strcpy(pInfo->szDescription, "face is not convex");
				return FALSE;
			}
		}
	}

	pInfo->iPoint = -2;
	return TRUE;
}


//-----------------------------------------------------------------------------
// Purpose: Constructor.
//-----------------------------------------------------------------------------
CSSolid::CSSolid()
{
	m_nVertices = 0;
	m_nEdges = 0;
	m_nFaces = 0;
	m_curid = 1;
	m_pMapSolid = NULL;
	m_bShowVertices = TRUE;
	m_bShowEdges = TRUE;
}


//-----------------------------------------------------------------------------
// Purpose: Destructor.
//-----------------------------------------------------------------------------
CSSolid::~CSSolid()
{
	memset(this, 0, sizeof(this));
}


SSHANDLE CSSolid::GetNewID()
{
	return m_curid++;
}


PVOID CSSolid::GetHandleData(SSHANDLE id)
{
	SSHANDLEINFO hi;
	if(!GetHandleInfo(&hi, id))
		return NULL;
	return hi.pData;
}


BOOL CSSolid::GetHandleInfo(SSHANDLEINFO *pInfo, SSHANDLE id)
{
	// try vertices .. 
	for(int i = 0; i < m_nVertices; i++)
	{
		if(m_Vertices[i].id != id)
			continue;	// not this one

		pInfo->Type = shtVertex;
		pInfo->iIndex = i;
		pInfo->pData = PVOID(& m_Vertices[i]);
		pInfo->p2DHandle = & m_Vertices[i];
		pInfo->pos = m_Vertices[i].pos;

		return TRUE;
	}

	// try edges .. 
	for(int i = 0; i < m_nEdges; i++)
	{
		if(m_Edges[i].id != id)
			continue;	// not this one

		pInfo->Type = shtEdge;
		pInfo->iIndex = i;
		pInfo->pData = PVOID(& m_Edges[i]);
		pInfo->p2DHandle = & m_Edges[i];
		pInfo->pos = m_Edges[i].ptCenter;

		return TRUE;
	}

	// try faces ..
	for(int i = 0; i < m_nFaces; i++)
	{
		if(m_Faces[i].id != id)
			continue;	// not this one

		pInfo->Type = shtFace;
		pInfo->iIndex = i;
		pInfo->pData = PVOID(& m_Faces[i]);
		pInfo->p2DHandle = & m_Faces[i];
		pInfo->pos = m_Faces[i].ptCenter;

		return TRUE;
	}

	pInfo->Type = shtNothing;
	return FALSE;
}


// Find data functions ->
int CSSolid::GetEdgeIndex(SSHANDLE v1, SSHANDLE v2)
{
	for(int i = 0; i < m_nEdges; i++)
	{
		CSSEdge & theEdge = m_Edges[i];
		if((theEdge.hvStart == v1 && theEdge.hvEnd == v2) ||
			(theEdge.hvStart == v2 && theEdge.hvEnd == v1))
		{
			return i;
		}
	}
	return -1;
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : Point - 
//			fLeniency - 
// Output : 
//-----------------------------------------------------------------------------
int CSSolid::GetEdgeIndex(const Vector &Point, float fLeniency)
{
	for (int i = 0; i < m_nEdges; i++)
	{
		Vector ptEdgeCenter = m_Edges[i].ptCenter;

		float fDiff = 0.0f;
		for (int j = 0; j < 3; j++)
		{
			fDiff += (Point[j] - ptEdgeCenter[j]) * (Point[j] - ptEdgeCenter[j]);
		}

		if (fDiff > fLeniency * fLeniency)
		{
			continue;
		}
		
		// if we are here, the 3 axes compare ok.
		return i;
	}

	// no edge matches
	return -1;
}


int CSSolid::GetVertexIndex(const Vector &Point, float fLeniency)
{
	for(int i = 0; i < m_nVertices; i++)
	{
		Vector Vertex = m_Vertices[i].pos;

		float fDiff = 0.0f;
		for(int j = 0; j < 3; j++)
		{
			fDiff += (Point[j] - Vertex[j]) * (Point[j] - Vertex[j]);
		}

		if (fDiff > (fLeniency*fLeniency))
			continue;
			
		// if we are here, the 3 axes compare ok.
		return i;
	}

	// no vertex matches.
	return -1;
}


int CSSolid::GetFaceIndex(const Vector &Point, float fLeniency)
{
	return -1;
}


//-----------------------------------------------------------------------------
// Purpose: Calculates the center of an edge.
// Input  : pEdge - 
//-----------------------------------------------------------------------------
void CSSolid::CalcEdgeCenter(CSSEdge *pEdge)
{
	SSHANDLEINFO hi;

	GetHandleInfo(&hi, pEdge->hvStart);
	Vector &pt1 = m_Vertices[hi.iIndex].pos;

	GetHandleInfo(&hi, pEdge->hvEnd);
	Vector &pt2 = m_Vertices[hi.iIndex].pos;

	for (int i = 0; i < 3; i++)
	{
		pEdge->ptCenter[i] = (pt1[i] + pt2[i]) / 2.0f;
	}
}

// get common vertex from two edges ->

SSHANDLE CSSolid::GetConnectionVertex(CSSEdge *pEdge1, CSSEdge *pEdge2)
{
	if((pEdge1->hvStart == pEdge2->hvStart) || 
		(pEdge1->hvStart == pEdge2->hvEnd))
		return pEdge1->hvStart;

	if((pEdge1->hvEnd == pEdge2->hvStart) || 
		(pEdge1->hvEnd == pEdge2->hvEnd))
		return pEdge1->hvEnd;

	return 0;
}


// Create list of points from face ->
Vector * CSSolid::CreatePointList(CSSFace & face)
{
	Vector * pts = new Vector[face.nEdges+1];

	for(int i = 0; i < face.nEdges; i++)
	{
		// calc next edge so we can see which is the next clockwise point
		int iNextEdge = i+1;
		if(iNextEdge == face.nEdges)
			iNextEdge = 0;

		CSSEdge * edgeCur = (CSSEdge*) GetHandleData(face.Edges[i]);
		CSSEdge * edgeNext = (CSSEdge*) GetHandleData(face.Edges[iNextEdge]);

		if(!edgeCur || !edgeNext)
		{
			CString str;
			str.Format("Conversion error!\n"
				"edgeCur = %p, edgeNext = %p", edgeCur, edgeNext);
			AfxMessageBox(str);
			return NULL;
		}

		SSHANDLE hVertex = GetConnectionVertex(edgeCur, edgeNext);

		if(!hVertex)
		{
			CString str;
			str.Format("Conversion error!\n"
				"hVertex = %08X", hVertex);
			AfxMessageBox(str);
			return NULL;
		}

		CSSVertex *pVertex = (CSSVertex*) GetHandleData(hVertex);

		pts[i] = pVertex->pos;
	}

	return pts;
}


// Create point list, but return indices instead of positions ->
PINT CSSolid::CreatePointIndexList(CSSFace & face, PINT piPoints)
{
	PINT pts;
	if(piPoints)
		pts = piPoints;
	else
		pts = new int[face.nEdges+1];

	SSHANDLEINFO hi;

	for(int i = 0; i < face.nEdges; i++)
	{
		// calc next edge so we can see which is the next clockwise point
		int iNextEdge = i+1;
		if(iNextEdge == face.nEdges)
			iNextEdge = 0;

		CSSEdge * edgeCur = (CSSEdge*) GetHandleData(face.Edges[i]);
		CSSEdge * edgeNext = (CSSEdge*)  GetHandleData(face.Edges[iNextEdge]);

		SSHANDLE hVertex = GetConnectionVertex(edgeCur, edgeNext);
		Assert(hVertex);

		GetHandleInfo(&hi, hVertex);
		pts[i] = hi.iIndex;
	}

	return pts;
}

// Create point list, and use handles ->

SSHANDLE* CSSolid::CreatePointHandleList(CSSFace & face, SSHANDLE* phPoints)
{
	SSHANDLE* pts;
	if(phPoints)
		pts = phPoints;
	else
		pts = new SSHANDLE[face.nEdges+1];

	for(int i = 0; i < face.nEdges; i++)
	{
		// calc next edge so we can see which is the next clockwise point
		int iNextEdge = i+1;
		if(iNextEdge == face.nEdges)
			iNextEdge = 0;

		CSSEdge * edgeCur = (CSSEdge*) GetHandleData(face.Edges[i]);
		CSSEdge * edgeNext = (CSSEdge*) GetHandleData(face.Edges[iNextEdge]);

		SSHANDLE hVertex = GetConnectionVertex(edgeCur, edgeNext);
		Assert(hVertex);

		pts[i] = hVertex;
	}

	return pts;
}


void CSSolid::Attach(CMapSolid *pMapSolid)
{
	m_pMapSolid = pMapSolid;
}


CMapSolid *CSSolid::Detach()
{
	CMapSolid *pTmp = m_pMapSolid;
	m_pMapSolid = NULL;
	return pTmp;
}

//-----------------------------------------------------------------------------
// Purpose: Returns whether or not the SSolid has displacements.
//-----------------------------------------------------------------------------
bool CSSolid::HasDisps( void )
{
	for ( int iFace = 0; iFace < m_nFaces; ++iFace )
	{
		CSSFace *pFace = &m_Faces[iFace];
		if ( pFace->m_hDisp != EDITDISPHANDLE_INVALID )
			return true;
	}

	return false;
}

//-----------------------------------------------------------------------------
// Purpose: Check to see if the SSolid with displacement surfaces has valid 
//          base face surfaces.
//-----------------------------------------------------------------------------
bool CSSolid::IsValidWithDisps( void )
{
	if ( !HasDisps() )
		return true;

	for ( int iFace = 0; iFace < m_nFaces; ++iFace )
	{
		// Get the face(s) that have displacements.
		CSSFace *pFace = &m_Faces[iFace];
		if ( pFace->m_hDisp == EDITDISPHANDLE_INVALID )
			continue;

		// Create a face point list.
		Vector *pFacePoints = CreatePointList( *pFace );

		// If the face has changed the number of points - via merges, etc.
		if ( pFace->nEdges != 4 )
			return false;

		// Check the face for validity.
		CCheckFaceInfo faceInfo;
		if ( !CheckFace( pFacePoints, pFace->nEdges, NULL, 0.0f, &faceInfo ) )
			return false;
	}

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: Destroy all the displacement data on the SSolid.
//-----------------------------------------------------------------------------
void CSSolid::DestroyDisps( void )
{
	for ( int iFace = 0; iFace < m_nFaces; ++iFace )
	{
		CSSFace *pFace = &m_Faces[iFace];
		if ( pFace->m_hDisp != EDITDISPHANDLE_INVALID )
		{
			EditDispMgr()->Destroy( pFace->m_hDisp );
			pFace->m_hDisp = EDITDISPHANDLE_INVALID;
		}
	}
}

void CSSolid::Convert(BOOL bFromMap, bool bSkipDisplacementFaces )
{
	if(bFromMap)
		FromMapSolid(NULL, bSkipDisplacementFaces);
	else
		ToMapSolid();
}


void CSSolid::ToMapSolid(CMapSolid *p)
{
	// so we can pass NULL (default) or another solid (to copy):
	CMapSolid *pSolid;
	if (p)
	{
		pSolid = p;
	}
	else
	{
		pSolid = m_pMapSolid;
	}

	pSolid->SetFaceCount(m_nFaces);

	unsigned char r, g, b;
	pSolid->GetRenderColor( r,g,b );

	for (int i = 0; i < m_nFaces; i++)
	{
		CSSFace &pFace = m_Faces[i];
		CMapFace SolidFace;

		//
		// Copy original texture information and face ID back.
		//
		Q_memcpy(&SolidFace.texture, &pFace.texture, sizeof(TEXTURE));
		SolidFace.SetTexture(SolidFace.texture.texture);
		SolidFace.SetFaceID(pFace.m_nFaceID);

		//
		// Create face from new points.
		//
		Vector *pts = CreatePointList(pFace);
		
		SolidFace.CreateFace(pts, pFace.nEdges);

		//
		// Vertex manipulation; the face orientation may have changed. If one of the texture axes is now
		// perpendicular to the face, recalculate the texture axes using the default alignment (world or face).
		// Ideally we would transform the texture axes so that their orientation relative to the face is preserved.
		// By reinitializing the axes we risk having the axes rotate unpredictably.
		//
		if (!SolidFace.IsTextureAxisValid())
		{
			SolidFace.InitializeTextureAxes(Options.GetTextureAlignment(), INIT_TEXTURE_AXES | INIT_TEXTURE_FORCE);
		}
			
		// Attempt to update the displacement - if there is one.
		if ( pFace.m_hDisp != EDITDISPHANDLE_INVALID )
		{
			EditDispHandle_t hDisp = EditDispMgr()->Create();
			CMapDisp *pSolidDisp = EditDispMgr()->GetDisp( hDisp );
			CMapDisp *pDisp = EditDispMgr()->GetDisp( pFace.m_hDisp );
			pSolidDisp->CopyFrom( pDisp, false );
			int iStart = pSolidDisp->GetSurfPointStartIndex();
			pSolidDisp->SetSurfPointStartIndex( (iStart+3)%4 );
			pSolidDisp->InitDispSurfaceData( &SolidFace, false );
			pSolidDisp->Create();
			SolidFace.SetDisp( hDisp );
		}

		CMapFace *pNewFace = pSolid->GetFace( i );
		pNewFace->CopyFrom( &SolidFace, COPY_FACE_POINTS);
		pNewFace->SetRenderColor(r, g, b);
		pNewFace->SetParent(pSolid);

		delete[] pts;
	}

	pSolid->PostUpdate(Notify_Changed);
}


CSSFace* CSSolid::AddFace(int* piNewIndex)
{
	m_Faces.SetCount(++m_nFaces);
	if(piNewIndex)
		piNewIndex[0] = m_nFaces-1;
	CSSFace *pFace = & m_Faces[m_nFaces-1];
	pFace->id = GetNewID();
	return pFace;
}

// Add Edge ->

CSSEdge* CSSolid::AddEdge(int* piNewIndex)
{
	m_Edges.SetCount(++m_nEdges);
	if(piNewIndex)
		piNewIndex[0] = m_nEdges-1;
	CSSEdge *pEdge = & m_Edges[m_nEdges-1];
	pEdge->id = GetNewID();
	return pEdge;
}

// Add Vertex ->

CSSVertex* CSSolid::AddVertex(int* piNewIndex)
{
	m_Vertices.SetCount(++m_nVertices);
	if(piNewIndex)
		piNewIndex[0] = m_nVertices-1;
	CSSVertex *pVertex = & m_Vertices[m_nVertices-1];
	pVertex->id = GetNewID();
	return pVertex;
}

// Assign a face to an edge ->

void CSSolid::AssignFace(CSSEdge* pEdge, SSHANDLE hFace, BOOL bRemove)
{
	if(!bRemove)
	{
		if(pEdge->Faces[0] == 0 || pEdge->Faces[0] == hFace)
			pEdge->Faces[0] = hFace;
		else if(pEdge->Faces[1] == 0)
			pEdge->Faces[1] = hFace;
	}
	else
	{
		if(pEdge->Faces[0] == hFace)
			pEdge->Faces[0] = 0;
		if(pEdge->Faces[1] == hFace)
			pEdge->Faces[1] = 0;
	}
}

// Convert From Map Solid ->

void CSSolid::FromMapSolid(CMapSolid *p, bool bSkipDisplacementFaces)
{
	// so we can pass NULL (default) or another solid (to copy):
	CMapSolid *pSolid;
	if(p)
		pSolid = p;
	else
		pSolid = m_pMapSolid;

	m_nFaces = 0;
	m_nEdges = 0;
	m_nVertices = 0;

	// Create vertices, edges, faces.
	int nSolidFaces = pSolid->GetFaceCount();
	for(int i = 0; i < nSolidFaces; i++)
	{
		CMapFace *pSolidFace = pSolid->GetFace(i);

		if (bSkipDisplacementFaces)
		{
			if (pSolidFace->HasDisp())
				continue;
		}

		// Add a face
		CSSFace *pFace = AddFace();

		memcpy(pFace->PlanePts, pSolidFace->plane.planepts, sizeof(Vector) * 3);
		pFace->texture = pSolidFace->texture;
		pFace->normal = pSolidFace->plane.normal;
		pFace->m_nFaceID = pSolidFace->GetFaceID();

		// Displacement.
		if ( pSolidFace->HasDisp() )
		{
			pFace->m_hDisp = EditDispMgr()->Create();
			CMapDisp *pDisp = EditDispMgr()->GetDisp( pFace->m_hDisp );
			CMapDisp *pSolidDisp = EditDispMgr()->GetDisp( pSolidFace->GetDisp() );
			pDisp->CopyFrom( pSolidDisp, false );
		}

		// Convert vertices and edges
		int nFacePoints = pSolidFace->nPoints;
		Vector *pFacePoints = pSolidFace->Points;
		SSHANDLE hLastVertex = 0;	// valid IDs start at 1
		SSHANDLE hThisVertex, hFirstVertex = 0;
		for(int pt = 0; pt <= nFacePoints; pt++)
		{
			int iVertex;
			
			if(pt < nFacePoints)
			{
				// YWB:  Change leniency from 1.0 down to 0.1
				iVertex = GetVertexIndex(pFacePoints[pt], 0.1f);
				if (iVertex == -1)
				{
					// not found - add the vertex
					CSSVertex *pVertex = AddVertex(&iVertex);
					pVertex->pos = pFacePoints[pt];
				}

				// assign this vertex handle
				hThisVertex = m_Vertices[iVertex].id;

				if (pt == 0)
					hFirstVertex = hThisVertex;
			}
			else
			{
				// connect last to first
				hThisVertex = hFirstVertex;
			}

			if (hLastVertex)
			{
				// create the edge from the last vertex to current vertex.
				//  first check to see if this edge already exists.. 
				int iEdge = GetEdgeIndex(hLastVertex, hThisVertex);
				CSSEdge *pEdge;
				if (iEdge == -1)
				{
					// not found - add new edge
					pEdge = AddEdge(&iEdge);
					pEdge->hvStart = hLastVertex;
					pEdge->hvEnd   = hThisVertex;

					// make sure edge center is valid:
					CalcEdgeCenter(pEdge);
				}
				else
				{
					pEdge = &m_Edges[iEdge];
				}

				// add the edge to the face
				pFace->Edges[pFace->nEdges++] = pEdge->id;

				// set edge's face array
				if(!pEdge->Faces[0])
					pEdge->Faces[0] = pFace->id;
				else if(!pEdge->Faces[1])
					pEdge->Faces[1] = pFace->id;
				else
				{
					// YWB try filling in front side
					//  rather than Assert(0) crash
					pEdge->Faces[0] = pFace->id;
					AfxMessageBox("Edge with both face id's already filled, skipping...");
				}
			}

			hLastVertex = hThisVertex;
		}
	}
}

// Find edges that reference a vertex ->

CSSEdge ** CSSolid::FindAffectedEdges(SSHANDLE *pHandles, int iNumHandles, int& iNumEdges)
{
	static CSSEdge *ppEdges[128];
	iNumEdges = 0;

	for(int h = 0; h < iNumHandles; h++)
	{
		for(int i = 0; i < m_nEdges; i++)
		{
			CSSEdge *pEdge = &m_Edges[i];
			if(pEdge->hvStart == pHandles[h] || 
				pEdge->hvEnd == pHandles[h])
			{
				// ensure it's not already stored
				int s;
				for(s = 0; s < iNumEdges; s++)
				{
					if(ppEdges[s] == pEdge)
						break;
				}
				if(s == iNumEdges)
					ppEdges[iNumEdges++] = pEdge;
			}
		}
	}

	return ppEdges;
}


// tell drawing code to show/hide kinds of handles
void CSSolid::ShowHandles(BOOL bShowVertices, BOOL bShowEdges)
{
	m_bShowEdges = bShowEdges;
	m_bShowVertices = bShowVertices;
}


// Move handle(s) to a new location ->
void CSSolid::MoveSelectedHandles(const Vector &Delta)
{
	SSHANDLE MoveVertices[128];
	int nMoveVertices = 0;

	SSHANDLEINFO hi;
	
	for(int i = 0; i < m_nVertices; i++)
	{
		if(m_Vertices[i].m_bSelected)
			MoveVertices[nMoveVertices++] = m_Vertices[i].id;
	}

	for(int i = 0; i < m_nEdges; i++)
	{
		CSSEdge* pEdge = &m_Edges[i];

		if(!pEdge->m_bSelected)	// make sure it's selected
			continue;

		// add edge's vertices to the movement list
		BOOL bAddStart = TRUE, bAddEnd = TRUE;
		for(int i2 = 0; i2 < nMoveVertices; i2++)
		{
			if(pEdge->hvStart == MoveVertices[i2])
				bAddStart = FALSE;	// already got this one
			if(pEdge->hvEnd == MoveVertices[i2])
				bAddEnd = FALSE;	// already got this one
		}

		if(bAddStart)
			MoveVertices[nMoveVertices++] = pEdge->hvStart;
		if(bAddEnd)
			MoveVertices[nMoveVertices++] = pEdge->hvEnd;
	}

	// move vertices now
	for(int i = 0; i < nMoveVertices; i++)
	{
		GetHandleInfo(&hi, MoveVertices[i]);
		CSSVertex* pVertex = (CSSVertex*) hi.pData;
		SetVertexPosition(hi.iIndex, pVertex->pos[0] + Delta[0], pVertex->pos[1] + Delta[1], pVertex->pos[2] + Delta[2]);
	}

	// calculate center of moved edges
	int nEdges;
	CSSEdge ** ppEdges = FindAffectedEdges(MoveVertices, nMoveVertices, nEdges);
	for(int i = 0; i < nEdges; i++)
	{
		CalcEdgeCenter(ppEdges[i]);
	}
}


// check faces for irregularities ->
void CSSolid::CheckFaces()
{
	for(int i = 0; i < m_nFaces; i++)
	{
		CSSFace &face = m_Faces[i];

		// get points for face
		Vector *pts = CreatePointList(face);

		// call checkface function
		CCheckFaceInfo cfi;

		while(CheckFace(pts, face.nEdges, NULL, 0, &cfi) == FALSE)
		{
			CString str;
			str.Format("face %d - %s", i, cfi.szDescription);
			AfxMessageBox(str);
		}

		delete[] pts;
	}
}


void CSSolid::SetVertexPosition(int iVertex, float x, float y, float z)
{
	m_Vertices[iVertex].pos = Vector(x, y, z);
}


static int GetNext(int iIndex, int iDirection, int iMax)
{
	iIndex += iDirection;
	if(iIndex == iMax)
		iIndex = 0;
	if(iIndex == -1)
		iIndex = iMax-1;
	return iIndex;
}


BOOL CSSolid::SplitFace(SSHANDLE h1, SSHANDLE h2)
{
	SSHANDLEINFO hi;
	GetHandleInfo(&hi, h1);

	if(m_nFaces == MAX_FACES-1)
		return FALSE;

	BOOL bRvl = FALSE;

	if(hi.Type == shtEdge)
	{
		// edge-based face split
		bRvl = SplitFaceByEdges((CSSEdge*) hi.pData, 
			(CSSEdge*) GetHandleData(h2));
	}
	else if(hi.Type == shtVertex)
	{
		// vertex-based face split
		bRvl = SplitFaceByVertices((CSSVertex*) hi.pData, 
			(CSSVertex*) GetHandleData(h2));
	}

	return bRvl;
}


BOOL CSSolid::SplitFaceByVertices(CSSVertex *pVertex1, CSSVertex *pVertex2)
{
	if(GetEdgeIndex(pVertex1->id, pVertex2->id) != -1)
		return FALSE;	// already an edge there!

	// find the face, first - get a list of affected edges and find
	//  two with a common face
	int iNumEdges1, iNumEdges2;
	SSHANDLE hFace = 0;
	CSSEdge *pEdges1[64], *pEdges2[64], **pTmp;

	pTmp = FindAffectedEdges(&pVertex1->id, 1, iNumEdges1);
	memcpy(pEdges1, pTmp, iNumEdges1 * sizeof(CSSEdge*));
	pTmp = FindAffectedEdges(&pVertex2->id, 1, iNumEdges2);
	memcpy(pEdges2, pTmp, iNumEdges2 * sizeof(CSSEdge*));

	for(int i = 0; i < iNumEdges1; i++)
	{
		SSHANDLE hFace0 = pEdges1[i]->Faces[0];
		SSHANDLE hFace1 = pEdges1[i]->Faces[1];
		for(int i2 = 0; i2 < iNumEdges2; i2++)
		{
			if(hFace0 == pEdges2[i2]->Faces[0] ||
				hFace0 == pEdges2[i2]->Faces[1])
			{
				hFace = hFace0;
				break;
			}
			else if(hFace1 == pEdges2[i2]->Faces[0] ||
				hFace1 == pEdges2[i2]->Faces[1])
			{
				hFace = hFace1;
				break;
			}
		}
	}

	// couldn't find a common face
	if(hFace == 0)
		return FALSE;

	CSSFace *pFace = (CSSFace*) GetHandleData(hFace);

	// create a new face
	CSSFace *pNewFace = AddFace();
	memcpy(&pNewFace->texture, &pFace->texture, sizeof(TEXTURE));

	// create a new edge between two vertices
	CSSEdge *pNewEdge = AddEdge();
	pNewEdge->hvStart = pVertex1->id;
	pNewEdge->hvEnd = pVertex2->id;
	CalcEdgeCenter(pNewEdge);

	// assign face ids to the new edge
	AssignFace(pNewEdge, pFace->id);
	AssignFace(pNewEdge, pNewFace->id);

	// set up edges - start with newvertex1 
	SSHANDLE hNewEdges[64];
	int nNewEdges;
	BOOL bFirst = TRUE;
	CSSFace *pStoreFace = pFace;

	SSHANDLE *phVertexList = CreatePointHandleList(*pFace);
	int nVertices = pFace->nEdges;

	int v1index = 0, v2index = 0;

	// find where the vertices are and
	//  kill face references in edges first
	for(int i = 0; i < nVertices; i++)
	{
		int iNextVertex = GetNext(i, 1, nVertices);
		int iEdgeIndex = GetEdgeIndex(phVertexList[i], 
			phVertexList[iNextVertex]);
		CSSEdge *pEdge = &m_Edges[iEdgeIndex];
		AssignFace(pEdge, pFace->id, TRUE);

		if(phVertexList[i] == pVertex1->id)
			v1index = i;
		else if(phVertexList[i] == pVertex2->id)
			v2index = i;
	}

DoNextFace:
	nNewEdges = 0;
	for(int i = v1index; ; i++)
	{
		if(i == nVertices)
			i = 0;

		if(i == v2index)
			break;

		int iNextVertex = GetNext(i, 1, nVertices);
		int iEdgeIndex = GetEdgeIndex(phVertexList[i], phVertexList[iNextVertex]);
		Assert(iEdgeIndex != -1);

		hNewEdges[nNewEdges++] = m_Edges[iEdgeIndex].id;

		AssignFace(&m_Edges[iEdgeIndex], pFace->id);
	}
	// now add the middle edge
	hNewEdges[nNewEdges++] = pNewEdge->id;
	// now set up in face
	pStoreFace->nEdges = nNewEdges;
	memcpy(pStoreFace->Edges, hNewEdges, sizeof(SSHANDLE) * nNewEdges);

	if(bFirst)
	{
		int tmp = v1index;
		v1index = v2index;
		v2index = tmp;
		pStoreFace = pNewFace;
		bFirst = FALSE;
		goto DoNextFace;
	}

	delete phVertexList;

	return(TRUE);
}

BOOL CSSolid::SplitFaceByEdges(CSSEdge *pEdge1, CSSEdge *pEdge2)
{
	SSHANDLE hFace;

	// find the handle of the face
	if(pEdge1->Faces[0] == pEdge2->Faces[0] || 
		pEdge1->Faces[0] == pEdge2->Faces[1])
	{
		hFace = pEdge1->Faces[0];
	}
	else if(pEdge1->Faces[1] == pEdge2->Faces[0] || 
		pEdge1->Faces[1] == pEdge2->Faces[1])
	{
		hFace = pEdge1->Faces[1];
	}
	else return FALSE;	// not the same face

	// get pointer to face
	CSSFace *pFace = (CSSFace*) GetHandleData(hFace);

	// create new objects
	CSSFace *pNewFace = AddFace();
	CSSEdge *pNewEdgeMid = AddEdge();
	int iNewVertex1, iNewVertex2;
	CSSVertex *pNewVertex1 = AddVertex(&iNewVertex1);
	CSSVertex *pNewVertex2 = AddVertex(&iNewVertex2);

	// assign faces to new edge
	AssignFace(pNewEdgeMid, pFace->id);
	AssignFace(pNewEdgeMid, pNewFace->id);

	// copy texture info from one face to the other
	memcpy(&pNewFace->texture, &pFace->texture, sizeof(TEXTURE));

	// set vertex positions
	m_Vertices[iNewVertex1].pos = pEdge1->ptCenter;
	m_Vertices[iNewVertex2].pos = pEdge2->ptCenter;

	// set up middle edge
	pNewEdgeMid->hvStart = pNewVertex1->id;
	pNewEdgeMid->hvEnd = pNewVertex2->id;
	CalcEdgeCenter(pNewEdgeMid);

	// set up new side edges
	CSSEdge *pEdgeTmp = AddEdge();
	pEdgeTmp->hvStart = pEdge1->hvStart;
	pEdgeTmp->hvEnd = pNewVertex1->id;
	CalcEdgeCenter(pEdgeTmp);

	pEdgeTmp = AddEdge();
	pEdgeTmp->hvStart = pEdge1->hvEnd;
	pEdgeTmp->hvEnd = pNewVertex1->id;
	CalcEdgeCenter(pEdgeTmp);

	pEdgeTmp = AddEdge();
	pEdgeTmp->hvStart = pEdge2->hvStart;
	pEdgeTmp->hvEnd = pNewVertex2->id;
	CalcEdgeCenter(pEdgeTmp);

	pEdgeTmp = AddEdge();
	pEdgeTmp->hvStart = pEdge2->hvEnd;
	pEdgeTmp->hvEnd = pNewVertex2->id;
	CalcEdgeCenter(pEdgeTmp);

/*
	FILE *fp = fopen("split", "w");
	for(i = 0; i < nVertices; i++)
	{
		fprintf(fp, "%lu\n", phVertexList[i]);
	}
	fclose(fp);
*/

	// set up edges - start with newvertex1 
	SSHANDLE hNewEdges[64];
	int nNewEdges;
	BOOL bFirst = TRUE;
	CSSFace *pStoreFace = pFace;

	// ** do two new faces first **

	int nv1index, nv2index;
	SSHANDLE *phVertexList = CreateNewVertexList(pFace, pEdge1, pEdge2, 
		nv1index, nv2index, pNewVertex1, pNewVertex2);
	int nVertices = pFace->nEdges;
	if(nv1index != -1)
		++nVertices;
	if(nv2index != -1)
		++nVertices;

	// kill face references in edges first
	for(int i = 0; i < nVertices; i++)
	{
		int iNextVertex = GetNext(i, 1, nVertices);
		int iEdgeIndex = GetEdgeIndex(phVertexList[i], 
			phVertexList[iNextVertex]);
		CSSEdge *pEdge = &m_Edges[iEdgeIndex];
		Assert(pEdge->id != pEdge1->id);
		Assert(pEdge->id != pEdge2->id);
		AssignFace(pEdge, pFace->id, TRUE);
	}

DoNextFace:
	nNewEdges = 0;
	for(int i = nv1index; ; i++)
	{
		if(i == nVertices)
			i = 0;

		if(i == nv2index)
			break;

		int iNextVertex = GetNext(i, 1, nVertices);
		int iEdgeIndex = GetEdgeIndex(phVertexList[i], phVertexList[iNextVertex]);
		Assert(iEdgeIndex != -1);

		hNewEdges[nNewEdges++] = m_Edges[iEdgeIndex].id;

		AssignFace(&m_Edges[iEdgeIndex], pStoreFace->id);
	}
	// now add the middle edge
	hNewEdges[nNewEdges++] = pNewEdgeMid->id;
	// now set up in face
	pStoreFace->nEdges = nNewEdges;
	memcpy(pStoreFace->Edges, hNewEdges, sizeof(SSHANDLE) * nNewEdges);

	if(bFirst)
	{
		int tmp = nv1index;
		nv1index = nv2index;
		nv2index = tmp;
		pStoreFace = pNewFace;
		bFirst = FALSE;
		goto DoNextFace;
	}

	delete phVertexList;

	// ** now regular faces **
	for(int iFace = 0; iFace < m_nFaces; iFace++)
	{
		CSSFace *pUpdFace = &m_Faces[iFace];

		if(pUpdFace == pNewFace || pUpdFace == pFace)
			continue;
		
		phVertexList = CreateNewVertexList(pUpdFace, pEdge1, pEdge2,
			nv1index, nv2index, pNewVertex1, pNewVertex2);

		if(phVertexList == NULL)	// don't need to update this face
			continue;
	
		nNewEdges = 0;
		nVertices = pUpdFace->nEdges;
		if(nv1index != -1)
			++nVertices;
		if(nv2index != -1)
			++nVertices;
		for(int i = 0; i < nVertices; i++)
		{
			int iNextVertex = GetNext(i, 1, nVertices);
			int iEdgeIndex = GetEdgeIndex(phVertexList[i], phVertexList[iNextVertex]);
			Assert(iEdgeIndex != -1);

			AssignFace(&m_Edges[iEdgeIndex], pUpdFace->id);
			hNewEdges[nNewEdges++] = m_Edges[iEdgeIndex].id;
		}

		// now set up in face
		pUpdFace->nEdges = nNewEdges;
		memcpy(pUpdFace->Edges, hNewEdges, sizeof(SSHANDLE) * nNewEdges);

		delete phVertexList;
	}

	SSHANDLE id1 = pEdge1->id;
	SSHANDLE id2 = pEdge2->id;

	// delete old edges
	for(int i = 0; i < m_nEdges; i++)
	{
		if(m_Edges[i].id == id1 || m_Edges[i].id == id2)
		{
			DeleteEdge(i);
			--i;
		}
	}

	return TRUE;
}


void CSSolid::DeleteEdge(int iEdge)
{
	SSHANDLE edgeid = m_Edges[iEdge].id;
		
	// kill this edge
	for(int i2 = iEdge; i2 < m_nEdges-1; i2++)
	{
		memcpy(&m_Edges[i2], &m_Edges[i2+1], sizeof(CSSEdge));
	}
	--m_nEdges;

	memset(&m_Edges[m_nEdges], 0, sizeof(CSSEdge));

	// kill all references to this edge in faces
	for(int f = 0; f < m_nFaces; f++)
	{
		CSSFace& face = m_Faces[f];
		for(int e = 0; e < face.nEdges; e++)
		{
			if(face.Edges[e] != edgeid)
				continue;

			memcpy(&face.Edges[e], &face.Edges[e+1], (face.nEdges-e) * 
				sizeof(face.Edges[0]));
			--face.nEdges;
			break;	// no more in this face
		}
	}
}


void CSSolid::DeleteVertex(int iVertex)
{
	for(int i2 = iVertex; i2 < m_nVertices-1; i2++)
	{
		memcpy(&m_Vertices[i2], &m_Vertices[i2+1], sizeof(CSSVertex));
	}
	--m_nVertices;

	memset(&m_Vertices[m_nVertices], 0, sizeof(CSSVertex));
}


void CSSolid::DeleteFace(int iFace)
{
	// Destroy the displacement if there is one.
	CSSFace *pFace = &m_Faces[iFace];
	if ( pFace )
	{
		if ( pFace->m_hDisp != EDITDISPHANDLE_INVALID )
		{
			EditDispMgr()->Destroy( pFace->m_hDisp );
			pFace->m_hDisp = EDITDISPHANDLE_INVALID;
		}
	}

	for(int i2 = iFace; i2 < m_nFaces-1; i2++)
	{
		memcpy(&m_Faces[i2], &m_Faces[i2+1], sizeof(CSSFace));
	}
	--m_nFaces;

	m_Faces[m_nFaces].Init();
}


SSHANDLE* CSSolid::CreateNewVertexList(CSSFace *pFace, CSSEdge *pEdge1, 
									   CSSEdge *pEdge2, int& nv1index, int& nv2index,
									   CSSVertex *pNewVertex1, CSSVertex *pNewVertex2)
{
	// get original vertex list
	CUtlVector<SSHANDLE> hVertexList;
	hVertexList.SetCount(pFace->nEdges+4);
	CreatePointHandleList(*pFace, hVertexList.Base());

	// add vertex1 and vertex2.
	nv1index = -1;
	nv2index = -1;

	int nVertices = pFace->nEdges;
	int iPass = 0;
DoAgain:
	for(int i = 0; i < nVertices; i++)
	{
		int iPrevIndex = GetNext(i, -1, nVertices);
		int iNextIndex = GetNext(i, 1, nVertices);

		if(nv1index == -1 && (hVertexList[i] == pEdge1->hvEnd || 
			hVertexList[i] == pEdge1->hvStart))
		{
			// find pEdge1->hvStart
			if(hVertexList[iPrevIndex] == pEdge1->hvStart || 
				hVertexList[iPrevIndex] == pEdge1->hvEnd)
			{
				// add at i.
				nv1index = i;
			}
			if(hVertexList[iNextIndex] == pEdge1->hvStart || 
				hVertexList[iNextIndex] == pEdge1->hvEnd)
			{
				// add at iNextIndex
				nv1index = iNextIndex;
			}

			if(nv1index != -1)
			{
				hVertexList.InsertBefore(nv1index, pNewVertex1->id);
				++nVertices;
				break;
			}
		}

		if(nv2index == -1 && (hVertexList[i] == pEdge2->hvEnd || 
			hVertexList[i] == pEdge2->hvStart))
		{
			// find pEdge1->hvStart
			if(hVertexList[iPrevIndex] == pEdge2->hvStart || 
				hVertexList[iPrevIndex] == pEdge2->hvEnd)
			{
				// add at i.
				nv2index = i;
			}
			if(hVertexList[iNextIndex] == pEdge2->hvStart || 
				hVertexList[iNextIndex] == pEdge2->hvEnd)
			{
				// add at iNextIndex
				nv2index = iNextIndex;
			}

			if(nv2index != -1)
			{
				hVertexList.InsertBefore(nv2index, pNewVertex2->id);
				++nVertices;
				break;
			}
		}
	}

	SSHANDLE hTmp[64];
	memcpy(hTmp, hVertexList.Base(), sizeof(SSHANDLE) * nVertices);

	if(nv1index == -1 && nv2index == -1)
		return NULL;	// not used here.

	if(nv1index == -1 || nv2index == -1)
	{
		if(++iPass != 2)
			goto DoAgain;
	}

	SSHANDLE *rvl = new SSHANDLE[nVertices];
	memcpy(rvl, hVertexList.Base(), sizeof(SSHANDLE) * nVertices);

	return rvl;
}

// merge same vertices ->

BOOL CSSolid::CanMergeVertices()
{
	for(int v1 = 0; v1 < m_nVertices; v1++)
	{
		for(int v2 = 0; v2 < m_nVertices; v2++)
		{
			if(v1 == v2)
				continue;	// no!
			if(VectorCompare(m_Vertices[v1].pos, m_Vertices[v2].pos))
				return TRUE;	// got a match
		}
	}

	return FALSE;
}

SSHANDLE * CSSolid::MergeSameVertices(int& nDeleted)
{
	int nMerged = 0;
	nDeleted = 0;
	static SSHANDLE hDeletedList[128];

DoVertices:
	for(int v1 = 0; v1 < m_nVertices; v1++)
	{
		for(int v2 = 0; v2 < m_nVertices; v2++)
		{
			if(v1 == v2)
				continue;	// no!
			if(!VectorCompare(m_Vertices[v1].pos, m_Vertices[v2].pos))
			{	// no match
				continue;
			}

			++nMerged;

			// same vertices - kill v1, set edge refs to use v2.
			SSHANDLE hV1 = m_Vertices[v1].id;
			SSHANDLE hV2 = m_Vertices[v2].id;
			
			hDeletedList[nDeleted++] = hV1;
			
			DeleteVertex(v1);

			int nAffected;
			CSSEdge **ppEdges = FindAffectedEdges(&hV1, 1, nAffected);

			// run through edges and change references
			for(int e = 0; e < nAffected; e++)
			{
				if(ppEdges[e]->hvStart == hV1)
					ppEdges[e]->hvStart = hV2;
				if(ppEdges[e]->hvEnd == hV1)
					ppEdges[e]->hvEnd = hV2;
				CalcEdgeCenter(ppEdges[e]);
			}
			
			goto DoVertices;
		}
	}

	if(!nMerged)
		return NULL;

	int e;

	// kill edges that have same vertices
	for(e = 0; e < m_nEdges; e++)
	{
		CSSEdge &edge = m_Edges[e];

		if(edge.hvStart != edge.hvEnd)
			continue;	// edge is OK

		hDeletedList[nDeleted++] = edge.id;

		DeleteEdge(e);
		--e;
	}

	// kill similar edges (replace in faces too)
DoEdges:
	for(e = 0; e < m_nEdges; e++)
	{
		CSSEdge &edge = m_Edges[e];

		for(int e2 = 0; e2 < m_nEdges; e2++)
		{
			if(e == e2)
				continue;

			CSSEdge &edge2 = m_Edges[e2];

			if(!((edge2.hvStart == edge.hvStart && edge2.hvEnd == edge.hvEnd) || 
				(edge2.hvEnd == edge.hvStart && edge2.hvStart == edge.hvEnd)))
				continue;

			// we're going to delete edge2.
			SSHANDLE id2 = edge2.id;
			SSHANDLE id1 = edge.id;

			for(int f = 0; f < m_nFaces; f++)
			{
				CSSFace& face = m_Faces[f];
				for(int ef = 0; ef < face.nEdges; ef++)
				{
					if(face.Edges[ef] == id2)
					{
						face.Edges[ef] = id1;
						break;
					}
				}
			}

			hDeletedList[nDeleted++] = id2;
			DeleteEdge(e2);

			goto DoEdges;
		}
	}

	// delete concurrent edge references in face
	for(int f = 0; f < m_nFaces; f++)
	{
		CSSFace& face = m_Faces[f];

DoConcurrentEdges:
		for(int ef1 = 0; ef1 < face.nEdges; ef1++)
		{
			for(int ef2 = 0; ef2 < face.nEdges; ef2++)
			{
				if(ef2 == ef1)
					continue;

				if(face.Edges[ef1] != face.Edges[ef2])
					continue;
			
				// delete this ref
				memcpy(&face.Edges[ef2], &face.Edges[ef2+1], (face.nEdges-ef2) * 
					sizeof(face.Edges[0]));
				--face.nEdges;

				goto DoConcurrentEdges;
			}
		}

		if(face.nEdges < 3)
		{
			// kill this face
			hDeletedList[nDeleted++] = face.id;
			DeleteFace(f);
			--f;
		}
	}

	return hDeletedList;
}


//-----------------------------------------------------------------------------
// Purpose: Constructor.
//-----------------------------------------------------------------------------
CSSFace::CSSFace(void)
{
	Init();
}

//-----------------------------------------------------------------------------
// Purpose: Initialize the SSFace.
//-----------------------------------------------------------------------------
void CSSFace::Init(void)
{
	nEdges = 0;
	bModified = FALSE;
	
	m_nFaceID = 0;
	m_hDisp = EDITDISPHANDLE_INVALID;
	
	memset(&texture, 0, sizeof(TEXTURE));

	texture.scale[0] = g_pGameConfig->GetDefaultTextureScale();
	texture.scale[1] = g_pGameConfig->GetDefaultTextureScale();
}

//-----------------------------------------------------------------------------
// Purpose: Destructor.
//-----------------------------------------------------------------------------
CSSFace::~CSSFace(void)
{
	if ( m_hDisp != EDITDISPHANDLE_INVALID )
	{
		EditDispMgr()->Destroy( m_hDisp );
		m_hDisp = EDITDISPHANDLE_INVALID;
	}

	memset(this, 0, sizeof(this));
}


//-----------------------------------------------------------------------------
// Purpose: Constructor.
//-----------------------------------------------------------------------------
CSSEdge::CSSEdge(void)
{
	Faces[0] = Faces[1] = 0;
}


//-----------------------------------------------------------------------------
// Purpose: Destructor.
//-----------------------------------------------------------------------------
CSSEdge::~CSSEdge()
{
	memset(this, 0, sizeof(this));
}


//-----------------------------------------------------------------------------
// Purpose: Gets the world coordinates of the center point of this edge.
// Input  : Point - Receives the world coordinates of the center point.
//-----------------------------------------------------------------------------
void CSSEdge::GetCenterPoint(Vector& Point)
{
	Point = ptCenter;
}


CSSVertex::CSSVertex(void)
{
}


CSSVertex::~CSSVertex(void)
{
	pos[0] = pos[1] = pos[2] = 0;
	id = 0;
}


//-----------------------------------------------------------------------------
// Purpose: Gets the world coordinates of this vertex.
// Input  : Position - Receives the world coordinates.
//-----------------------------------------------------------------------------
void CSSVertex::GetPosition(Vector& Position)
{
	Position = pos;
}


// 
// save to .DXF
//
void CSSolid::SerializeDXF(FILE *stream, int nObject)
{
	char szName[128];
	sprintf(szName, "OBJECT%03d", nObject);

	// count number of triangulated faces
	int nTriFaces = 0;
	for(int i = 0; i < m_nFaces; i++)
	{
		CSSFace &face = m_Faces[i];
		nTriFaces += face.nEdges-2;
	}

	fprintf(stream,"0\nPOLYLINE\n8\n%s\n66\n1\n70\n64\n71\n%u\n72\n%u\n", szName, m_nVertices, nTriFaces);
	fprintf(stream,"62\n50\n");

	for (int i = 0; i < m_nVertices; i++)
	{
		Vector &pos = m_Vertices[i].pos;
		fprintf(stream,	"0\nVERTEX\n8\n%s\n10\n%.6f\n20\n%.6f\n30\n%.6f\n70\n192\n", szName, pos[0], pos[1], pos[2]);
	}

	// triangulate each face and write
	for(int i = 0; i < m_nFaces; i++)
	{
		CSSFace &face = m_Faces[i];
		PINT pVerts = CreatePointIndexList(face);

		for(int v = 0; v < face.nEdges; v++)
			pVerts[v]++;

		for(int v = 0; v < face.nEdges-2; v++)
		{
			fprintf(stream, "0\nVERTEX\n8\n%s\n10\n0\n20\n0\n30\n"
				"0\n70\n128\n71\n%d\n72\n%d\n73\n%d\n", szName, 
				v == 0 ? pVerts[0] : -pVerts[0],
				pVerts[v+1],
				v == (face.nEdges-3) ? pVerts[v+2] : -pVerts[v+2]
				);
		}
	}

	fprintf(stream, "0\nSEQEND\n8\n%s\n", szName);
}