source-engine/hammer/ssolid.cpp

1712 lines
38 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= 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);
}