You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1711 lines
38 KiB
1711 lines
38 KiB
//========= 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); |
|
}
|
|
|