//========= Copyright © 1996-2005, 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 BOOL CheckFace(Vector *Points, int nPoints, Vector* pNormal, float dist, CCheckFaceInfo *pInfo) { int i, 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(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 = %08X, edgeNext = %08X", 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; SSHANDLE *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 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); }