diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /hammer/ssolid.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'hammer/ssolid.cpp')
| -rw-r--r-- | hammer/ssolid.cpp | 1711 |
1 files changed, 1711 insertions, 0 deletions
diff --git a/hammer/ssolid.cpp b/hammer/ssolid.cpp new file mode 100644 index 0000000..1054cb9 --- /dev/null +++ b/hammer/ssolid.cpp @@ -0,0 +1,1711 @@ +//========= 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); +} |