3896 lines
159 KiB
C++
3896 lines
159 KiB
C++
|
//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose:
|
||
|
//
|
||
|
// $NoKeywords: $
|
||
|
//
|
||
|
// Notes: For some semblance of clarity. All spatial orientation notation assumes you're
|
||
|
// looking at the polyhedron from an outside point located spherically above
|
||
|
// the primitive in question
|
||
|
//
|
||
|
//======================================================================================//
|
||
|
|
||
|
#include "mathlib/polyhedron.h"
|
||
|
#include "mathlib/vmatrix.h"
|
||
|
#include <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
#include "tier1/utlvector.h"
|
||
|
#include "mathlib/ssemath.h"
|
||
|
|
||
|
|
||
|
|
||
|
struct GeneratePolyhedronFromPlanes_Point;
|
||
|
struct GeneratePolyhedronFromPlanes_PointLL;
|
||
|
struct GeneratePolyhedronFromPlanes_Line;
|
||
|
struct GeneratePolyhedronFromPlanes_LineLL;
|
||
|
struct GeneratePolyhedronFromPlanes_Polygon;
|
||
|
struct GeneratePolyhedronFromPlanes_PolygonLL;
|
||
|
|
||
|
struct GeneratePolyhedronFromPlanes_UnorderedPointLL;
|
||
|
struct GeneratePolyhedronFromPlanes_UnorderedLineLL;
|
||
|
struct GeneratePolyhedronFromPlanes_UnorderedPolygonLL;
|
||
|
|
||
|
Vector FindPointInPlanes( const float *pPlanes, int planeCount );
|
||
|
bool FindConvexShapeLooseAABB( const float *pInwardFacingPlanes, int iPlaneCount, Vector *pAABBMins, Vector *pAABBMaxs );
|
||
|
CPolyhedron *ClipLinkedGeometry( GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pPolygons, GeneratePolyhedronFromPlanes_UnorderedLineLL *pLines, GeneratePolyhedronFromPlanes_UnorderedPointLL *pPoints, int iPointCount, const fltx4 *pOutwardFacingPlanes, int iPlaneCount, float fOnPlaneEpsilon, bool bUseTemporaryMemory, fltx4 vShiftResultPositions );
|
||
|
CPolyhedron *ConvertLinkedGeometryToPolyhedron( GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pPolygons, GeneratePolyhedronFromPlanes_UnorderedLineLL *pLines, GeneratePolyhedronFromPlanes_UnorderedPointLL *pPoints, bool bUseTemporaryMemory, fltx4 vShiftResultPositions );
|
||
|
|
||
|
//#define ENABLE_DEBUG_POLYHEDRON_DUMPS //Dumps debug information to disk for use with glview. Requires that tier2 also be in all projects using debug mathlib
|
||
|
//#define DEBUG_DUMP_POLYHEDRONS_TO_NUMBERED_GLVIEWS //dumps successfully generated polyhedrons
|
||
|
#define USE_WORLD_CENTERED_POSITIONS //shift all our incoming math towards world origin before performing operations on it, shift it back in the result.
|
||
|
|
||
|
#ifdef DBGFLAG_ASSERT
|
||
|
void DumpPolyhedronToGLView( const CPolyhedron *pPolyhedron, const char *pFilename, const VMatrix *pTransform, const char *szfileOpenOptions = "ab" );
|
||
|
void DumpPlaneToGlView( const float *pPlane, float fGrayScale, const char *pszFileName, const VMatrix *pTransform );
|
||
|
void DumpLineToGLView( const Vector &vPoint1, const Vector &vColor1, const Vector &vPoint2, const Vector &vColor2, float fThickness, FILE *pFile );
|
||
|
void DumpAABBToGLView( const Vector &vCenter, const Vector &vExtents, const Vector &vColor, FILE *pFile );
|
||
|
|
||
|
#if defined( ENABLE_DEBUG_POLYHEDRON_DUMPS ) && defined( WIN32 )
|
||
|
#include "winlite.h"
|
||
|
#endif
|
||
|
|
||
|
static VMatrix s_matIdentity( 1.0f, 0.0f, 0.0f, 0.0f,
|
||
|
0.0f, 1.0f, 0.0f, 0.0f,
|
||
|
0.0f, 0.0f, 1.0f, 0.0f,
|
||
|
0.0f, 0.0f, 0.0f, 1.0f );
|
||
|
|
||
|
#ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS
|
||
|
void DumpWorkingStatePolyhedron( GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pAllPolygons, GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pDeadPolygons,
|
||
|
GeneratePolyhedronFromPlanes_UnorderedLineLL *pAllLines, GeneratePolyhedronFromPlanes_UnorderedLineLL *pDeadLines,
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPointLL *pAllPoints, GeneratePolyhedronFromPlanes_UnorderedPointLL *pDeadPoints,
|
||
|
const char *pFilename, const VMatrix *pTransform );
|
||
|
#endif
|
||
|
|
||
|
#if defined( DEBUG_DUMP_POLYHEDRONS_TO_NUMBERED_GLVIEWS )
|
||
|
static int g_iPolyhedronDumpCounter = 0;
|
||
|
#endif
|
||
|
|
||
|
#define DBG_ONLY(x) x
|
||
|
#else
|
||
|
#define DBG_ONLY(x)
|
||
|
#endif
|
||
|
|
||
|
|
||
|
|
||
|
// memdbgon must be the last include file in a .cpp file!!!
|
||
|
#include "tier0/memdbgon.h"
|
||
|
|
||
|
#if defined( DBGFLAG_ASSERT ) && defined( ENABLE_DEBUG_POLYHEDRON_DUMPS )
|
||
|
|
||
|
void CreateDumpDirectory( const char *szDirectoryName )
|
||
|
{
|
||
|
#if defined( WIN32 )
|
||
|
CreateDirectory( szDirectoryName, NULL );
|
||
|
#else
|
||
|
Assert( false ); //TODO: create directories in linux
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
#define DEBUG_POLYHEDRON_CONVERSION 1
|
||
|
typedef bool (*PFN_PolyhedronCarvingDebugStepCallback)( CPolyhedron *pPolyhedron ); //function that receives a polyhedron conversion after each cut. For the slowest, surest debugging possible. Returns true if the polyhedron passes mustard, false to dump the current work state
|
||
|
PFN_PolyhedronCarvingDebugStepCallback g_pPolyhedronCarvingDebugStepCallback = NULL;
|
||
|
|
||
|
#endif
|
||
|
|
||
|
|
||
|
|
||
|
void CPolyhedron_AllocByNew::Release( void )
|
||
|
{
|
||
|
delete this;
|
||
|
}
|
||
|
|
||
|
CPolyhedron_AllocByNew *CPolyhedron_AllocByNew::Allocate( unsigned short iVertices, unsigned short iLines, unsigned short iIndices, unsigned short iPolygons ) //creates the polyhedron along with enough memory to hold all it's data in a single allocation
|
||
|
{
|
||
|
void *pMemory = new unsigned char [ sizeof( CPolyhedron_AllocByNew ) +
|
||
|
(iVertices * sizeof(Vector)) +
|
||
|
(iLines * sizeof(Polyhedron_IndexedLine_t)) +
|
||
|
(iIndices * sizeof( Polyhedron_IndexedLineReference_t )) +
|
||
|
(iPolygons * sizeof( Polyhedron_IndexedPolygon_t ))];
|
||
|
|
||
|
#include "tier0/memdbgoff.h" //the following placement new doesn't compile with memory debugging
|
||
|
CPolyhedron_AllocByNew *pAllocated = new ( pMemory ) CPolyhedron_AllocByNew;
|
||
|
#include "tier0/memdbgon.h"
|
||
|
|
||
|
pAllocated->iVertexCount = iVertices;
|
||
|
pAllocated->iLineCount = iLines;
|
||
|
pAllocated->iIndexCount = iIndices;
|
||
|
pAllocated->iPolygonCount = iPolygons;
|
||
|
pAllocated->pVertices = (Vector *)(pAllocated + 1); //start vertex memory at the end of the class
|
||
|
pAllocated->pLines = (Polyhedron_IndexedLine_t *)(pAllocated->pVertices + iVertices);
|
||
|
pAllocated->pIndices = (Polyhedron_IndexedLineReference_t *)(pAllocated->pLines + iLines);
|
||
|
pAllocated->pPolygons = (Polyhedron_IndexedPolygon_t *)(pAllocated->pIndices + iIndices);
|
||
|
|
||
|
return pAllocated;
|
||
|
}
|
||
|
|
||
|
|
||
|
class CPolyhedron_TempMemory : public CPolyhedron
|
||
|
{
|
||
|
public:
|
||
|
#ifdef DBGFLAG_ASSERT
|
||
|
int iReferenceCount;
|
||
|
#endif
|
||
|
|
||
|
virtual void Release( void )
|
||
|
{
|
||
|
#ifdef DBGFLAG_ASSERT
|
||
|
--iReferenceCount;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
CPolyhedron_TempMemory( void )
|
||
|
#ifdef DBGFLAG_ASSERT
|
||
|
: iReferenceCount( 0 )
|
||
|
#endif
|
||
|
{ };
|
||
|
};
|
||
|
|
||
|
|
||
|
static CUtlVector<unsigned char> s_TempMemoryPolyhedron_Buffer;
|
||
|
static CPolyhedron_TempMemory s_TempMemoryPolyhedron;
|
||
|
|
||
|
CPolyhedron *GetTempPolyhedron( unsigned short iVertices, unsigned short iLines, unsigned short iIndices, unsigned short iPolygons ) //grab the temporary polyhedron. Avoids new/delete for quick work. Can only be in use by one chunk of code at a time
|
||
|
{
|
||
|
AssertMsg( s_TempMemoryPolyhedron.iReferenceCount == 0, "Temporary polyhedron memory being rewritten before released" );
|
||
|
#ifdef DBGFLAG_ASSERT
|
||
|
++s_TempMemoryPolyhedron.iReferenceCount;
|
||
|
#endif
|
||
|
s_TempMemoryPolyhedron_Buffer.SetCount( (sizeof( Vector ) * iVertices) +
|
||
|
(sizeof( Polyhedron_IndexedLine_t ) * iLines) +
|
||
|
(sizeof( Polyhedron_IndexedLineReference_t ) * iIndices) +
|
||
|
(sizeof( Polyhedron_IndexedPolygon_t ) * iPolygons) );
|
||
|
|
||
|
s_TempMemoryPolyhedron.iVertexCount = iVertices;
|
||
|
s_TempMemoryPolyhedron.iLineCount = iLines;
|
||
|
s_TempMemoryPolyhedron.iIndexCount = iIndices;
|
||
|
s_TempMemoryPolyhedron.iPolygonCount = iPolygons;
|
||
|
|
||
|
s_TempMemoryPolyhedron.pVertices = (Vector *)s_TempMemoryPolyhedron_Buffer.Base();
|
||
|
s_TempMemoryPolyhedron.pLines = (Polyhedron_IndexedLine_t *)(&s_TempMemoryPolyhedron.pVertices[s_TempMemoryPolyhedron.iVertexCount]);
|
||
|
s_TempMemoryPolyhedron.pIndices = (Polyhedron_IndexedLineReference_t *)(&s_TempMemoryPolyhedron.pLines[s_TempMemoryPolyhedron.iLineCount]);
|
||
|
s_TempMemoryPolyhedron.pPolygons = (Polyhedron_IndexedPolygon_t *)(&s_TempMemoryPolyhedron.pIndices[s_TempMemoryPolyhedron.iIndexCount]);
|
||
|
|
||
|
return &s_TempMemoryPolyhedron;
|
||
|
}
|
||
|
|
||
|
|
||
|
Vector CPolyhedron::Center( void ) const
|
||
|
{
|
||
|
if( iVertexCount == 0 )
|
||
|
return vec3_origin;
|
||
|
|
||
|
Vector vAABBMin, vAABBMax;
|
||
|
vAABBMin = vAABBMax = pVertices[0];
|
||
|
for( int i = 1; i != iVertexCount; ++i )
|
||
|
{
|
||
|
Vector &vPoint = pVertices[i];
|
||
|
if( vPoint.x < vAABBMin.x )
|
||
|
vAABBMin.x = vPoint.x;
|
||
|
if( vPoint.y < vAABBMin.y )
|
||
|
vAABBMin.y = vPoint.y;
|
||
|
if( vPoint.z < vAABBMin.z )
|
||
|
vAABBMin.z = vPoint.z;
|
||
|
|
||
|
if( vPoint.x > vAABBMax.x )
|
||
|
vAABBMax.x = vPoint.x;
|
||
|
if( vPoint.y > vAABBMax.y )
|
||
|
vAABBMax.y = vPoint.y;
|
||
|
if( vPoint.z > vAABBMax.z )
|
||
|
vAABBMax.z = vPoint.z;
|
||
|
}
|
||
|
return ((vAABBMin + vAABBMax) * 0.5f);
|
||
|
}
|
||
|
|
||
|
enum PolyhedronPointPlanarity
|
||
|
{
|
||
|
POINT_DEAD,
|
||
|
POINT_ONPLANE,
|
||
|
POINT_ALIVE
|
||
|
};
|
||
|
|
||
|
struct GeneratePolyhedronFromPlanes_Point //must be aligned to 16 byte boundary
|
||
|
{
|
||
|
fltx4 ptPosition; //w = -1 to make plane distance an all addition operation after the multiply
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pConnectedLines; //keep these in a clockwise order, circular linking
|
||
|
float fPlaneDist; //used in plane cutting
|
||
|
PolyhedronPointPlanarity planarity;
|
||
|
union //temporary work variables
|
||
|
{
|
||
|
int iSaveIndices;
|
||
|
};
|
||
|
|
||
|
#ifdef DBGFLAG_ASSERT
|
||
|
struct ClipDebugData_t
|
||
|
{
|
||
|
void Reset( void ) { memset( this, 0, sizeof( *this ) ); }
|
||
|
bool bIsNew;
|
||
|
Vector vWorkingStateColorOverride;
|
||
|
bool bVisited;
|
||
|
float fInitialPlaneDistance;
|
||
|
} debugdata;
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
enum PolyhedronLinePlanarity
|
||
|
{
|
||
|
LINE_ONPLANE = 0,
|
||
|
LINE_ALIVE = (1 << 0),
|
||
|
LINE_DEAD = (1 << 1),
|
||
|
LINE_CUT = LINE_ALIVE | LINE_DEAD,
|
||
|
};
|
||
|
|
||
|
PolyhedronLinePlanarity &operator|=( PolyhedronLinePlanarity &a, const PolyhedronLinePlanarity &b )
|
||
|
{
|
||
|
a = (PolyhedronLinePlanarity)((int)a | int(b));
|
||
|
return a;
|
||
|
}
|
||
|
|
||
|
struct GeneratePolyhedronFromPlanes_LineLL
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_Line *pLine;
|
||
|
//whatever is referencing the line should know which side of the line it's on (points and polygons).
|
||
|
//for polygons, it's the index back to the polygon's self. It's also which point to follow to continue going clockwise, which makes polygon 0 the one on the left side of an upward facing line vector
|
||
|
//for points, it's the OTHER point's index
|
||
|
int iReferenceIndex;
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pPrev;
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pNext;
|
||
|
};
|
||
|
|
||
|
struct GeneratePolyhedronFromPlanes_Line
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_Point *pPoints[2]; //the 2 connecting points in no particular order
|
||
|
GeneratePolyhedronFromPlanes_Polygon *pPolygons[2]; //viewing from the outside with the point connections going up, 0 is the left polygon, 1 is the right
|
||
|
int iSaveIndices;
|
||
|
PolyhedronLinePlanarity planarity;
|
||
|
bool bNewLengthThisPass;
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_LineLL PointLineLinks[2]; //rather than going into a point and searching for its link to this line, lets just cache it to eliminate searching
|
||
|
GeneratePolyhedronFromPlanes_LineLL PolygonLineLinks[2]; //rather than going into a polygon and searching for its link to this line, lets just cache it to eliminate searching
|
||
|
#ifdef POLYHEDRON_EXTENSIVE_DEBUGGING
|
||
|
int iDebugFlags;
|
||
|
#endif
|
||
|
|
||
|
void InitLineLinks( void )
|
||
|
{
|
||
|
PointLineLinks[0].iReferenceIndex = 1;
|
||
|
PointLineLinks[1].iReferenceIndex = 0;
|
||
|
PolygonLineLinks[0].iReferenceIndex = 0;
|
||
|
PolygonLineLinks[1].iReferenceIndex = 1;
|
||
|
|
||
|
PointLineLinks[0].pLine = PointLineLinks[1].pLine = PolygonLineLinks[0].pLine = PolygonLineLinks[1].pLine = this;
|
||
|
}
|
||
|
|
||
|
#ifdef DBGFLAG_ASSERT
|
||
|
struct ClipDebugData_t
|
||
|
{
|
||
|
void Reset( void ) { memset( this, 0, sizeof( *this ) ); }
|
||
|
bool bIsNew; //was generated during this cut
|
||
|
Vector vWorkingStateColorOverride;
|
||
|
bool bTested;
|
||
|
} debugdata;
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
struct GeneratePolyhedronFromPlanes_Polygon
|
||
|
{
|
||
|
Vector vSurfaceNormal;
|
||
|
//float fNormalDist;
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pLines; //keep these in a clockwise order, circular linking
|
||
|
|
||
|
bool bDead;
|
||
|
bool bHasNewPoints;
|
||
|
bool bMovedExistingPoints;
|
||
|
|
||
|
#ifdef DBGFLAG_ASSERT
|
||
|
struct ClipDebugData_t
|
||
|
{
|
||
|
void Reset( void ) { memset( this, 0, sizeof( *this ) ); }
|
||
|
bool bIsNew; //only one should be new per clip, unless we triangulate
|
||
|
Vector vWorkingStateColorOverride;
|
||
|
} debugdata;
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
struct GeneratePolyhedronFromPlanes_UnorderedPolygonLL //an unordered collection of polygons
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_Polygon polygon;
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pNext;
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pPrev;
|
||
|
};
|
||
|
|
||
|
struct GeneratePolyhedronFromPlanes_UnorderedLineLL //an unordered collection of lines
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_Line line;
|
||
|
GeneratePolyhedronFromPlanes_UnorderedLineLL *pNext;
|
||
|
GeneratePolyhedronFromPlanes_UnorderedLineLL *pPrev;
|
||
|
};
|
||
|
|
||
|
#pragma pack( push )
|
||
|
#pragma pack( 16 ) //help align the position to 16 byte boundaries when in arrays
|
||
|
struct GeneratePolyhedronFromPlanes_UnorderedPointLL //an unordered collection of points
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_Point point;
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPointLL *pNext;
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPointLL *pPrev;
|
||
|
};
|
||
|
#pragma pack(pop)
|
||
|
|
||
|
#ifdef DBGFLAG_ASSERT
|
||
|
void Debug_ResetWorkingStateColorOverrides( GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pAllPolygons, GeneratePolyhedronFromPlanes_UnorderedPointLL *pAllPoints, GeneratePolyhedronFromPlanes_UnorderedLineLL *pAllLines )
|
||
|
{
|
||
|
while( pAllPolygons )
|
||
|
{
|
||
|
pAllPolygons->polygon.debugdata.vWorkingStateColorOverride.Init();
|
||
|
pAllPolygons = pAllPolygons->pNext;
|
||
|
}
|
||
|
|
||
|
while( pAllPoints )
|
||
|
{
|
||
|
pAllPoints->point.debugdata.vWorkingStateColorOverride.Init();
|
||
|
pAllPoints = pAllPoints->pNext;
|
||
|
}
|
||
|
|
||
|
while( pAllLines )
|
||
|
{
|
||
|
pAllLines->line.debugdata.vWorkingStateColorOverride.Init();
|
||
|
pAllLines = pAllLines->pNext;
|
||
|
}
|
||
|
}
|
||
|
#define DBG_RESETWORKINGSTATECOLORS() Debug_ResetWorkingStateColorOverrides( pAllPolygons, pAllPoints, pAllLines );
|
||
|
#else
|
||
|
#define DBG_RESETWORKINGSTATECOLORS()
|
||
|
#endif
|
||
|
|
||
|
|
||
|
|
||
|
CPolyhedron *ClipPolyhedron( const CPolyhedron *pExistingPolyhedron, const float *pOutwardFacingPlanes, int iPlaneCount, float fOnPlaneEpsilon, bool bUseTemporaryMemory )
|
||
|
{
|
||
|
if( pExistingPolyhedron == NULL )
|
||
|
return NULL;
|
||
|
|
||
|
AssertMsg( (pExistingPolyhedron->iVertexCount >= 3) && (pExistingPolyhedron->iPolygonCount >= 2), "Polyhedron doesn't meet absolute minimum spec" );
|
||
|
|
||
|
const size_t kFltX4Align = sizeof( fltx4 ) - 1;
|
||
|
uint8 *pAlignedAlloc = (uint8 *)stackalloc( (sizeof( fltx4 ) * iPlaneCount) + kFltX4Align );
|
||
|
pAlignedAlloc = (uint8 *)(((size_t)(pAlignedAlloc + kFltX4Align)) & ~kFltX4Align);
|
||
|
fltx4 *pUsefulPlanes = (fltx4 *)pAlignedAlloc;
|
||
|
int iUsefulPlaneCount = 0;
|
||
|
Vector *pExistingVertices = pExistingPolyhedron->pVertices;
|
||
|
|
||
|
//A large part of clipping will either eliminate the polyhedron entirely, or clip nothing at all, so lets just check for those first and throw away useless planes
|
||
|
{
|
||
|
int iLiveCount = 0;
|
||
|
int iDeadCount = 0;
|
||
|
const float fNegativeOnPlaneEpsilon = -fOnPlaneEpsilon;
|
||
|
|
||
|
for( int i = 0; i != iPlaneCount; ++i )
|
||
|
{
|
||
|
Vector vNormal = *((Vector *)&pOutwardFacingPlanes[(i * 4) + 0]);
|
||
|
float fPlaneDist = pOutwardFacingPlanes[(i * 4) + 3];
|
||
|
|
||
|
for( int j = 0; j != pExistingPolyhedron->iVertexCount; ++j )
|
||
|
{
|
||
|
float fPointDist = vNormal.Dot( pExistingVertices[j] ) - fPlaneDist;
|
||
|
|
||
|
if( fPointDist <= fNegativeOnPlaneEpsilon )
|
||
|
++iLiveCount;
|
||
|
else if( fPointDist > fOnPlaneEpsilon )
|
||
|
++iDeadCount;
|
||
|
}
|
||
|
|
||
|
if( iLiveCount == 0 )
|
||
|
{
|
||
|
//all points are dead or on the plane, so the polyhedron is dead
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if( iDeadCount != 0 )
|
||
|
{
|
||
|
//at least one point died, this plane yields useful results
|
||
|
SubFloat( pUsefulPlanes[iUsefulPlaneCount], 0 ) = vNormal.x; //PolyhedronFloatStandardize( vNormal.x );
|
||
|
SubFloat( pUsefulPlanes[iUsefulPlaneCount], 1 ) = vNormal.y; //PolyhedronFloatStandardize( vNormal.y );
|
||
|
SubFloat( pUsefulPlanes[iUsefulPlaneCount], 2 ) = vNormal.z; //PolyhedronFloatStandardize( vNormal.z );
|
||
|
SubFloat( pUsefulPlanes[iUsefulPlaneCount], 3 ) = fPlaneDist; //PolyhedronFloatStandardize( fPlaneDist );
|
||
|
++iUsefulPlaneCount;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( iUsefulPlaneCount == 0 )
|
||
|
{
|
||
|
//testing shows that the polyhedron won't even be cut, clone the existing polyhedron and return that
|
||
|
|
||
|
CPolyhedron *pReturn;
|
||
|
if( bUseTemporaryMemory )
|
||
|
{
|
||
|
pReturn = GetTempPolyhedron( pExistingPolyhedron->iVertexCount,
|
||
|
pExistingPolyhedron->iLineCount,
|
||
|
pExistingPolyhedron->iIndexCount,
|
||
|
pExistingPolyhedron->iPolygonCount );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pReturn = CPolyhedron_AllocByNew::Allocate( pExistingPolyhedron->iVertexCount,
|
||
|
pExistingPolyhedron->iLineCount,
|
||
|
pExistingPolyhedron->iIndexCount,
|
||
|
pExistingPolyhedron->iPolygonCount );
|
||
|
}
|
||
|
|
||
|
memcpy( pReturn->pVertices, pExistingPolyhedron->pVertices, sizeof( Vector ) * pReturn->iVertexCount );
|
||
|
memcpy( pReturn->pLines, pExistingPolyhedron->pLines, sizeof( Polyhedron_IndexedLine_t ) * pReturn->iLineCount );
|
||
|
memcpy( pReturn->pIndices, pExistingPolyhedron->pIndices, sizeof( Polyhedron_IndexedLineReference_t ) * pReturn->iIndexCount );
|
||
|
memcpy( pReturn->pPolygons, pExistingPolyhedron->pPolygons, sizeof( Polyhedron_IndexedPolygon_t ) * pReturn->iPolygonCount );
|
||
|
|
||
|
#if defined( DEBUG_POLYHEDRON_CONVERSION )
|
||
|
//last bit of debugging from whatever outside source wants this stupid thing
|
||
|
if( g_pPolyhedronCarvingDebugStepCallback != NULL )
|
||
|
{
|
||
|
AssertMsg( g_pPolyhedronCarvingDebugStepCallback( pReturn ), "Outside conversion failed" );
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
return pReturn;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//convert the polyhedron to linked geometry
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPointLL *pPoints = (GeneratePolyhedronFromPlanes_UnorderedPointLL *)stackalloc( pExistingPolyhedron->iVertexCount * sizeof( GeneratePolyhedronFromPlanes_UnorderedPointLL ) );
|
||
|
GeneratePolyhedronFromPlanes_UnorderedLineLL *pLines = (GeneratePolyhedronFromPlanes_UnorderedLineLL *)stackalloc( pExistingPolyhedron->iLineCount * sizeof( GeneratePolyhedronFromPlanes_UnorderedLineLL ) );
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pPolygons = (GeneratePolyhedronFromPlanes_UnorderedPolygonLL *)stackalloc( pExistingPolyhedron->iPolygonCount * sizeof( GeneratePolyhedronFromPlanes_UnorderedPolygonLL ) );
|
||
|
|
||
|
#if defined( USE_WORLD_CENTERED_POSITIONS )
|
||
|
fltx4 vPointOffset = LoadZeroSIMD();
|
||
|
#endif
|
||
|
|
||
|
//setup points
|
||
|
for( int i = 0; i != pExistingPolyhedron->iVertexCount; ++i )
|
||
|
{
|
||
|
SubFloat( pPoints[i].point.ptPosition, 0 ) = pExistingPolyhedron->pVertices[i].x;
|
||
|
SubFloat( pPoints[i].point.ptPosition, 1 ) = pExistingPolyhedron->pVertices[i].y;
|
||
|
SubFloat( pPoints[i].point.ptPosition, 2 ) = pExistingPolyhedron->pVertices[i].z;
|
||
|
SubFloat( pPoints[i].point.ptPosition, 3 ) = -1.0f;
|
||
|
#if defined( USE_WORLD_CENTERED_POSITIONS )
|
||
|
vPointOffset = AddSIMD( vPointOffset, pPoints[i].point.ptPosition );
|
||
|
#endif
|
||
|
pPoints[i].point.pConnectedLines = NULL; //we won't be circular linking until later
|
||
|
}
|
||
|
|
||
|
#if defined( USE_WORLD_CENTERED_POSITIONS )
|
||
|
//move everything towards origin for more precise math
|
||
|
vPointOffset = MulSIMD( vPointOffset, ReplicateX4( -1.0f / (float)pExistingPolyhedron->iVertexCount ) );
|
||
|
vPointOffset = FloorSIMD( vPointOffset );
|
||
|
SubFloat( vPointOffset, 3 ) = -1.0f;
|
||
|
|
||
|
for( int i = 0; i != pExistingPolyhedron->iVertexCount; ++i )
|
||
|
{
|
||
|
pPoints[i].point.ptPosition = AddSIMD( pPoints[i].point.ptPosition, vPointOffset );
|
||
|
SubFloat( pPoints[i].point.ptPosition, 3 ) = -1.0f;
|
||
|
}
|
||
|
for( int i = 0; i != iUsefulPlaneCount; ++i )
|
||
|
{
|
||
|
fltx4 vMul = MulSIMD( pUsefulPlanes[i], vPointOffset );
|
||
|
SubFloat( pUsefulPlanes[i], 3 ) = SubFloat(vMul, 0) + SubFloat(vMul, 1) + SubFloat(vMul, 2) - SubFloat(vMul, 3 );
|
||
|
}
|
||
|
fltx4 vResultOffset = NegSIMD( vPointOffset );
|
||
|
SubFloat( vResultOffset, 3 ) = 0.0f;
|
||
|
#else
|
||
|
fltx4 vResultOffset = LoadZeroSIMD();
|
||
|
#endif
|
||
|
|
||
|
//setup lines and interlink to points (line links are not yet circularly linked, and are unordered)
|
||
|
for( int i = 0; i != pExistingPolyhedron->iLineCount; ++i )
|
||
|
{
|
||
|
pLines[i].line.InitLineLinks();
|
||
|
for( int j = 0; j != 2; ++j )
|
||
|
{
|
||
|
pLines[i].line.pPoints[j] = &pPoints[pExistingPolyhedron->pLines[i].iPointIndices[j]].point;
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pLineLink = &pLines[i].line.PointLineLinks[j];
|
||
|
//pLineLink->pPrev = NULL;
|
||
|
pLineLink->pNext = pLines[i].line.pPoints[j]->pConnectedLines;
|
||
|
pLines[i].line.pPoints[j]->pConnectedLines = pLineLink;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//setup polygons
|
||
|
for( int i = 0; i != pExistingPolyhedron->iPolygonCount; ++i )
|
||
|
{
|
||
|
pPolygons[i].polygon.vSurfaceNormal = pExistingPolyhedron->pPolygons[i].polyNormal;
|
||
|
Polyhedron_IndexedLineReference_t *pOffsetPolyhedronLines = &pExistingPolyhedron->pIndices[pExistingPolyhedron->pPolygons[i].iFirstIndex];
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pFirstLink = &pLines[pOffsetPolyhedronLines[0].iLineIndex].line.PolygonLineLinks[pOffsetPolyhedronLines[0].iEndPointIndex];
|
||
|
pPolygons[i].polygon.pLines = pFirstLink; //technically going to link to itself on first pass, then get linked properly immediately afterward
|
||
|
for( int j = 0; j != pExistingPolyhedron->pPolygons[i].iIndexCount; ++j )
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pLineLink = &pLines[pOffsetPolyhedronLines[j].iLineIndex].line.PolygonLineLinks[pOffsetPolyhedronLines[j].iEndPointIndex];
|
||
|
pLineLink->pLine->pPolygons[pLineLink->iReferenceIndex] = &pPolygons[i].polygon;
|
||
|
|
||
|
pLineLink->pPrev = pPolygons[i].polygon.pLines;
|
||
|
pPolygons[i].polygon.pLines->pNext = pLineLink;
|
||
|
pPolygons[i].polygon.pLines = pLineLink;
|
||
|
}
|
||
|
|
||
|
pFirstLink->pPrev = pPolygons[i].polygon.pLines;
|
||
|
pPolygons[i].polygon.pLines->pNext = pFirstLink;
|
||
|
}
|
||
|
|
||
|
//go back to point line links so we can circularly link them as well as order them now that every point has all its line links
|
||
|
for( int i = 0; i != pExistingPolyhedron->iVertexCount; ++i )
|
||
|
{
|
||
|
//interlink the points
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pLastVisitedLink = pPoints[i].point.pConnectedLines;
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pCurrentLink = pLastVisitedLink;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
pCurrentLink->pPrev = pLastVisitedLink;
|
||
|
pLastVisitedLink = pCurrentLink;
|
||
|
pCurrentLink = pCurrentLink->pNext;
|
||
|
} while( pCurrentLink );
|
||
|
|
||
|
//circular link
|
||
|
pLastVisitedLink->pNext = pPoints[i].point.pConnectedLines;
|
||
|
pPoints[i].point.pConnectedLines->pPrev = pLastVisitedLink;
|
||
|
}
|
||
|
|
||
|
|
||
|
//fix ordering
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pFirstLink = pPoints[i].point.pConnectedLines;
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pWorkLink = pFirstLink;
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pSearchLink;
|
||
|
GeneratePolyhedronFromPlanes_Polygon *pLookingForPolygon;
|
||
|
Assert( pFirstLink->pNext != pFirstLink );
|
||
|
do
|
||
|
{
|
||
|
pLookingForPolygon = pWorkLink->pLine->pPolygons[1 - pWorkLink->iReferenceIndex]; //grab pointer to left polygon
|
||
|
pSearchLink = pWorkLink->pPrev;
|
||
|
|
||
|
while( pSearchLink->pLine->pPolygons[pSearchLink->iReferenceIndex] != pLookingForPolygon )
|
||
|
pSearchLink = pSearchLink->pPrev;
|
||
|
|
||
|
Assert( pSearchLink->pLine->pPolygons[pSearchLink->iReferenceIndex] == pWorkLink->pLine->pPolygons[1 - pWorkLink->iReferenceIndex] );
|
||
|
|
||
|
//pluck the search link from wherever it is
|
||
|
pSearchLink->pPrev->pNext = pSearchLink->pNext;
|
||
|
pSearchLink->pNext->pPrev = pSearchLink->pPrev;
|
||
|
|
||
|
//insert the search link just before the work link
|
||
|
pSearchLink->pPrev = pWorkLink->pPrev;
|
||
|
pSearchLink->pNext = pWorkLink;
|
||
|
|
||
|
pSearchLink->pPrev->pNext = pSearchLink;
|
||
|
pWorkLink->pPrev = pSearchLink;
|
||
|
|
||
|
pWorkLink = pSearchLink;
|
||
|
} while( pWorkLink != pFirstLink );
|
||
|
}
|
||
|
|
||
|
//setup point collection
|
||
|
{
|
||
|
pPoints[0].pPrev = NULL;
|
||
|
pPoints[0].pNext = &pPoints[1];
|
||
|
int iLastPoint = pExistingPolyhedron->iVertexCount - 1;
|
||
|
for( int i = 1; i != iLastPoint; ++i )
|
||
|
{
|
||
|
pPoints[i].pPrev = &pPoints[i - 1];
|
||
|
pPoints[i].pNext = &pPoints[i + 1];
|
||
|
}
|
||
|
pPoints[iLastPoint].pPrev = &pPoints[iLastPoint - 1];
|
||
|
pPoints[iLastPoint].pNext = NULL;
|
||
|
}
|
||
|
|
||
|
//setup line collection
|
||
|
{
|
||
|
pLines[0].pPrev = NULL;
|
||
|
pLines[0].pNext = &pLines[1];
|
||
|
int iLastLine = pExistingPolyhedron->iLineCount - 1;
|
||
|
for( int i = 1; i != iLastLine; ++i )
|
||
|
{
|
||
|
pLines[i].pPrev = &pLines[i - 1];
|
||
|
pLines[i].pNext = &pLines[i + 1];
|
||
|
}
|
||
|
pLines[iLastLine].pPrev = &pLines[iLastLine - 1];
|
||
|
pLines[iLastLine].pNext = NULL;
|
||
|
}
|
||
|
|
||
|
//setup polygon collection
|
||
|
{
|
||
|
pPolygons[0].pPrev = NULL;
|
||
|
pPolygons[0].pNext = &pPolygons[1];
|
||
|
int iLastPolygon = pExistingPolyhedron->iPolygonCount - 1;
|
||
|
for( int i = 1; i != iLastPolygon; ++i )
|
||
|
{
|
||
|
pPolygons[i].pPrev = &pPolygons[i - 1];
|
||
|
pPolygons[i].pNext = &pPolygons[i + 1];
|
||
|
}
|
||
|
pPolygons[iLastPolygon].pPrev = &pPolygons[iLastPolygon - 1];
|
||
|
pPolygons[iLastPolygon].pNext = NULL;
|
||
|
}
|
||
|
|
||
|
CPolyhedron *pRetVal = ClipLinkedGeometry( pPolygons, pLines, pPoints, pExistingPolyhedron->iVertexCount, pUsefulPlanes, iUsefulPlaneCount, fOnPlaneEpsilon, bUseTemporaryMemory, vResultOffset );
|
||
|
#if defined( USE_WORLD_CENTERED_POSITIONS ) && defined( DEBUG_POLYHEDRON_CONVERSION )
|
||
|
//last bit of debugging from whatever outside source wants this stupid thing
|
||
|
if( pRetVal && (g_pPolyhedronCarvingDebugStepCallback != NULL) )
|
||
|
{
|
||
|
VMatrix matScaleCentered;
|
||
|
matScaleCentered.Identity();
|
||
|
matScaleCentered[0][0] = matScaleCentered[1][1] = matScaleCentered[2][2] = 10.0f;
|
||
|
matScaleCentered.SetTranslation( -pRetVal->Center() * 10.0f );
|
||
|
|
||
|
DumpPolyhedronToGLView( pRetVal, "AssertPolyhedron.txt", &matScaleCentered );
|
||
|
AssertMsg( g_pPolyhedronCarvingDebugStepCallback( pRetVal ), "Outside conversion failed. Offset failure" ); //this REALLY sucks. Because the difference between success and failure was a translation of all points by the same vector. LAME
|
||
|
}
|
||
|
#endif //#if defined( USE_WORLD_CENTERED_POSITIONS )
|
||
|
|
||
|
return pRetVal;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
Vector FindPointInPlanes( const float *pPlanes, int planeCount )
|
||
|
{
|
||
|
Vector point = vec3_origin;
|
||
|
|
||
|
for ( int i = 0; i < planeCount; i++ )
|
||
|
{
|
||
|
float fD = DotProduct( *(Vector *)&pPlanes[i*4], point ) - pPlanes[i*4 + 3];
|
||
|
if ( fD < 0 )
|
||
|
{
|
||
|
point -= fD * (*(Vector *)&pPlanes[i*4]);
|
||
|
}
|
||
|
}
|
||
|
return point;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
bool FindConvexShapeLooseAABB( const fltx4 *pInwardFacingPlanes, int iPlaneCount, Vector *pAABBMins, Vector *pAABBMaxs ) //bounding box of the convex shape (subject to floating point error)
|
||
|
{
|
||
|
//returns false if the AABB hasn't been set
|
||
|
if( pAABBMins == NULL && pAABBMaxs == NULL ) //no use in actually finding out what it is
|
||
|
return false;
|
||
|
|
||
|
struct FindConvexShapeAABB_Polygon_t
|
||
|
{
|
||
|
float *verts;
|
||
|
int iVertCount;
|
||
|
};
|
||
|
|
||
|
const size_t kPlaneAlign = sizeof( Vector4D ) - 1;
|
||
|
uint8 *pAlignedAlloc = (uint8 *)stackalloc( (sizeof( Vector4D ) * iPlaneCount) + kPlaneAlign );
|
||
|
pAlignedAlloc = (uint8 *)(((size_t)(pAlignedAlloc + kPlaneAlign)) & ~kPlaneAlign);
|
||
|
Vector4D *pMovedPlanes = (Vector4D *)pAlignedAlloc;
|
||
|
for( int i = 0; i != iPlaneCount; ++i )
|
||
|
{
|
||
|
pMovedPlanes[i].Init( SubFloat( pInwardFacingPlanes[i], 0 ), SubFloat( pInwardFacingPlanes[i], 1 ), SubFloat( pInwardFacingPlanes[i], 2 ), SubFloat( pInwardFacingPlanes[i], 3 ) - 100.0f ); //move planes out a lot to kill some imprecision problems
|
||
|
}
|
||
|
|
||
|
//vAABBMins = vAABBMaxs = FindPointInPlanes( pPlanes, iPlaneCount );
|
||
|
float *vertsIn = NULL; //we'll be allocating a new buffer for this with each new polygon, and moving it off to the polygon array
|
||
|
float *vertsOut = (float *)stackalloc( (iPlaneCount + 4) * (sizeof( float ) * 3) ); //each plane will initially have 4 points in its polygon representation, and each plane clip has the possibility to add 1 point to the polygon
|
||
|
float *vertsSwap;
|
||
|
|
||
|
FindConvexShapeAABB_Polygon_t *pPolygons = (FindConvexShapeAABB_Polygon_t *)stackalloc( iPlaneCount * sizeof( FindConvexShapeAABB_Polygon_t ) );
|
||
|
int iPolyCount = 0;
|
||
|
|
||
|
for ( int i = 0; i < iPlaneCount; i++ )
|
||
|
{
|
||
|
Vector vPlaneNormal = pMovedPlanes[i].AsVector3D();
|
||
|
float fPlaneDist = pMovedPlanes[i].w;// + 50.0f;
|
||
|
|
||
|
if( vertsIn == NULL )
|
||
|
vertsIn = (float *)stackalloc( (iPlaneCount + 4) * (sizeof( float ) * 3) );
|
||
|
|
||
|
// Build a big-ass poly in this plane
|
||
|
int vertCount = PolyFromPlane( (Vector *)vertsIn, vPlaneNormal, fPlaneDist, 100000.0f );
|
||
|
|
||
|
//chop it by every other plane
|
||
|
for( int j = 0; j < iPlaneCount; j++ )
|
||
|
{
|
||
|
// don't clip planes with themselves
|
||
|
if ( i == j )
|
||
|
continue;
|
||
|
|
||
|
// Chop the polygon against this plane
|
||
|
vertCount = ClipPolyToPlane( (Vector *)vertsIn, vertCount, (Vector *)vertsOut, pMovedPlanes[j].AsVector3D(), pMovedPlanes[j].w, 0.0f );
|
||
|
|
||
|
//swap the input and output arrays
|
||
|
vertsSwap = vertsIn; vertsIn = vertsOut; vertsOut = vertsSwap;
|
||
|
|
||
|
// Less than a poly left, something's wrong, don't bother with this polygon
|
||
|
if ( vertCount < 3 )
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( vertCount < 3 )
|
||
|
continue; //not enough to work with
|
||
|
|
||
|
pPolygons[iPolyCount].iVertCount = vertCount;
|
||
|
pPolygons[iPolyCount].verts = vertsIn;
|
||
|
vertsIn = NULL;
|
||
|
++iPolyCount;
|
||
|
}
|
||
|
|
||
|
if( iPolyCount == 0 )
|
||
|
return false;
|
||
|
|
||
|
//initialize the AABB to the first point available
|
||
|
Vector vAABBMins, vAABBMaxs;
|
||
|
vAABBMins = vAABBMaxs = ((Vector *)pPolygons[0].verts)[0];
|
||
|
|
||
|
if( pAABBMins && pAABBMaxs ) //they want the full box
|
||
|
{
|
||
|
for( int i = 0; i != iPolyCount; ++i )
|
||
|
{
|
||
|
Vector *PolyVerts = (Vector *)pPolygons[i].verts;
|
||
|
for( int j = 0; j != pPolygons[i].iVertCount; ++j )
|
||
|
{
|
||
|
if( PolyVerts[j].x < vAABBMins.x )
|
||
|
vAABBMins.x = PolyVerts[j].x;
|
||
|
if( PolyVerts[j].y < vAABBMins.y )
|
||
|
vAABBMins.y = PolyVerts[j].y;
|
||
|
if( PolyVerts[j].z < vAABBMins.z )
|
||
|
vAABBMins.z = PolyVerts[j].z;
|
||
|
|
||
|
if( PolyVerts[j].x > vAABBMaxs.x )
|
||
|
vAABBMaxs.x = PolyVerts[j].x;
|
||
|
if( PolyVerts[j].y > vAABBMaxs.y )
|
||
|
vAABBMaxs.y = PolyVerts[j].y;
|
||
|
if( PolyVerts[j].z > vAABBMaxs.z )
|
||
|
vAABBMaxs.z = PolyVerts[j].z;
|
||
|
}
|
||
|
}
|
||
|
*pAABBMins = vAABBMins;
|
||
|
*pAABBMaxs = vAABBMaxs;
|
||
|
}
|
||
|
else if( pAABBMins ) //they only want the min
|
||
|
{
|
||
|
for( int i = 0; i != iPolyCount; ++i )
|
||
|
{
|
||
|
Vector *PolyVerts = (Vector *)pPolygons[i].verts;
|
||
|
for( int j = 0; j != pPolygons[i].iVertCount; ++j )
|
||
|
{
|
||
|
if( PolyVerts[j].x < vAABBMins.x )
|
||
|
vAABBMins.x = PolyVerts[j].x;
|
||
|
if( PolyVerts[j].y < vAABBMins.y )
|
||
|
vAABBMins.y = PolyVerts[j].y;
|
||
|
if( PolyVerts[j].z < vAABBMins.z )
|
||
|
vAABBMins.z = PolyVerts[j].z;
|
||
|
}
|
||
|
}
|
||
|
*pAABBMins = vAABBMins;
|
||
|
}
|
||
|
else //they only want the max
|
||
|
{
|
||
|
for( int i = 0; i != iPolyCount; ++i )
|
||
|
{
|
||
|
Vector *PolyVerts = (Vector *)pPolygons[i].verts;
|
||
|
for( int j = 0; j != pPolygons[i].iVertCount; ++j )
|
||
|
{
|
||
|
if( PolyVerts[j].x > vAABBMaxs.x )
|
||
|
vAABBMaxs.x = PolyVerts[j].x;
|
||
|
if( PolyVerts[j].y > vAABBMaxs.y )
|
||
|
vAABBMaxs.y = PolyVerts[j].y;
|
||
|
if( PolyVerts[j].z > vAABBMaxs.z )
|
||
|
vAABBMaxs.z = PolyVerts[j].z;
|
||
|
}
|
||
|
}
|
||
|
*pAABBMaxs = vAABBMaxs;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
CPolyhedron *ConvertLinkedGeometryToPolyhedron( GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pPolygons, GeneratePolyhedronFromPlanes_UnorderedLineLL *pLines, GeneratePolyhedronFromPlanes_UnorderedPointLL *pPoints, bool bUseTemporaryMemory, fltx4 vShiftResultPositions )
|
||
|
{
|
||
|
Assert( (pPolygons != NULL) && (pLines != NULL) && (pPoints != NULL) );
|
||
|
unsigned int iPolyCount = 0, iLineCount = 0, iPointCount = 0, iIndexCount = 0;
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pActivePolygonWalk = pPolygons;
|
||
|
do
|
||
|
{
|
||
|
++iPolyCount;
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pLineWalk = pActivePolygonWalk->polygon.pLines;
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pFirstLine = pLineWalk;
|
||
|
Assert( pLineWalk != NULL );
|
||
|
|
||
|
do
|
||
|
{
|
||
|
++iIndexCount;
|
||
|
pLineWalk = pLineWalk->pNext;
|
||
|
} while( pLineWalk != pFirstLine );
|
||
|
|
||
|
pActivePolygonWalk = pActivePolygonWalk->pNext;
|
||
|
} while( pActivePolygonWalk );
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_UnorderedLineLL *pActiveLineWalk = pLines;
|
||
|
do
|
||
|
{
|
||
|
++iLineCount;
|
||
|
pActiveLineWalk = pActiveLineWalk->pNext;
|
||
|
} while( pActiveLineWalk );
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPointLL *pActivePointWalk = pPoints;
|
||
|
do
|
||
|
{
|
||
|
++iPointCount;
|
||
|
pActivePointWalk = pActivePointWalk->pNext;
|
||
|
} while( pActivePointWalk );
|
||
|
|
||
|
CPolyhedron *pReturn;
|
||
|
if( bUseTemporaryMemory )
|
||
|
{
|
||
|
pReturn = GetTempPolyhedron( iPointCount, iLineCount, iIndexCount, iPolyCount );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pReturn = CPolyhedron_AllocByNew::Allocate( iPointCount, iLineCount, iIndexCount, iPolyCount );
|
||
|
}
|
||
|
|
||
|
Vector *pVertexArray = pReturn->pVertices;
|
||
|
Polyhedron_IndexedLine_t *pLineArray = pReturn->pLines;
|
||
|
Polyhedron_IndexedLineReference_t *pIndexArray = pReturn->pIndices;
|
||
|
Polyhedron_IndexedPolygon_t *pPolyArray = pReturn->pPolygons;
|
||
|
|
||
|
//copy points
|
||
|
pActivePointWalk = pPoints;
|
||
|
for( unsigned int i = 0; i != iPointCount; ++i )
|
||
|
{
|
||
|
#if defined( USE_WORLD_CENTERED_POSITIONS )
|
||
|
fltx4 vShiftedResult = AddSIMD( pActivePointWalk->point.ptPosition, vShiftResultPositions );
|
||
|
pVertexArray[i].Init( SubFloat( vShiftedResult, 0 ), SubFloat( vShiftedResult, 1 ), SubFloat( vShiftedResult, 2 ) );
|
||
|
#else
|
||
|
pVertexArray[i].Init( SubFloat( pActivePointWalk->point.ptPosition, 0 ), SubFloat( pActivePointWalk->point.ptPosition, 1 ), SubFloat( pActivePointWalk->point.ptPosition, 2 ) );
|
||
|
#endif
|
||
|
pActivePointWalk->point.iSaveIndices = i; //storing array indices
|
||
|
pActivePointWalk = pActivePointWalk->pNext;
|
||
|
}
|
||
|
|
||
|
//copy lines
|
||
|
pActiveLineWalk = pLines;
|
||
|
for( unsigned int i = 0; i != iLineCount; ++i )
|
||
|
{
|
||
|
pLineArray[i].iPointIndices[0] = (unsigned short)pActiveLineWalk->line.pPoints[0]->iSaveIndices;
|
||
|
pLineArray[i].iPointIndices[1] = (unsigned short)pActiveLineWalk->line.pPoints[1]->iSaveIndices;
|
||
|
|
||
|
pActiveLineWalk->line.iSaveIndices = i; //storing array indices
|
||
|
|
||
|
pActiveLineWalk = pActiveLineWalk->pNext;
|
||
|
}
|
||
|
|
||
|
//copy polygons and indices at the same time
|
||
|
pActivePolygonWalk = pPolygons;
|
||
|
iIndexCount = 0;
|
||
|
for( unsigned int i = 0; i != iPolyCount; ++i )
|
||
|
{
|
||
|
pPolyArray[i].polyNormal = pActivePolygonWalk->polygon.vSurfaceNormal;
|
||
|
pPolyArray[i].iFirstIndex = iIndexCount;
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pLineWalk = pActivePolygonWalk->polygon.pLines;
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pFirstLine = pLineWalk;
|
||
|
do
|
||
|
{
|
||
|
pIndexArray[iIndexCount].iLineIndex = pLineWalk->pLine->iSaveIndices;
|
||
|
pIndexArray[iIndexCount].iEndPointIndex = pLineWalk->iReferenceIndex;
|
||
|
|
||
|
++iIndexCount;
|
||
|
pLineWalk = pLineWalk->pNext;
|
||
|
} while( pLineWalk != pFirstLine );
|
||
|
|
||
|
pPolyArray[i].iIndexCount = iIndexCount - pPolyArray[i].iFirstIndex;
|
||
|
|
||
|
pActivePolygonWalk = pActivePolygonWalk->pNext;
|
||
|
}
|
||
|
|
||
|
#if defined( DBGFLAG_ASSERT ) && defined( ENABLE_DEBUG_POLYHEDRON_DUMPS ) && defined( DEBUG_DUMP_POLYHEDRONS_TO_NUMBERED_GLVIEWS )
|
||
|
char szCollisionFile[128];
|
||
|
CreateDumpDirectory( "PolyhedronDumps" );
|
||
|
Q_snprintf( szCollisionFile, 128, "PolyhedronDumps/NewStyle_PolyhedronDump%i.txt", g_iPolyhedronDumpCounter );
|
||
|
++g_iPolyhedronDumpCounter;
|
||
|
|
||
|
remove( szCollisionFile );
|
||
|
DumpPolyhedronToGLView( pReturn, szCollisionFile, &s_matIdentity );
|
||
|
DumpPolyhedronToGLView( pReturn, "PolyhedronDumps/NewStyle_PolyhedronDump_All-Appended.txt", &s_matIdentity );
|
||
|
#endif
|
||
|
|
||
|
#if defined( DEBUG_POLYHEDRON_CONVERSION ) && 0 //probably too redundant to check here
|
||
|
//last bit of debugging from whatever outside source wants this stupid thing
|
||
|
if( (g_pPolyhedronCarvingDebugStepCallback != NULL) && (pReturn != NULL) )
|
||
|
{
|
||
|
AssertMsg( g_pPolyhedronCarvingDebugStepCallback( pReturn ), "Outside conversion failed" );
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
return pReturn;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
#ifdef DBGFLAG_ASSERT
|
||
|
|
||
|
void DumpPointListToGLView( GeneratePolyhedronFromPlanes_UnorderedPointLL *pHead, PolyhedronPointPlanarity planarity, const Vector &vColor, const char *szDumpFile, const VMatrix *pTransform )
|
||
|
{
|
||
|
#ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS
|
||
|
if( pTransform == NULL )
|
||
|
pTransform = &s_matIdentity;
|
||
|
|
||
|
FILE *pFile = fopen( szDumpFile, "ab" );
|
||
|
|
||
|
while( pHead )
|
||
|
{
|
||
|
if( pHead->point.planarity == planarity )
|
||
|
{
|
||
|
const Vector vPointExtents( 0.5f, 0.5f, 0.01f );
|
||
|
fltx4 f4Pos = pHead->point.ptPosition;
|
||
|
Vector vPos( SubFloat( f4Pos, 0 ), SubFloat( f4Pos, 1 ), SubFloat( f4Pos, 2 ) );
|
||
|
DumpAABBToGLView( (*pTransform) * vPos, vPointExtents, vColor, pFile );
|
||
|
}
|
||
|
pHead = pHead->pNext;
|
||
|
}
|
||
|
|
||
|
fclose( pFile );
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
const char * DumpPolyhedronCutHistory( const CUtlVector<CPolyhedron *> &DumpedHistory, const CUtlVector<const float *> &CutHistory, const VMatrix *pTransform )
|
||
|
{
|
||
|
#ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS
|
||
|
if( pTransform == NULL )
|
||
|
pTransform = &s_matIdentity;
|
||
|
|
||
|
static char szDumpFile[100] = "FailedPolyhedronCut_Error.txt"; //most recent filename returned for further dumping
|
||
|
|
||
|
for( int i = 0; i != DumpedHistory.Count(); ++i )
|
||
|
{
|
||
|
if( DumpedHistory[i] != NULL )
|
||
|
{
|
||
|
Q_snprintf( szDumpFile, 100, "FailedPolyhedronCut_%d.txt", i );
|
||
|
|
||
|
DumpPolyhedronToGLView( DumpedHistory[i], szDumpFile, pTransform, "wb" );
|
||
|
if( CutHistory.Count() > i )
|
||
|
{
|
||
|
DumpPlaneToGlView( CutHistory[i], 1.0f, szDumpFile, pTransform );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return szDumpFile;
|
||
|
#else
|
||
|
return NULL;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
#ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS
|
||
|
#define DUMP_POLYHEDRON_SCALE 10.0f
|
||
|
bool g_bDumpNullPolyhedrons = false;
|
||
|
#define AssertMsg_DumpPolyhedron_Destructors(condition, destructors, message)\
|
||
|
if( (condition) == false )\
|
||
|
{\
|
||
|
if( ((destructors).DebugCutHistory.Count() != 0) && ((destructors).PlaneCutHistory.Count() != 0) )\
|
||
|
{\
|
||
|
VMatrix matTransform;\
|
||
|
matTransform.Identity();\
|
||
|
matTransform[0][0] = matTransform[1][1] = matTransform[2][2] = DUMP_POLYHEDRON_SCALE;\
|
||
|
matTransform.SetTranslation( -(destructors).DebugCutHistory.Tail()->Center() * DUMP_POLYHEDRON_SCALE );\
|
||
|
const char *szLastDumpFile = DumpPolyhedronCutHistory( (destructors).DebugCutHistory, (destructors).PlaneCutHistory, &matTransform );\
|
||
|
DumpPointListToGLView( (destructors).pAllPoints, POINT_ALIVE, Vector( 0.9f, 0.9f, 0.9f ), szLastDumpFile, &matTransform );\
|
||
|
DumpPointListToGLView( (destructors).pAllPoints, POINT_ONPLANE, Vector( 0.5f, 0.5f, 0.5f ), szLastDumpFile, &matTransform );\
|
||
|
DumpPointListToGLView( (destructors).pDeadPointCollection, POINT_DEAD, Vector( 0.1f, 0.1f, 0.1f ), szLastDumpFile, &matTransform );\
|
||
|
DumpWorkingStatePolyhedron( (destructors).pAllPolygons, (destructors).pDeadPolygonCollection, (destructors).pAllLines, (destructors).pDeadLineCollection, (destructors).pAllPoints, (destructors).pDeadPointCollection, "FailedPolyhedronCut_LastCutDebug.txt", &matTransform );\
|
||
|
DumpPlaneToGlView( (destructors).PlaneCutHistory.Tail(), 1.0f, "FailedPolyhedronCut_LastCutDebug.txt", &matTransform );\
|
||
|
}\
|
||
|
AssertMsg( condition, message );\
|
||
|
}
|
||
|
#else
|
||
|
#define AssertMsg_DumpPolyhedron_Destructors(condition, destructors, message) AssertMsg( condition, message )
|
||
|
#endif
|
||
|
|
||
|
#else
|
||
|
|
||
|
#define AssertMsg_DumpPolyhedron_Destructors(condition, destructors, message) NULL;
|
||
|
|
||
|
#endif
|
||
|
|
||
|
#define Assert_DumpPolyhedron_Destructors(condition, destructors) AssertMsg_DumpPolyhedron_Destructors( condition, destructors, #condition )
|
||
|
|
||
|
#define AssertMsg_DumpPolyhedron(condition, message) AssertMsg_DumpPolyhedron_Destructors(condition, destructors, message)
|
||
|
#define Assert_DumpPolyhedron(condition) Assert_DumpPolyhedron_Destructors( condition, destructors )
|
||
|
|
||
|
|
||
|
//a little class that acts like a small block heap, using stack memory given to it
|
||
|
class CStackMemoryDispenser
|
||
|
{
|
||
|
public:
|
||
|
CStackMemoryDispenser( void *pStackAllocation, size_t iStackAllocationSize )
|
||
|
{
|
||
|
m_pDispenserBuffer = (unsigned char *)pStackAllocation;
|
||
|
m_iDispenserSizeLeft = iStackAllocationSize;
|
||
|
m_pDeleteList = NULL;
|
||
|
}
|
||
|
|
||
|
~CStackMemoryDispenser( void )
|
||
|
{
|
||
|
RecurseDelete( m_pDeleteList );
|
||
|
}
|
||
|
|
||
|
void *Allocate( size_t iSize, size_t iAlignTo = 16 )
|
||
|
{
|
||
|
AssertMsg( ((iAlignTo - 1) & iAlignTo) == 0, "Alignment must be a power of 2" );
|
||
|
size_t iAlignOffset = iAlignTo - ((size_t)m_pDispenserBuffer) & (iAlignTo - 1);
|
||
|
m_iDispenserSizeLeft -= iAlignOffset;
|
||
|
m_pDispenserBuffer += iAlignOffset;
|
||
|
|
||
|
if( iSize > m_iDispenserSizeLeft )
|
||
|
{
|
||
|
//allocate a new buffer
|
||
|
size_t iNewBufferSize = MAX( 128 * 1024, (iSize + iAlignTo) * 2 ); //either allocate 128k or enough to hold 2x the allocation.
|
||
|
unsigned char *pNewBuffer = new unsigned char [iNewBufferSize]; //allocate 128k at a time
|
||
|
*(void **)pNewBuffer = NULL;
|
||
|
|
||
|
//insert this allocation into the linked list of allocations to delete on destruct
|
||
|
void **pWriteDeleteAddress = &m_pDeleteList;
|
||
|
while( *pWriteDeleteAddress != NULL )
|
||
|
{
|
||
|
pWriteDeleteAddress = (void **)*pWriteDeleteAddress;
|
||
|
}
|
||
|
*pWriteDeleteAddress = pNewBuffer;
|
||
|
|
||
|
//save this as the new dispenser buffer, skipping the linked list pointer
|
||
|
m_pDispenserBuffer = pNewBuffer + sizeof( void * );
|
||
|
m_iDispenserSizeLeft = iNewBufferSize - sizeof( void * );
|
||
|
|
||
|
iAlignOffset = iAlignTo - ((size_t)m_pDispenserBuffer) & (iAlignTo - 1); //recompute alignment offset
|
||
|
m_iDispenserSizeLeft -= iAlignOffset;
|
||
|
m_pDispenserBuffer += iAlignOffset;
|
||
|
}
|
||
|
|
||
|
void *pRetVal = m_pDispenserBuffer;
|
||
|
m_pDispenserBuffer += iSize;
|
||
|
m_iDispenserSizeLeft -= iSize;
|
||
|
Assert( (((size_t)pRetVal) & (iAlignTo - 1)) == 0 );
|
||
|
return pRetVal;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
static void RecurseDelete( void *pDelete )
|
||
|
{
|
||
|
if( pDelete != NULL )
|
||
|
{
|
||
|
RecurseDelete( *(void **)pDelete );
|
||
|
delete [](void**)pDelete;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
unsigned char *m_pDispenserBuffer;
|
||
|
size_t m_iDispenserSizeLeft;
|
||
|
void *m_pDeleteList; //a linked list of pointers to actual memory allocations we had to make that need to be deleted. The first thing in each allocation is a reserved space for another pointer
|
||
|
};
|
||
|
|
||
|
template<class T>
|
||
|
class CStackItemDispenser
|
||
|
{
|
||
|
public:
|
||
|
CStackItemDispenser( CStackMemoryDispenser &MemoryDispenser ) : m_FallbackDispenser( MemoryDispenser )
|
||
|
{
|
||
|
COMPILE_TIME_ASSERT( sizeof( T ) > sizeof( void * ) );
|
||
|
m_pHead = NULL;
|
||
|
}
|
||
|
|
||
|
T *Allocate( void )
|
||
|
{
|
||
|
if( m_pHead != NULL )
|
||
|
{
|
||
|
T *pRetVal = m_pHead;
|
||
|
m_pHead = *(T **)m_pHead;
|
||
|
#ifdef DBGFLAG_ASSERT
|
||
|
memset( pRetVal, 0xCCCCCCCC, sizeof( T ) );
|
||
|
#endif
|
||
|
return pRetVal;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return (T *)m_FallbackDispenser.Allocate( sizeof( T ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void Free( T *pFree )
|
||
|
{
|
||
|
*(T **)pFree = m_pHead;
|
||
|
m_pHead = pFree;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
CStackMemoryDispenser &m_FallbackDispenser;
|
||
|
T *m_pHead;
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
inline void ComputePlanarDistances( GeneratePolyhedronFromPlanes_UnorderedPointLL *pAllPoints, int iPointCount, fltx4 fPlane )
|
||
|
{
|
||
|
uint8 *pAlignedAlloc = (uint8 *)stackalloc( (iPointCount) * sizeof(fltx4) + 15 );
|
||
|
pAlignedAlloc = (uint8 *)(((size_t)(pAlignedAlloc + 15)) & ~15);
|
||
|
fltx4 *pIntermediateResults = (fltx4 *)pAlignedAlloc;
|
||
|
|
||
|
int i = 0;
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPointLL *pPointWalk = pAllPoints;
|
||
|
do
|
||
|
{
|
||
|
Assert( SubFloat( pPointWalk->point.ptPosition, 3 ) == -1.0f );
|
||
|
pIntermediateResults[i] = MulSIMD( fPlane, pPointWalk->point.ptPosition );
|
||
|
++i;
|
||
|
pPointWalk = pPointWalk->pNext;
|
||
|
} while( pPointWalk != NULL );
|
||
|
|
||
|
i = 0;
|
||
|
pPointWalk = pAllPoints;
|
||
|
do
|
||
|
{
|
||
|
pPointWalk->point.fPlaneDist = SubFloat( pIntermediateResults[i], 0 ) + SubFloat( pIntermediateResults[i], 1 ) + SubFloat( pIntermediateResults[i], 2 ) + SubFloat( pIntermediateResults[i], 3 );
|
||
|
DBG_ONLY( pPointWalk->point.debugdata.fInitialPlaneDistance = pPointWalk->point.fPlaneDist; );
|
||
|
++i;
|
||
|
pPointWalk = pPointWalk->pNext;
|
||
|
} while( pPointWalk != NULL );
|
||
|
}
|
||
|
|
||
|
class CClipLinkedGeometryDestructors
|
||
|
{
|
||
|
public:
|
||
|
int &iPointCount;
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPolygonLL *&pAllPolygons;
|
||
|
GeneratePolyhedronFromPlanes_UnorderedLineLL *&pAllLines;
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPointLL *&pAllPoints;
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPointLL *&pDeadPointCollection;
|
||
|
|
||
|
#ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPolygonLL *&pDeadPolygonCollection;
|
||
|
GeneratePolyhedronFromPlanes_UnorderedLineLL *&pDeadLineCollection;
|
||
|
|
||
|
CClipLinkedGeometryDestructors( int &iPointCount_IN,
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPolygonLL *&pAllPolygons_IN,
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPolygonLL *&pDeadPolygonCollection_IN,
|
||
|
GeneratePolyhedronFromPlanes_UnorderedLineLL *&pAllLines_IN,
|
||
|
GeneratePolyhedronFromPlanes_UnorderedLineLL *&pDeadLineCollection_IN,
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPointLL *&pAllPoints_IN,
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPointLL *&pDeadPointCollection_IN )
|
||
|
: iPointCount( iPointCount_IN ), pAllPolygons( pAllPolygons_IN ), pAllLines( pAllLines_IN ), pDeadPolygonCollection( pDeadPolygonCollection_IN ), pDeadLineCollection( pDeadLineCollection_IN ), pAllPoints( pAllPoints_IN ), pDeadPointCollection( pDeadPointCollection_IN ) {};
|
||
|
|
||
|
#else
|
||
|
CStackItemDispenser<GeneratePolyhedronFromPlanes_UnorderedPolygonLL> &polygonAllocator;
|
||
|
CStackItemDispenser<GeneratePolyhedronFromPlanes_UnorderedLineLL> &lineAllocator;
|
||
|
|
||
|
CClipLinkedGeometryDestructors( int &iPointCount_IN,
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPolygonLL *&pAllPolygons_IN,
|
||
|
CStackItemDispenser<GeneratePolyhedronFromPlanes_UnorderedPolygonLL> &polygonAllocator_IN,
|
||
|
GeneratePolyhedronFromPlanes_UnorderedLineLL *&pAllLines_IN,
|
||
|
CStackItemDispenser<GeneratePolyhedronFromPlanes_UnorderedLineLL> &lineAllocator_IN,
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPointLL *&pAllPoints_IN,
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPointLL *&pDeadPointCollection_IN )
|
||
|
: iPointCount( iPointCount_IN ), pAllPolygons( pAllPolygons_IN ), pAllLines( pAllLines_IN ), polygonAllocator( polygonAllocator_IN ), lineAllocator( lineAllocator_IN ), pAllPoints( pAllPoints_IN ), pDeadPointCollection( pDeadPointCollection_IN ) {};
|
||
|
#endif
|
||
|
|
||
|
#if defined( DBGFLAG_ASSERT )
|
||
|
//let some generic debug data hitch a ride on this structure since it goes pretty much everywhere
|
||
|
CUtlVector<CPolyhedron *> DebugCutHistory;
|
||
|
CUtlVector<const float *> PlaneCutHistory;
|
||
|
CUtlVector<int> DebugCutPlaneIndex;
|
||
|
bool bDebugTrigger;
|
||
|
|
||
|
~CClipLinkedGeometryDestructors( void )
|
||
|
{
|
||
|
for( int i = 0; i != DebugCutHistory.Count(); ++i )
|
||
|
{
|
||
|
if( DebugCutHistory[i] != NULL )
|
||
|
{
|
||
|
DebugCutHistory[i]->Release();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
static FORCEINLINE GeneratePolyhedronFromPlanes_UnorderedPointLL *DestructPoint( GeneratePolyhedronFromPlanes_UnorderedPointLL *pKillPoint, CClipLinkedGeometryDestructors &destructors )
|
||
|
{
|
||
|
#if defined( DBGFLAG_ASSERT )
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPointLL *pDeadPointWalk = destructors.pDeadPointCollection;
|
||
|
while( pDeadPointWalk )
|
||
|
{
|
||
|
Assert( pDeadPointWalk != pKillPoint );
|
||
|
pDeadPointWalk = pDeadPointWalk->pNext;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPointLL *pRetVal = pKillPoint->pNext;
|
||
|
DBG_ONLY( pKillPoint->point.planarity = POINT_DEAD; );
|
||
|
if( pKillPoint->pNext )
|
||
|
{
|
||
|
pKillPoint->pNext->pPrev = pKillPoint->pPrev;
|
||
|
}
|
||
|
|
||
|
if( pKillPoint == destructors.pAllPoints )
|
||
|
{
|
||
|
destructors.pAllPoints = pKillPoint->pNext;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pKillPoint->pPrev->pNext = pKillPoint->pNext;
|
||
|
}
|
||
|
|
||
|
pKillPoint->pNext = destructors.pDeadPointCollection;
|
||
|
destructors.pDeadPointCollection = pKillPoint;
|
||
|
|
||
|
--destructors.iPointCount;
|
||
|
|
||
|
return pRetVal;
|
||
|
}
|
||
|
static FORCEINLINE GeneratePolyhedronFromPlanes_UnorderedPointLL *DestructPoint( GeneratePolyhedronFromPlanes_Point *pKillPoint, CClipLinkedGeometryDestructors &destructors )
|
||
|
{
|
||
|
#ifdef OSX
|
||
|
Assert( &(((GeneratePolyhedronFromPlanes_UnorderedPointLL *)pKillPoint)->point) == pKillPoint );
|
||
|
#else
|
||
|
// This COMPILE_TIME_ASSERT was breaking gcc under OSX
|
||
|
COMPILE_TIME_ASSERT( offsetof(GeneratePolyhedronFromPlanes_UnorderedPointLL, point) == 0 );
|
||
|
#endif
|
||
|
return DestructPoint( (GeneratePolyhedronFromPlanes_UnorderedPointLL *)pKillPoint, destructors );
|
||
|
}
|
||
|
|
||
|
static FORCEINLINE GeneratePolyhedronFromPlanes_UnorderedLineLL *DestructLine( GeneratePolyhedronFromPlanes_UnorderedLineLL *pKillLine, CClipLinkedGeometryDestructors &destructors )
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_UnorderedLineLL *pRetVal = pKillLine->pNext;
|
||
|
DBG_ONLY( pKillLine->line.planarity = LINE_DEAD; );
|
||
|
|
||
|
if( pKillLine->pNext )
|
||
|
{
|
||
|
pKillLine->pNext->pPrev = pKillLine->pPrev;
|
||
|
}
|
||
|
|
||
|
if( pKillLine == destructors.pAllLines )
|
||
|
{
|
||
|
destructors.pAllLines = pKillLine->pNext;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pKillLine->pPrev->pNext = pKillLine->pNext;
|
||
|
}
|
||
|
|
||
|
#ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS
|
||
|
pKillLine->pNext = destructors.pDeadLineCollection;
|
||
|
destructors.pDeadLineCollection = pKillLine;
|
||
|
#else
|
||
|
destructors.lineAllocator.Free( pKillLine );
|
||
|
#endif
|
||
|
|
||
|
return pRetVal;
|
||
|
}
|
||
|
static FORCEINLINE GeneratePolyhedronFromPlanes_UnorderedLineLL *DestructLine( GeneratePolyhedronFromPlanes_Line *pKillLine, CClipLinkedGeometryDestructors &destructors )
|
||
|
{
|
||
|
#ifdef OSX
|
||
|
Assert( &(((GeneratePolyhedronFromPlanes_UnorderedLineLL *)pKillLine)->line) == pKillLine );
|
||
|
#else
|
||
|
// This COMPILE_TIME_ASSERT was breaking gcc under OSX
|
||
|
COMPILE_TIME_ASSERT( offsetof(GeneratePolyhedronFromPlanes_UnorderedLineLL, line) == 0 );
|
||
|
#endif
|
||
|
return DestructLine( (GeneratePolyhedronFromPlanes_UnorderedLineLL *)pKillLine, destructors );
|
||
|
}
|
||
|
|
||
|
static FORCEINLINE void UnlinkLine( GeneratePolyhedronFromPlanes_Line *pUnlinkLine )
|
||
|
{
|
||
|
//disconnect the line from everything
|
||
|
for( int i = 0; i != 2; ++i )
|
||
|
{
|
||
|
pUnlinkLine->PointLineLinks[i].pNext->pPrev = pUnlinkLine->PointLineLinks[i].pPrev;
|
||
|
pUnlinkLine->PointLineLinks[i].pPrev->pNext = pUnlinkLine->PointLineLinks[i].pNext;
|
||
|
|
||
|
pUnlinkLine->PolygonLineLinks[i].pNext->pPrev = pUnlinkLine->PolygonLineLinks[i].pPrev;
|
||
|
pUnlinkLine->PolygonLineLinks[i].pPrev->pNext = pUnlinkLine->PolygonLineLinks[i].pNext;
|
||
|
|
||
|
pUnlinkLine->pPoints[i]->pConnectedLines = pUnlinkLine->PointLineLinks[i].pNext;
|
||
|
pUnlinkLine->pPolygons[i]->pLines = pUnlinkLine->PolygonLineLinks[i].pNext;
|
||
|
|
||
|
Assert( (pUnlinkLine->pPoints[i]->pConnectedLines != &pUnlinkLine->PointLineLinks[i]) || (pUnlinkLine->pPoints[i]->planarity == POINT_DEAD) );
|
||
|
Assert( (pUnlinkLine->pPolygons[i]->pLines != &pUnlinkLine->PolygonLineLinks[i]) || (pUnlinkLine->pPolygons[i]->bDead == true) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static FORCEINLINE void UnlinkLine( GeneratePolyhedronFromPlanes_UnorderedLineLL *pUnlinkLine )
|
||
|
{
|
||
|
UnlinkLine( &pUnlinkLine->line );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
static FORCEINLINE GeneratePolyhedronFromPlanes_UnorderedPolygonLL *DestructPolygon( GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pKillPolygon, CClipLinkedGeometryDestructors &destructors )
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pRetVal = pKillPolygon->pNext;
|
||
|
if( pKillPolygon->pNext )
|
||
|
{
|
||
|
pKillPolygon->pNext->pPrev = pKillPolygon->pPrev;
|
||
|
}
|
||
|
|
||
|
if( pKillPolygon == destructors.pAllPolygons )
|
||
|
{
|
||
|
destructors.pAllPolygons = pKillPolygon->pNext;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pKillPolygon->pPrev->pNext = pKillPolygon->pNext;
|
||
|
}
|
||
|
|
||
|
#ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS
|
||
|
pKillPolygon->pNext = destructors.pDeadPolygonCollection;
|
||
|
destructors.pDeadPolygonCollection = pKillPolygon;
|
||
|
#else
|
||
|
destructors.polygonAllocator.Free( pKillPolygon );
|
||
|
#endif
|
||
|
|
||
|
return pRetVal;
|
||
|
}
|
||
|
|
||
|
static FORCEINLINE GeneratePolyhedronFromPlanes_UnorderedPolygonLL *DestructPolygon( GeneratePolyhedronFromPlanes_Polygon *pKillPolygon, CClipLinkedGeometryDestructors &destructors )
|
||
|
{
|
||
|
#ifdef OSX
|
||
|
Assert( &(((GeneratePolyhedronFromPlanes_UnorderedPolygonLL *)pKillPolygon)->polygon) == pKillPolygon );
|
||
|
#else
|
||
|
// This COMPILE_TIME_ASSERT was breaking gcc under OSX
|
||
|
COMPILE_TIME_ASSERT( offsetof(GeneratePolyhedronFromPlanes_UnorderedPolygonLL, polygon) == 0 );
|
||
|
#endif
|
||
|
return DestructPolygon( (GeneratePolyhedronFromPlanes_UnorderedPolygonLL *)pKillPolygon, destructors );
|
||
|
}
|
||
|
|
||
|
//remove a known degenerate polygon. Returns INVALID pointer to the line we destroy in here.
|
||
|
GeneratePolyhedronFromPlanes_Line *RemoveDegeneratePolygon( GeneratePolyhedronFromPlanes_Polygon *pDegeneratePolygon, CClipLinkedGeometryDestructors &destructors )
|
||
|
{
|
||
|
Assert_DumpPolyhedron( pDegeneratePolygon->pLines->pNext == pDegeneratePolygon->pLines->pPrev );
|
||
|
Assert_DumpPolyhedron( pDegeneratePolygon->pLines->pNext != pDegeneratePolygon->pLines ); //just for the sake of paranoia. Check that it's not a single-lined polygon
|
||
|
|
||
|
Assert_DumpPolyhedron( (pDegeneratePolygon->pLines->pLine->pPoints[pDegeneratePolygon->pLines->iReferenceIndex] == pDegeneratePolygon->pLines->pNext->pLine->pPoints[1 - pDegeneratePolygon->pLines->pNext->iReferenceIndex]) &&
|
||
|
(pDegeneratePolygon->pLines->pLine->pPoints[1 - pDegeneratePolygon->pLines->iReferenceIndex] == pDegeneratePolygon->pLines->pNext->pLine->pPoints[pDegeneratePolygon->pLines->pNext->iReferenceIndex]) );
|
||
|
|
||
|
//both the lines are the same, just ditch one and link up the real polygons
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pKeepLine = pDegeneratePolygon->pLines;
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pDeadLine = pKeepLine->pNext;
|
||
|
GeneratePolyhedronFromPlanes_Line *pRetVal = pDeadLine->pLine;
|
||
|
|
||
|
#if defined( DBGFLAG_ASSERT ) || defined( ENABLE_DEBUG_POLYHEDRON_DUMPS )
|
||
|
pDegeneratePolygon->bDead = true; //to avoid future asserts, also for coloration. Not useful otherwise as we're about to delete it.
|
||
|
pDeadLine->pLine->planarity = LINE_DEAD;
|
||
|
#endif
|
||
|
|
||
|
pKeepLine->pLine->pPolygons[pKeepLine->iReferenceIndex] = pDeadLine->pLine->pPolygons[1 - pDeadLine->iReferenceIndex];
|
||
|
|
||
|
//unlink pDeadLine from the dead polygon so it doesn't mess with pKeepLine when we unlink it
|
||
|
pDeadLine->pLine->PolygonLineLinks[pDeadLine->iReferenceIndex].pNext = &pDeadLine->pLine->PolygonLineLinks[pDeadLine->iReferenceIndex];
|
||
|
pDeadLine->pLine->PolygonLineLinks[pDeadLine->iReferenceIndex].pPrev = &pDeadLine->pLine->PolygonLineLinks[pDeadLine->iReferenceIndex];
|
||
|
|
||
|
//insert pKeepLine just after pDeadLine on pDeadLine's flip side polygon, so when we unlink pDeadLine, everything links into place smoothly
|
||
|
pKeepLine->pLine->PolygonLineLinks[pKeepLine->iReferenceIndex].pPrev = &pDeadLine->pLine->PolygonLineLinks[1 - pDeadLine->iReferenceIndex];
|
||
|
pKeepLine->pLine->PolygonLineLinks[pKeepLine->iReferenceIndex].pNext = pDeadLine->pLine->PolygonLineLinks[1 - pDeadLine->iReferenceIndex].pNext;
|
||
|
pKeepLine->pLine->PolygonLineLinks[pKeepLine->iReferenceIndex].pPrev->pNext = &pKeepLine->pLine->PolygonLineLinks[pKeepLine->iReferenceIndex];
|
||
|
pKeepLine->pLine->PolygonLineLinks[pKeepLine->iReferenceIndex].pNext->pPrev = &pKeepLine->pLine->PolygonLineLinks[pKeepLine->iReferenceIndex];
|
||
|
|
||
|
UnlinkLine( pDeadLine->pLine );
|
||
|
DestructLine( pDeadLine->pLine, destructors );
|
||
|
DestructPolygon( pDegeneratePolygon, destructors );
|
||
|
return pRetVal;
|
||
|
}
|
||
|
|
||
|
|
||
|
//search/kill redundant points on the specified polygon
|
||
|
void RemoveDegeneratePoints( GeneratePolyhedronFromPlanes_Polygon *pSearchPolygon, CClipLinkedGeometryDestructors &destructors )
|
||
|
{
|
||
|
AssertMsg( destructors.pAllPolygons->pNext != NULL && destructors.pAllPolygons->pNext->pNext != NULL, "RemoveDegeneratePoints() is not safe to run on 2D polyhedrons, early out before you get here" );
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pHeadLine = pSearchPolygon->pLines;
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pWalkLine = pHeadLine;
|
||
|
do
|
||
|
{
|
||
|
while( true ) //inner loop to support retesting the same line over and over again( even if it's the head )
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pPointLineLink = &pWalkLine->pLine->PointLineLinks[1 - pWalkLine->iReferenceIndex];
|
||
|
|
||
|
Assert_DumpPolyhedron( (pPointLineLink->pLine->pPolygons[0]->pLines != pPointLineLink->pLine->pPolygons[0]->pLines->pNext) &&
|
||
|
(pPointLineLink->pLine->pPolygons[0]->pLines != pPointLineLink->pLine->pPolygons[0]->pLines->pNext->pNext) &&
|
||
|
(pPointLineLink->pLine->pPolygons[1]->pLines != pPointLineLink->pLine->pPolygons[1]->pLines->pNext) &&
|
||
|
(pPointLineLink->pLine->pPolygons[1]->pLines != pPointLineLink->pLine->pPolygons[1]->pLines->pNext->pNext) );
|
||
|
|
||
|
//try iterating forward 2 lines, jumping over already-dead lines.
|
||
|
//if we end up where we started, the point is redundant
|
||
|
|
||
|
//go forward 1
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pCircleBackLineLink = pPointLineLink->pNext;
|
||
|
|
||
|
//and again
|
||
|
pCircleBackLineLink = pCircleBackLineLink->pNext;
|
||
|
|
||
|
//point is connected to only 2 lines. This can only be part of a convex if that convex is entirely 2D or if the lines perfectly agree with eachother.
|
||
|
//Based on the assumption that the convex is 3D. We can force the lines to perfectly agree with each other by eliminating one and patching the other to do the work of both.
|
||
|
if( pCircleBackLineLink == pPointLineLink )
|
||
|
{
|
||
|
//connect the root of the next line to our root, this way multiple occurrences in a row can chain to the same root
|
||
|
GeneratePolyhedronFromPlanes_Point *pDeadPoint = pWalkLine->pLine->pPoints[1 - pWalkLine->iReferenceIndex];
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pRootLine = &pWalkLine->pPrev->pLine->PointLineLinks[1 - pWalkLine->pPrev->iReferenceIndex];
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pSurvivingLine = pWalkLine;
|
||
|
|
||
|
Assert_DumpPolyhedron( pDeadPoint->planarity != POINT_DEAD );
|
||
|
Assert_DumpPolyhedron( pRootLine->pLine->planarity != LINE_DEAD );
|
||
|
|
||
|
if( pWalkLine->pPrev == pHeadLine )
|
||
|
{
|
||
|
pHeadLine = pWalkLine;
|
||
|
}
|
||
|
|
||
|
pSurvivingLine = &pSurvivingLine->pLine->PointLineLinks[1 - pSurvivingLine->iReferenceIndex]; //convert it to point space
|
||
|
#if defined( DBGFLAG_ASSERT ) || defined( ENABLE_DEBUG_POLYHEDRON_DUMPS )
|
||
|
pDeadPoint->planarity = POINT_DEAD;
|
||
|
pRootLine->pLine->planarity = LINE_DEAD;
|
||
|
#endif
|
||
|
|
||
|
//the dead line/point removal code will unlink these properly. But will leave surviving line's endpoint pointing at the dead point.
|
||
|
//relink surviving to the root point
|
||
|
{
|
||
|
//unlink dead point from surviving line
|
||
|
pSurvivingLine->pNext->pPrev = pSurvivingLine->pPrev;
|
||
|
pSurvivingLine->pPrev->pNext = pSurvivingLine->pNext;
|
||
|
|
||
|
//arbitrarily insert after dead line on the root point, before or after doesn't matter as it'll be unlinked soon anyway
|
||
|
pSurvivingLine->pNext = pRootLine->pNext;
|
||
|
pSurvivingLine->pNext->pPrev = pSurvivingLine;
|
||
|
pSurvivingLine->pPrev = pRootLine;
|
||
|
pRootLine->pNext = pSurvivingLine;
|
||
|
|
||
|
//steal root from dead line
|
||
|
pSurvivingLine->pLine->pPoints[1 - pSurvivingLine->iReferenceIndex] = pRootLine->pLine->pPoints[1 - pRootLine->iReferenceIndex];
|
||
|
}
|
||
|
|
||
|
//pRootLine is fully connected to the root point, dead point, and both polygons. Unlink should work properly
|
||
|
UnlinkLine( pRootLine->pLine );
|
||
|
DestructLine( pRootLine->pLine, destructors );
|
||
|
DestructPoint( pDeadPoint, destructors );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pWalkLine = pWalkLine->pNext;
|
||
|
} while (pWalkLine != pHeadLine);
|
||
|
}
|
||
|
|
||
|
//given two lines that are both connected to the same two points, merge them.
|
||
|
//returns true if any deleted line was on the new polygon's edge. (could have deleted pValidLineForNewPolygon)
|
||
|
//pMergeLine[1] needs to be deleted after completion
|
||
|
static bool MergeTwoLines( GeneratePolyhedronFromPlanes_Line *pMergeLines[2], int iDyingPolygonReferenceIndices[2], CClipLinkedGeometryDestructors &destructors, bool bAllowNullPolygonCollapse )
|
||
|
{
|
||
|
//pMergeLines[0] will be the surviving line and pMergeLines[1] will be eliminated.
|
||
|
Assert( pMergeLines[0]->pPolygons[iDyingPolygonReferenceIndices[0]] == pMergeLines[1]->pPolygons[iDyingPolygonReferenceIndices[1]] );
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pKillPolygon = (GeneratePolyhedronFromPlanes_UnorderedPolygonLL *)pMergeLines[1]->pPolygons[iDyingPolygonReferenceIndices[1]];
|
||
|
|
||
|
//merge the polygon pointer from good side of 1 to the dead side of 0
|
||
|
pMergeLines[0]->pPolygons[iDyingPolygonReferenceIndices[0]] = pMergeLines[1]->pPolygons[1 - iDyingPolygonReferenceIndices[1]];
|
||
|
|
||
|
bool bDeletedALineBorderingNewPolygon = (pMergeLines[1]->pPolygons[0] == NULL) || (pMergeLines[1]->pPolygons[1] == NULL);
|
||
|
|
||
|
//swap the link from the polygon attached to 1 so it points at 0 instead of 1
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pEditLink = &pMergeLines[1]->PolygonLineLinks[1 - iDyingPolygonReferenceIndices[1]];
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pSwappedLink = &pMergeLines[0]->PolygonLineLinks[iDyingPolygonReferenceIndices[0]];
|
||
|
pSwappedLink->pNext = pEditLink->pNext;
|
||
|
pSwappedLink->pPrev = pEditLink->pPrev;
|
||
|
pSwappedLink->pPrev->pNext = pSwappedLink;
|
||
|
pSwappedLink->pNext->pPrev = pSwappedLink;
|
||
|
if( pMergeLines[1]->pPolygons[1 - iDyingPolygonReferenceIndices[1]]->pLines == pEditLink )
|
||
|
{
|
||
|
pMergeLines[1]->pPolygons[1 - iDyingPolygonReferenceIndices[1]]->pLines = pSwappedLink;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//have all associated points use 0 as their head pointer in case any were using 1 as a head pointer.
|
||
|
pMergeLines[0]->pPoints[0]->pConnectedLines = &pMergeLines[0]->PointLineLinks[0];
|
||
|
pMergeLines[0]->pPoints[1]->pConnectedLines = &pMergeLines[0]->PointLineLinks[1];
|
||
|
|
||
|
//unlink 1 from both points
|
||
|
{
|
||
|
for( int i = 0; i != 2; ++i )
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pUnlinkLine = &pMergeLines[1]->PointLineLinks[i];
|
||
|
pUnlinkLine->pNext->pPrev = pUnlinkLine->pPrev;
|
||
|
pUnlinkLine->pPrev->pNext = pUnlinkLine->pNext;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//kill the line
|
||
|
DestructLine( pMergeLines[1], destructors );
|
||
|
|
||
|
if( pKillPolygon || bAllowNullPolygonCollapse )
|
||
|
{
|
||
|
if( pKillPolygon ) //kill the polygon as well
|
||
|
{
|
||
|
DestructPolygon( pKillPolygon, destructors );
|
||
|
}
|
||
|
|
||
|
//in cases where we collapse a polygon, it's possible for the new line to be completely redundant along with 1 point in a set of 2 polygons
|
||
|
for( int i = 0; i != 2; ++i )
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pPointLink = &pMergeLines[0]->PointLineLinks[i];
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pNextLink = pPointLink->pNext;
|
||
|
|
||
|
if( (pNextLink->pNext == pPointLink) && //only 2 lines connected to this point
|
||
|
(pPointLink->pLine->pPolygons[1 - pPointLink->iReferenceIndex] == pNextLink->pLine->pPolygons[pNextLink->iReferenceIndex]) &&
|
||
|
(pPointLink->pLine->pPolygons[pPointLink->iReferenceIndex] == pNextLink->pLine->pPolygons[1 - pNextLink->iReferenceIndex]) ) //and they're bounded by the same 2 polygons
|
||
|
{
|
||
|
Assert( pPointLink->pLine->pPolygons[pPointLink->iReferenceIndex] == pNextLink->pLine->pPolygons[1 - pNextLink->iReferenceIndex] ); //one rotation around a point should yield this as a shared polygon in all cases
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_Point *pRedundantPoint = pMergeLines[0]->pPoints[i];
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pSurvivingLine = pRedundantPoint->pConnectedLines;
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pRedundantLine = pSurvivingLine->pNext;
|
||
|
Assert( pSurvivingLine->pNext == pRedundantLine );
|
||
|
Assert( pRedundantLine->pNext == pSurvivingLine );
|
||
|
|
||
|
//link over the redundant point in the surviving line
|
||
|
pSurvivingLine->pLine->pPoints[1 - pSurvivingLine->iReferenceIndex] = pRedundantLine->pLine->pPoints[pRedundantLine->iReferenceIndex];
|
||
|
|
||
|
//link over the redunant line from the side opposite the redundant point
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pRedunantPointLink = &pRedundantLine->pLine->PointLineLinks[pRedundantLine->iReferenceIndex];
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pSwapLink = &pSurvivingLine->pLine->PointLineLinks[1 - pSurvivingLine->iReferenceIndex];
|
||
|
pSwapLink->pNext = pRedunantPointLink->pNext;
|
||
|
pSwapLink->pPrev = pRedunantPointLink->pPrev;
|
||
|
pSwapLink->pNext->pPrev = pSwapLink;
|
||
|
pSwapLink->pPrev->pNext = pSwapLink;
|
||
|
pRedundantLine->pLine->pPoints[pRedundantLine->iReferenceIndex]->pConnectedLines = pSwapLink;
|
||
|
}
|
||
|
|
||
|
//link past the redundant line in both polygons
|
||
|
for( int j = 0; j != 2; ++j )
|
||
|
{
|
||
|
if( pRedundantLine->pLine->pPolygons[i] == NULL )
|
||
|
{
|
||
|
bDeletedALineBorderingNewPolygon = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pLinkOver = &pRedundantLine->pLine->PolygonLineLinks[j];
|
||
|
|
||
|
pLinkOver->pNext->pPrev = pLinkOver->pPrev;
|
||
|
pLinkOver->pPrev->pNext = pLinkOver->pNext;
|
||
|
|
||
|
if( pRedundantLine->pLine->pPolygons[j]->pLines == pLinkOver )
|
||
|
pRedundantLine->pLine->pPolygons[j]->pLines = pLinkOver->pNext;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//kill the redundant line
|
||
|
DestructLine( pRedundantLine->pLine, destructors );
|
||
|
|
||
|
//kill the redundant point
|
||
|
DestructPoint( pRedundantPoint, destructors );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return bDeletedALineBorderingNewPolygon;
|
||
|
}
|
||
|
|
||
|
|
||
|
static inline GeneratePolyhedronFromPlanes_Point *AllocatePoint( GeneratePolyhedronFromPlanes_UnorderedPointLL * &pAllPoints, CStackItemDispenser<GeneratePolyhedronFromPlanes_UnorderedPointLL> &pointAllocator, int &iPointCount )
|
||
|
{
|
||
|
pAllPoints->pPrev = pointAllocator.Allocate();
|
||
|
DBG_ONLY( pAllPoints->pPrev->point.debugdata.Reset() );
|
||
|
pAllPoints->pPrev->pNext = pAllPoints;
|
||
|
pAllPoints = pAllPoints->pPrev;
|
||
|
pAllPoints->pPrev = NULL;
|
||
|
DBG_ONLY( pAllPoints->point.debugdata.bIsNew = true; );
|
||
|
|
||
|
Assert( (((size_t)&pAllPoints->point.ptPosition) & 15) == 0 );
|
||
|
++iPointCount;
|
||
|
|
||
|
return &pAllPoints->point;
|
||
|
}
|
||
|
|
||
|
|
||
|
static inline GeneratePolyhedronFromPlanes_Line *AllocateLine( GeneratePolyhedronFromPlanes_UnorderedLineLL * &pAllLines, CStackItemDispenser<GeneratePolyhedronFromPlanes_UnorderedLineLL> &lineAllocator )
|
||
|
{
|
||
|
//before we forget, add this line to the active list
|
||
|
pAllLines->pPrev = lineAllocator.Allocate();
|
||
|
DBG_ONLY( pAllLines->pPrev->line.debugdata.Reset(); );
|
||
|
pAllLines->pPrev->pNext = pAllLines;
|
||
|
pAllLines = pAllLines->pPrev;
|
||
|
pAllLines->pPrev = NULL;
|
||
|
|
||
|
pAllLines->line.InitLineLinks();
|
||
|
pAllLines->line.planarity = LINE_ONPLANE;
|
||
|
pAllLines->line.bNewLengthThisPass = true;
|
||
|
|
||
|
DBG_ONLY( pAllLines->line.debugdata.bIsNew = true; );
|
||
|
return &pAllLines->line;
|
||
|
}
|
||
|
|
||
|
static inline GeneratePolyhedronFromPlanes_Polygon *AllocatePolygon( GeneratePolyhedronFromPlanes_UnorderedPolygonLL * &pAllPolygons, CStackItemDispenser<GeneratePolyhedronFromPlanes_UnorderedPolygonLL> &polygonAllocator, const Vector &vSurfaceNormal/*, float fPlaneDist*/ )
|
||
|
{
|
||
|
pAllPolygons->pPrev = polygonAllocator.Allocate();
|
||
|
DBG_ONLY( pAllPolygons->pPrev->polygon.debugdata.Reset() );
|
||
|
pAllPolygons->pPrev->pNext = pAllPolygons;
|
||
|
pAllPolygons = pAllPolygons->pPrev;
|
||
|
pAllPolygons->pPrev = NULL;
|
||
|
|
||
|
pAllPolygons->polygon.bDead = false; //technically missing all it's sides, but we're fixing it now
|
||
|
pAllPolygons->polygon.bHasNewPoints = true;
|
||
|
pAllPolygons->polygon.bMovedExistingPoints = false;
|
||
|
pAllPolygons->polygon.vSurfaceNormal = vSurfaceNormal;
|
||
|
//pAllPolygons->polygon.fNormalDist = fPlaneDist;
|
||
|
|
||
|
DBG_ONLY( pAllPolygons->polygon.debugdata.bIsNew = true; );
|
||
|
return &pAllPolygons->polygon;
|
||
|
}
|
||
|
|
||
|
#if defined( DBGFLAG_ASSERT )
|
||
|
int g_iDebugPolyhedronClipProcess = -1;
|
||
|
#endif
|
||
|
|
||
|
struct MarkPlanarityControlStruct_t
|
||
|
{
|
||
|
MarkPlanarityControlStruct_t( GeneratePolyhedronFromPlanes_Polygon *pPolygon,
|
||
|
CStackItemDispenser<GeneratePolyhedronFromPlanes_UnorderedLineLL> &lineAlloc, GeneratePolyhedronFromPlanes_UnorderedLineLL *&pLines,
|
||
|
CStackItemDispenser<GeneratePolyhedronFromPlanes_UnorderedPointLL> &pointAlloc, GeneratePolyhedronFromPlanes_UnorderedPointLL *&pPoints, int &iPoints, float fPlaneEpsilon )
|
||
|
: pNewPolygon( pPolygon ),
|
||
|
lineAllocator( lineAlloc ), pAllLines( pLines ),
|
||
|
pointAllocator( pointAlloc ), pAllPoints( pPoints ), iPointCount( iPoints ),
|
||
|
fOnPlaneEpsilon( fPlaneEpsilon ), fNegativeOnPlaneEpsilon( -fPlaneEpsilon )
|
||
|
{
|
||
|
bAllPointsDead = true;
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_Line *pStartLine = AllocateLine( pAllLines, lineAllocator );
|
||
|
{
|
||
|
//A bit of setup on the dummy line, links to nothing
|
||
|
pStartLine->pPolygons[0] = NULL;
|
||
|
pStartLine->PolygonLineLinks[0].pNext = &pStartLine->PolygonLineLinks[0];
|
||
|
pStartLine->PolygonLineLinks[0].pPrev = &pStartLine->PolygonLineLinks[0];
|
||
|
|
||
|
pStartLine->pPolygons[1] = pNewPolygon;
|
||
|
pStartLine->PolygonLineLinks[1].pNext = &pStartLine->PolygonLineLinks[1];
|
||
|
pStartLine->PolygonLineLinks[1].pPrev = &pStartLine->PolygonLineLinks[1];
|
||
|
|
||
|
pStartLine->pPoints[0] = NULL;
|
||
|
pStartLine->PointLineLinks[0].pNext = &pStartLine->PointLineLinks[0];
|
||
|
pStartLine->PointLineLinks[0].pPrev = &pStartLine->PointLineLinks[0];
|
||
|
|
||
|
pStartLine->pPoints[1] = NULL;
|
||
|
pStartLine->PointLineLinks[1].pNext = &pStartLine->PointLineLinks[1];
|
||
|
pStartLine->PointLineLinks[1].pPrev = &pStartLine->PointLineLinks[1];
|
||
|
|
||
|
pStartLine->planarity = LINE_ONPLANE;
|
||
|
}
|
||
|
|
||
|
pActivePolyLine = &pStartLine->PolygonLineLinks[1];
|
||
|
|
||
|
pPolygon->pLines = pActivePolyLine;
|
||
|
}
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pActivePolyLine;
|
||
|
GeneratePolyhedronFromPlanes_Polygon *pNewPolygon;
|
||
|
|
||
|
CStackItemDispenser<GeneratePolyhedronFromPlanes_UnorderedLineLL> &lineAllocator;
|
||
|
GeneratePolyhedronFromPlanes_UnorderedLineLL *&pAllLines;
|
||
|
|
||
|
CStackItemDispenser<GeneratePolyhedronFromPlanes_UnorderedPointLL> &pointAllocator;
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPointLL *&pAllPoints;
|
||
|
int &iPointCount;
|
||
|
|
||
|
float fOnPlaneEpsilon;
|
||
|
float fNegativeOnPlaneEpsilon;
|
||
|
bool bAllPointsDead;
|
||
|
|
||
|
#if defined( DBGFLAG_ASSERT )
|
||
|
fltx4 vCutPlane;
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_Line *MarkPlanarity_CreateNewPolyLine( MarkPlanarityControlStruct_t &control )
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_Line *pNewLine = AllocateLine( control.pAllLines, control.lineAllocator );
|
||
|
//make sure we can link into it
|
||
|
{
|
||
|
pNewLine->pPoints[0] = NULL;
|
||
|
pNewLine->PointLineLinks[0].pPrev = NULL;
|
||
|
pNewLine->PointLineLinks[0].pNext = NULL;
|
||
|
|
||
|
pNewLine->pPoints[1] = NULL;
|
||
|
pNewLine->PointLineLinks[1].pPrev = NULL;
|
||
|
pNewLine->PointLineLinks[1].pNext = NULL;
|
||
|
|
||
|
pNewLine->pPolygons[0] = NULL;
|
||
|
pNewLine->PolygonLineLinks[0].pNext = NULL;
|
||
|
pNewLine->PolygonLineLinks[0].pPrev = NULL;
|
||
|
|
||
|
pNewLine->pPolygons[1] = control.pNewPolygon;
|
||
|
pNewLine->PolygonLineLinks[1].pNext = control.pActivePolyLine->pNext;
|
||
|
pNewLine->PolygonLineLinks[1].pPrev = control.pActivePolyLine;
|
||
|
|
||
|
control.pActivePolyLine->pNext->pPrev = &pNewLine->PolygonLineLinks[1];
|
||
|
control.pActivePolyLine->pNext = &pNewLine->PolygonLineLinks[1];
|
||
|
control.pActivePolyLine = &pNewLine->PolygonLineLinks[1];
|
||
|
|
||
|
control.pActivePolyLine->pLine->planarity = LINE_ONPLANE;
|
||
|
}
|
||
|
return pNewLine;
|
||
|
}
|
||
|
|
||
|
//design the following algorithms to never crawl past the cutting plane. That way we can get consistent results
|
||
|
void Recursive_MarkPlanarity_OnPlane( GeneratePolyhedronFromPlanes_LineLL *pLineWalk, MarkPlanarityControlStruct_t &control );
|
||
|
|
||
|
void Recursive_MarkPlanarity_Dead( GeneratePolyhedronFromPlanes_LineLL *pLineWalk, MarkPlanarityControlStruct_t &control )
|
||
|
{
|
||
|
Assert( pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->planarity == POINT_DEAD );
|
||
|
//Assert( !pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->debugdata.bVisited || (pLineWalk->pLine->planarity == LINE_DEAD) );
|
||
|
DBG_ONLY( pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->debugdata.bVisited = true; );
|
||
|
|
||
|
while( pLineWalk->pLine->planarity == LINE_ALIVE ) //any line connected to a dead point can't be alive, therefore has not been traversed since we only travel through dead points
|
||
|
{
|
||
|
Assert( !pLineWalk->pLine->debugdata.bTested && !pLineWalk->pLine->debugdata.bIsNew );
|
||
|
DBG_ONLY( pLineWalk->pLine->debugdata.bTested = true; );
|
||
|
GeneratePolyhedronFromPlanes_Point *pConnectedPoint = pLineWalk->pLine->pPoints[pLineWalk->iReferenceIndex];
|
||
|
if( pConnectedPoint->fPlaneDist > control.fOnPlaneEpsilon )
|
||
|
{
|
||
|
//point dead, line is also dead
|
||
|
pConnectedPoint->planarity = POINT_DEAD;
|
||
|
pLineWalk->pLine->planarity = LINE_DEAD;
|
||
|
|
||
|
//branch into the point as a new root
|
||
|
Recursive_MarkPlanarity_Dead( pLineWalk->pLine->PointLineLinks[pLineWalk->iReferenceIndex].pNext, control );
|
||
|
}
|
||
|
else if( pConnectedPoint->fPlaneDist >= control.fNegativeOnPlaneEpsilon )
|
||
|
{
|
||
|
//point onplane, line is dead
|
||
|
pConnectedPoint->planarity = POINT_ONPLANE;
|
||
|
pLineWalk->pLine->planarity = LINE_DEAD;
|
||
|
|
||
|
control.pNewPolygon->bMovedExistingPoints = true; //mark the polygon as using off-plane points
|
||
|
|
||
|
Recursive_MarkPlanarity_OnPlane( pLineWalk->pLine->PointLineLinks[pLineWalk->iReferenceIndex].pNext, control );
|
||
|
|
||
|
control.pActivePolyLine->pLine->pPoints[0] = pConnectedPoint;
|
||
|
control.pActivePolyLine->pLine->PointLineLinks[0].pNext = &pLineWalk->pLine->PointLineLinks[pLineWalk->iReferenceIndex];
|
||
|
control.pActivePolyLine->pLine->PointLineLinks[0].pPrev = pLineWalk->pLine->PointLineLinks[pLineWalk->iReferenceIndex].pPrev;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//point alive, line is cut
|
||
|
Assert( pConnectedPoint->planarity == POINT_ALIVE );
|
||
|
pLineWalk->pLine->planarity = LINE_CUT;
|
||
|
control.bAllPointsDead = false;
|
||
|
|
||
|
|
||
|
int iDeadIndex = 1 - pLineWalk->iReferenceIndex;
|
||
|
int iLivingIndex = pLineWalk->iReferenceIndex;
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_Point *pDeadPoint = pLineWalk->pLine->pPoints[iDeadIndex];
|
||
|
GeneratePolyhedronFromPlanes_Point *pLivingPoint = pLineWalk->pLine->pPoints[iLivingIndex];
|
||
|
|
||
|
//Generate a new point
|
||
|
GeneratePolyhedronFromPlanes_Point *pNewPoint = AllocatePoint( control.pAllPoints, control.pointAllocator, control.iPointCount );
|
||
|
{
|
||
|
Assert( (pDeadPoint->fPlaneDist - pLivingPoint->fPlaneDist) > control.fOnPlaneEpsilon );
|
||
|
float fInvTotalDist = 1.0f/(pDeadPoint->fPlaneDist - pLivingPoint->fPlaneDist); //subtraction because the living index is known to be negative
|
||
|
pNewPoint->ptPosition = SubSIMD( MulSIMD(pLivingPoint->ptPosition, ReplicateX4(pDeadPoint->fPlaneDist * fInvTotalDist)),
|
||
|
MulSIMD(pDeadPoint->ptPosition, ReplicateX4(pLivingPoint->fPlaneDist * fInvTotalDist)) );
|
||
|
SubFloat( pNewPoint->ptPosition, 3 ) = -1.0f;
|
||
|
|
||
|
#if defined( DBGFLAG_ASSERT ) //check length of line that will remain in the polyhedron, and doublecheck planar distance of new point
|
||
|
{
|
||
|
fltx4 vAliveLineDiff = SubSIMD( pNewPoint->ptPosition, pLivingPoint->ptPosition );
|
||
|
Vector vecAliveLineDiff( SubFloat( vAliveLineDiff, 0 ), SubFloat( vAliveLineDiff, 1 ), SubFloat( vAliveLineDiff, 2 ) );
|
||
|
float fLineLength = vecAliveLineDiff.Length();
|
||
|
AssertMsg( fLineLength > control.fOnPlaneEpsilon, "Dangerously short line" );
|
||
|
|
||
|
fltx4 vDist = MulSIMD( control.vCutPlane, pNewPoint->ptPosition );
|
||
|
float fDebugDist;
|
||
|
fDebugDist = SubFloat( vDist, 0 ) + SubFloat( vDist, 1 ) + SubFloat( vDist, 2 ) + SubFloat( vDist, 3 ); //just for looking at in watch windows
|
||
|
//Assert( fabs( fDebugDist ) <= control.fOnPlaneEpsilon );
|
||
|
}
|
||
|
#endif
|
||
|
pNewPoint->planarity = POINT_ONPLANE;
|
||
|
pNewPoint->fPlaneDist = 0.0f;
|
||
|
}
|
||
|
|
||
|
pLineWalk->pLine->pPolygons[0]->bHasNewPoints = true;
|
||
|
pLineWalk->pLine->pPolygons[1]->bHasNewPoints = true;
|
||
|
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_Line *pCompletedPolyLine = control.pActivePolyLine->pLine;
|
||
|
GeneratePolyhedronFromPlanes_Line *pNewPolyLine = MarkPlanarity_CreateNewPolyLine( control );
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_Line *pCutLine = pLineWalk->pLine;
|
||
|
GeneratePolyhedronFromPlanes_Line *pNewLivingLine = AllocateLine( control.pAllLines, control.lineAllocator );
|
||
|
|
||
|
//going to relink the cut line to be between the dead point and the new point
|
||
|
//also going to create a new line between new point and live point
|
||
|
//The new line will copy the cut line's iReferenceIndex orientation
|
||
|
{
|
||
|
//relink point pointers
|
||
|
pNewLivingLine->pPoints[iDeadIndex] = pNewPoint;
|
||
|
pNewLivingLine->pPoints[iLivingIndex] = pCutLine->pPoints[iLivingIndex];
|
||
|
pCutLine->pPoints[iLivingIndex] = pNewPoint;
|
||
|
pNewPoint->pConnectedLines = &pNewLivingLine->PointLineLinks[iDeadIndex];
|
||
|
pCutLine->planarity = LINE_DEAD;
|
||
|
pNewLivingLine->planarity = LINE_ALIVE;
|
||
|
|
||
|
|
||
|
//new line steals cut line's living index linkages
|
||
|
pNewLivingLine->PointLineLinks[iLivingIndex].pNext = pCutLine->PointLineLinks[iLivingIndex].pNext;
|
||
|
pNewLivingLine->PointLineLinks[iLivingIndex].pPrev = pCutLine->PointLineLinks[iLivingIndex].pPrev;
|
||
|
pNewLivingLine->PointLineLinks[iLivingIndex].pNext->pPrev = &pNewLivingLine->PointLineLinks[iLivingIndex];
|
||
|
pNewLivingLine->PointLineLinks[iLivingIndex].pPrev->pNext = &pNewLivingLine->PointLineLinks[iLivingIndex];
|
||
|
pNewLivingLine->pPoints[iLivingIndex]->pConnectedLines = &pNewLivingLine->PointLineLinks[iLivingIndex];
|
||
|
|
||
|
//crosslink cut living to new dead
|
||
|
pCutLine->PointLineLinks[iLivingIndex].pNext = pCutLine->PointLineLinks[iLivingIndex].pPrev = &pNewLivingLine->PointLineLinks[iDeadIndex];
|
||
|
pNewLivingLine->PointLineLinks[iDeadIndex].pNext = pNewLivingLine->PointLineLinks[iDeadIndex].pPrev = &pCutLine->PointLineLinks[iLivingIndex];
|
||
|
|
||
|
//fix up polygon linkages
|
||
|
pNewLivingLine->pPolygons[0] = pCutLine->pPolygons[0];
|
||
|
pNewLivingLine->pPolygons[1] = pCutLine->pPolygons[1];
|
||
|
|
||
|
//insert after cut line for 0 polygon
|
||
|
pNewLivingLine->PolygonLineLinks[0].pNext = pCutLine->PolygonLineLinks[0].pNext;
|
||
|
pNewLivingLine->PolygonLineLinks[0].pPrev = &pCutLine->PolygonLineLinks[0];
|
||
|
pCutLine->PolygonLineLinks[0].pNext = &pNewLivingLine->PolygonLineLinks[0];
|
||
|
pNewLivingLine->PolygonLineLinks[0].pNext->pPrev = &pNewLivingLine->PolygonLineLinks[0];
|
||
|
|
||
|
//insert before cut line for 1 polygon
|
||
|
pNewLivingLine->PolygonLineLinks[1].pNext = &pCutLine->PolygonLineLinks[1];
|
||
|
pNewLivingLine->PolygonLineLinks[1].pPrev = pCutLine->PolygonLineLinks[1].pPrev;
|
||
|
pCutLine->PolygonLineLinks[1].pPrev = &pNewLivingLine->PolygonLineLinks[1];
|
||
|
pNewLivingLine->PolygonLineLinks[1].pPrev->pNext = &pNewLivingLine->PolygonLineLinks[1];
|
||
|
}
|
||
|
|
||
|
|
||
|
//We now should have everything we need to finish constructing pCompletedPolyLine
|
||
|
{
|
||
|
//link polygon completed line to new point
|
||
|
pCompletedPolyLine->pPoints[1] = pNewPoint;
|
||
|
|
||
|
//Make a T junction between cut line, new line, and completed polygon line
|
||
|
pCompletedPolyLine->PointLineLinks[1].pPrev = &pCutLine->PointLineLinks[iLivingIndex];
|
||
|
pCutLine->PointLineLinks[iLivingIndex].pNext = &pCompletedPolyLine->PointLineLinks[1];
|
||
|
|
||
|
pCompletedPolyLine->PointLineLinks[1].pNext = &pNewLivingLine->PointLineLinks[iDeadIndex];
|
||
|
pNewLivingLine->PointLineLinks[iDeadIndex].pPrev = &pCompletedPolyLine->PointLineLinks[1];
|
||
|
|
||
|
|
||
|
//pCompletedLine->pLine->pPoints[0] should already have valid values that just need to be linked back in
|
||
|
pCompletedPolyLine->PointLineLinks[0].pNext->pPrev = &pCompletedPolyLine->PointLineLinks[0];
|
||
|
pCompletedPolyLine->PointLineLinks[0].pPrev->pNext = &pCompletedPolyLine->PointLineLinks[0];
|
||
|
|
||
|
//link outwardly into the patched up polygon
|
||
|
pCompletedPolyLine->pPolygons[0] = pNewLivingLine->pPolygons[iDeadIndex]; //left side of line going to living point is the polygon that will survive the planar clip
|
||
|
pCompletedPolyLine->PolygonLineLinks[0].pPrev = &pNewLivingLine->PolygonLineLinks[iDeadIndex];
|
||
|
pCompletedPolyLine->PolygonLineLinks[0].pNext = pNewLivingLine->PolygonLineLinks[iDeadIndex].pNext;
|
||
|
pCompletedPolyLine->PolygonLineLinks[0].pNext->pPrev = &pCompletedPolyLine->PolygonLineLinks[0];
|
||
|
pCompletedPolyLine->PolygonLineLinks[0].pPrev->pNext = &pCompletedPolyLine->PolygonLineLinks[0];
|
||
|
|
||
|
#if defined( DBGFLAG_ASSERT )
|
||
|
if( pCompletedPolyLine->pPoints[0] && pCompletedPolyLine->pPoints[1] )
|
||
|
{
|
||
|
fltx4 vLineTemp = SubSIMD( pCompletedPolyLine->pPoints[1]->ptPosition, pCompletedPolyLine->pPoints[0]->ptPosition );
|
||
|
AssertMsg( (SubFloat( vLineTemp, 0 ) != 0.0f) || (SubFloat( vLineTemp, 1 ) != 0.0f) || (SubFloat( vLineTemp, 2 ) != 0.0f), "Created zero length line" );
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
//keep updating the drag line
|
||
|
pNewPolyLine->pPoints[0] = pNewPoint;
|
||
|
pNewPolyLine->PointLineLinks[0].pNext = &pCutLine->PointLineLinks[iLivingIndex];
|
||
|
pNewPolyLine->PointLineLinks[0].pPrev = &pNewLivingLine->PointLineLinks[iDeadIndex];
|
||
|
}
|
||
|
|
||
|
pLineWalk = pLineWalk->pNext;
|
||
|
}
|
||
|
|
||
|
/*if( pLineWalk->pLine->planarity == LINE_DEAD )
|
||
|
{
|
||
|
//left polygon is dead
|
||
|
#if defined( DBGFLAG_ASSERT ) //make sure
|
||
|
{
|
||
|
//walk the polygon and ensure it should be dead
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pDebugLineWalkHead = &pLineWalk->pLine->PolygonLineLinks[1 - pLineWalk->iReferenceIndex];
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pDebugLineWalk = pDebugLineWalkHead;
|
||
|
do
|
||
|
{
|
||
|
Assert( pDebugLineWalk->pLine->planarity != LINE_ALIVE );
|
||
|
pDebugLineWalk = pDebugLineWalk->pNext;
|
||
|
} while (pDebugLineWalk != pDebugLineWalkHead);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
Assert( !pLineWalk->pLine->pPolygons[1 - pLineWalk->iReferenceIndex]->bDead ); //not already marked dead
|
||
|
pLineWalk->pLine->pPolygons[1 - pLineWalk->iReferenceIndex]->bDead = true;
|
||
|
}*/
|
||
|
}
|
||
|
|
||
|
bool Recursive_CanOnPlanePolyCrawlDead( GeneratePolyhedronFromPlanes_LineLL *pLineWalk, MarkPlanarityControlStruct_t &control );
|
||
|
|
||
|
//Once we start traversing on-plane points, our options reduce. We do this to ensure we never traverse a section that isn't touching the cut plane
|
||
|
void Recursive_MarkPlanarity_OnPlane( GeneratePolyhedronFromPlanes_LineLL *pLineWalk, MarkPlanarityControlStruct_t &control )
|
||
|
{
|
||
|
Assert( pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->planarity == POINT_ONPLANE );
|
||
|
//Assert( !pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->debugdata.bVisited );
|
||
|
DBG_ONLY( pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->debugdata.bVisited = true; );
|
||
|
|
||
|
//onplane's are only allowed to crawl within their polygon. So if the very next line is onplane, recurse. Alive has no work to do, and dead will be picked up later in the dead-crawling algorithm
|
||
|
if( pLineWalk->pLine->planarity == LINE_ALIVE ) //not already traversed
|
||
|
{
|
||
|
Assert( !pLineWalk->pLine->debugdata.bTested && !pLineWalk->pLine->debugdata.bIsNew );
|
||
|
//DBG_ONLY( pLineWalk->pLine->debugdata.bTested = true; );
|
||
|
GeneratePolyhedronFromPlanes_Point *pConnectedPoint = pLineWalk->pLine->pPoints[pLineWalk->iReferenceIndex];
|
||
|
if( (pConnectedPoint->fPlaneDist > control.fOnPlaneEpsilon) && !Recursive_CanOnPlanePolyCrawlDead( pLineWalk, control ) )
|
||
|
{
|
||
|
//do nothing, for consistency we want to be sure we traverse from the dead point to here
|
||
|
}
|
||
|
else if( pConnectedPoint->fPlaneDist >= control.fNegativeOnPlaneEpsilon )
|
||
|
{
|
||
|
//point onplane, line is onplane, line is part of the new polygon
|
||
|
pConnectedPoint->planarity = POINT_ONPLANE;
|
||
|
pLineWalk->pLine->planarity = LINE_ONPLANE;
|
||
|
|
||
|
Recursive_MarkPlanarity_OnPlane( pLineWalk->pLine->PointLineLinks[pLineWalk->iReferenceIndex].pNext, control );
|
||
|
|
||
|
//Stitch the line we just traversed into the polygon as-is
|
||
|
{
|
||
|
//remove from existing dead polygon
|
||
|
int iDeadSide = 1 - pLineWalk->iReferenceIndex;
|
||
|
pLineWalk->pLine->PolygonLineLinks[iDeadSide].pNext->pPrev = pLineWalk->pLine->PolygonLineLinks[iDeadSide].pPrev;
|
||
|
pLineWalk->pLine->PolygonLineLinks[iDeadSide].pPrev->pNext = pLineWalk->pLine->PolygonLineLinks[iDeadSide].pNext;
|
||
|
|
||
|
/*if( pLineWalk->pLine->PolygonLineLinks[iDeadSide].pNext == &pLineWalk->pLine->PolygonLineLinks[iDeadSide] )
|
||
|
{
|
||
|
//this was the last line in the polygon
|
||
|
pLineWalk->pLine->pPolygons[iDeadSide]->pLines = NULL;
|
||
|
pLineWalk->pLine->pPolygons[iDeadSide]->bDead = true;
|
||
|
}
|
||
|
else*/
|
||
|
{
|
||
|
pLineWalk->pLine->pPolygons[iDeadSide]->pLines = pLineWalk->pLine->PolygonLineLinks[iDeadSide].pNext;
|
||
|
}
|
||
|
|
||
|
//now replace it with the new polygon
|
||
|
pLineWalk->pLine->pPolygons[iDeadSide] = control.pNewPolygon;
|
||
|
|
||
|
//insert before pActivePolyLine
|
||
|
pLineWalk->pLine->PolygonLineLinks[iDeadSide].pPrev = control.pActivePolyLine->pPrev;
|
||
|
control.pActivePolyLine->pPrev->pNext = &pLineWalk->pLine->PolygonLineLinks[iDeadSide];
|
||
|
|
||
|
pLineWalk->pLine->PolygonLineLinks[iDeadSide].pNext = control.pActivePolyLine;
|
||
|
control.pActivePolyLine->pPrev = &pLineWalk->pLine->PolygonLineLinks[iDeadSide];
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//point alive, line is alive, root point stitched into the new polygon
|
||
|
GeneratePolyhedronFromPlanes_Line *pCompletedPolyLine = control.pActivePolyLine->pLine;
|
||
|
|
||
|
//We now should have everything we need to finish constructing pCompletedLine
|
||
|
{
|
||
|
//pCompletedLine->pLine->pPoints[0] should already have valid values that just need to be linked back in
|
||
|
pCompletedPolyLine->PointLineLinks[0].pNext->pPrev = &pCompletedPolyLine->PointLineLinks[0];
|
||
|
pCompletedPolyLine->PointLineLinks[0].pPrev->pNext = &pCompletedPolyLine->PointLineLinks[0];
|
||
|
|
||
|
pCompletedPolyLine->pPoints[1] = pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]; //root point is endpoint for completed polygon line
|
||
|
pCompletedPolyLine->PointLineLinks[1].pPrev = pLineWalk->pPrev;
|
||
|
pCompletedPolyLine->PointLineLinks[1].pNext = pLineWalk;
|
||
|
pCompletedPolyLine->PointLineLinks[1].pNext->pPrev = &pCompletedPolyLine->PointLineLinks[1];
|
||
|
pCompletedPolyLine->PointLineLinks[1].pPrev->pNext = &pCompletedPolyLine->PointLineLinks[1];
|
||
|
|
||
|
//link outwardly into the patched up polygon
|
||
|
pCompletedPolyLine->pPolygons[0] = pLineWalk->pLine->pPolygons[1 - pLineWalk->iReferenceIndex]; //left side of line going to live point is the polygon that will survive the planar clip
|
||
|
pCompletedPolyLine->PolygonLineLinks[0].pPrev = &pLineWalk->pLine->PolygonLineLinks[1 - pLineWalk->iReferenceIndex];
|
||
|
pCompletedPolyLine->PolygonLineLinks[0].pNext = pCompletedPolyLine->PolygonLineLinks[0].pPrev->pNext;
|
||
|
pCompletedPolyLine->PolygonLineLinks[0].pNext->pPrev = &pCompletedPolyLine->PolygonLineLinks[0];
|
||
|
pCompletedPolyLine->PolygonLineLinks[0].pPrev->pNext = &pCompletedPolyLine->PolygonLineLinks[0];
|
||
|
}
|
||
|
|
||
|
#if defined( DBGFLAG_ASSERT )
|
||
|
if( pCompletedPolyLine->pPoints[0] && pCompletedPolyLine->pPoints[1] )
|
||
|
{
|
||
|
fltx4 vLineTemp = SubSIMD( pCompletedPolyLine->pPoints[1]->ptPosition, pCompletedPolyLine->pPoints[0]->ptPosition );
|
||
|
AssertMsg( (SubFloat( vLineTemp, 0 ) != 0.0f) || (SubFloat( vLineTemp, 1 ) != 0.0f) || (SubFloat( vLineTemp, 2 ) != 0.0f), "Created zero length line" );
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//point 0 will be filled in as we backtrack to a dead point that recursed into Recursive_MarkPlanarity_OnPlane(). If we recursed from another onplane point, we'll be recycling its line
|
||
|
MarkPlanarity_CreateNewPolyLine( control );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*if( pLineWalk->pLine->planarity == LINE_DEAD )
|
||
|
{
|
||
|
//left polygon is dead
|
||
|
#if defined( DBGFLAG_ASSERT ) //make sure
|
||
|
{
|
||
|
//walk the polygon and ensure it should be dead
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pDebugLineWalkHead = &pLineWalk->pLine->PolygonLineLinks[1 - pLineWalk->iReferenceIndex];
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pDebugLineWalk = pDebugLineWalkHead;
|
||
|
do
|
||
|
{
|
||
|
Assert( pDebugLineWalk->pLine->planarity != LINE_ALIVE );
|
||
|
pDebugLineWalk = pDebugLineWalk->pNext;
|
||
|
} while (pDebugLineWalk != pDebugLineWalkHead);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
Assert( !pLineWalk->pLine->pPolygons[1 - pLineWalk->iReferenceIndex]->bDead ); //not already marked dead
|
||
|
pLineWalk->pLine->pPolygons[1 - pLineWalk->iReferenceIndex]->bDead = true;
|
||
|
}*/
|
||
|
}
|
||
|
|
||
|
|
||
|
bool Recursive_CanOnPlanePolyCrawlDead( GeneratePolyhedronFromPlanes_LineLL *pLineWalk, MarkPlanarityControlStruct_t &control )
|
||
|
{
|
||
|
pLineWalk = pLineWalk->pLine->PointLineLinks[pLineWalk->iReferenceIndex].pNext;
|
||
|
if( pLineWalk->pLine->planarity == LINE_ALIVE ) //not already traversed
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_Point *pConnectedPoint = pLineWalk->pLine->pPoints[pLineWalk->iReferenceIndex];
|
||
|
if( pConnectedPoint->fPlaneDist > control.fOnPlaneEpsilon )
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pTestAlive = pLineWalk->pNext;
|
||
|
if( pTestAlive->pLine->pPoints[pTestAlive->iReferenceIndex]->fPlaneDist < control.fNegativeOnPlaneEpsilon )
|
||
|
{
|
||
|
//couldn't have possibly crawled here from the other direction if we continue the onplane streak
|
||
|
return Recursive_CanOnPlanePolyCrawlDead( pLineWalk, control );
|
||
|
}
|
||
|
}
|
||
|
if( pConnectedPoint->fPlaneDist >= control.fNegativeOnPlaneEpsilon )
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pTestAlive = pLineWalk->pNext;
|
||
|
if( pTestAlive->pLine->pPoints[pTestAlive->iReferenceIndex]->fPlaneDist < control.fNegativeOnPlaneEpsilon )
|
||
|
{
|
||
|
//couldn't have possibly crawled here from the other direction
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void RecomputePolygonSurfaceNormal( GeneratePolyhedronFromPlanes_Polygon *pPolygon )
|
||
|
{
|
||
|
Vector vAggregateNormal = vec3_origin;
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pLineWalkHead = pPolygon->pLines;
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pLineWalk = pLineWalkHead->pPrev;
|
||
|
Vector vLastLine;
|
||
|
|
||
|
fltx4 vLineTemp = SubSIMD( pLineWalk->pLine->pPoints[pLineWalk->iReferenceIndex]->ptPosition, pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->ptPosition );
|
||
|
pLineWalk = pLineWalk->pNext;
|
||
|
vLastLine.Init( SubFloat( vLineTemp, 0 ), SubFloat( vLineTemp, 1 ), SubFloat( vLineTemp, 2 ) );
|
||
|
do
|
||
|
{
|
||
|
vLineTemp = SubSIMD( pLineWalk->pLine->pPoints[pLineWalk->iReferenceIndex]->ptPosition, pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->ptPosition );
|
||
|
Vector vThisLine( SubFloat( vLineTemp, 0 ), SubFloat( vLineTemp, 1 ), SubFloat( vLineTemp, 2 ) );
|
||
|
Vector vCross = vThisLine.Cross( vLastLine );
|
||
|
vAggregateNormal += vThisLine.Cross( vLastLine ); //intentionally not normalizing until the end. Larger lines deserve more influence in the result
|
||
|
|
||
|
vLastLine = vThisLine;
|
||
|
pLineWalk = pLineWalk->pNext;
|
||
|
} while ( pLineWalk != pLineWalkHead );
|
||
|
|
||
|
vAggregateNormal.NormalizeInPlace();
|
||
|
pPolygon->vSurfaceNormal = vAggregateNormal;
|
||
|
}
|
||
|
|
||
|
CPolyhedron *ClipLinkedGeometry( GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pAllPolygons, GeneratePolyhedronFromPlanes_UnorderedLineLL *pAllLines, GeneratePolyhedronFromPlanes_UnorderedPointLL *pAllPoints, int iPointCount, const fltx4 *pOutwardFacingPlanes, int iPlaneCount, float fOnPlaneEpsilon, bool bUseTemporaryMemory, fltx4 vShiftResultPositions )
|
||
|
{
|
||
|
//const float fNegativeOnPlaneEpsilon = -fOnPlaneEpsilon;
|
||
|
const float fOnPlaneEpsilonSquared = fOnPlaneEpsilon * fOnPlaneEpsilon;
|
||
|
|
||
|
#ifdef DBGFLAG_ASSERT
|
||
|
GeneratePolyhedronFromPlanes_Point *pStartPoint = NULL;
|
||
|
|
||
|
static int iPolyhedronClipCount = 0;
|
||
|
++iPolyhedronClipCount;
|
||
|
#endif
|
||
|
|
||
|
size_t iStackMemorySize = (64 * 1024); //start off trying to allocate 128k
|
||
|
void *pStackMemory = stackalloc( iStackMemorySize );
|
||
|
while( pStackMemory == NULL )
|
||
|
{
|
||
|
iStackMemorySize = iStackMemorySize >> 1;
|
||
|
pStackMemory = stackalloc( iStackMemorySize );
|
||
|
}
|
||
|
CStackMemoryDispenser memoryDispenser( pStackMemory, iStackMemorySize );
|
||
|
|
||
|
//Collections of dead pointers for reallocation, data in them shouldn't be touched until the current loop iteration is done.
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPointLL *pDeadPointCollection = NULL;
|
||
|
|
||
|
#ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS
|
||
|
GeneratePolyhedronFromPlanes_UnorderedLineLL *pDeadLineCollection = NULL;
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pDeadPolygonCollection = NULL;
|
||
|
#endif
|
||
|
|
||
|
CStackItemDispenser<GeneratePolyhedronFromPlanes_UnorderedPointLL> pointAllocator( memoryDispenser );
|
||
|
CStackItemDispenser<GeneratePolyhedronFromPlanes_UnorderedLineLL> lineAllocator( memoryDispenser );
|
||
|
CStackItemDispenser<GeneratePolyhedronFromPlanes_UnorderedPolygonLL> polygonAllocator( memoryDispenser );
|
||
|
|
||
|
#ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS
|
||
|
CClipLinkedGeometryDestructors destructors( iPointCount, pAllPolygons, pDeadPolygonCollection, pAllLines, pDeadLineCollection, pAllPoints, pDeadPointCollection );
|
||
|
#else
|
||
|
CClipLinkedGeometryDestructors destructors( iPointCount, pAllPolygons, polygonAllocator, pAllLines, lineAllocator, pAllPoints, pDeadPointCollection );
|
||
|
#endif
|
||
|
|
||
|
DBG_ONLY( destructors.DebugCutHistory.AddToTail( ConvertLinkedGeometryToPolyhedron( pAllPolygons, pAllLines, pAllPoints, false, vShiftResultPositions ) ) );
|
||
|
DBG_ONLY( destructors.DebugCutPlaneIndex.AddToTail( -1 ) );
|
||
|
|
||
|
for( int iCurrentPlane = 0; iCurrentPlane != iPlaneCount; ++iCurrentPlane )
|
||
|
{
|
||
|
#if defined( DBGFLAG_ASSERT )
|
||
|
destructors.bDebugTrigger = (g_iDebugPolyhedronClipProcess == iCurrentPlane);
|
||
|
if( destructors.bDebugTrigger )
|
||
|
{
|
||
|
g_iDebugPolyhedronClipProcess = -1; //remove the need for cleanup code wherever someone wanted this to break;
|
||
|
}
|
||
|
#endif
|
||
|
//clear out work variables
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pActivePolygonWalk = pAllPolygons;
|
||
|
do
|
||
|
{
|
||
|
pActivePolygonWalk->polygon.bDead = false;
|
||
|
pActivePolygonWalk->polygon.bHasNewPoints = false;
|
||
|
pActivePolygonWalk->polygon.bMovedExistingPoints = false;
|
||
|
DBG_ONLY( pActivePolygonWalk->polygon.debugdata.Reset(); );
|
||
|
|
||
|
pActivePolygonWalk = pActivePolygonWalk->pNext;
|
||
|
} while( pActivePolygonWalk );
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_UnorderedLineLL *pActiveLineWalk = pAllLines;
|
||
|
do
|
||
|
{
|
||
|
pActiveLineWalk->line.planarity = LINE_ALIVE;
|
||
|
pActiveLineWalk->line.bNewLengthThisPass = false;
|
||
|
DBG_ONLY( pActiveLineWalk->line.debugdata.Reset(); );
|
||
|
|
||
|
pActiveLineWalk = pActiveLineWalk->pNext;
|
||
|
} while( pActiveLineWalk );
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPointLL *pActivePointWalk = pAllPoints;
|
||
|
do
|
||
|
{
|
||
|
pActivePointWalk->point.planarity = POINT_ALIVE;
|
||
|
DBG_ONLY( pActivePointWalk->point.debugdata.Reset(); );
|
||
|
|
||
|
pActivePointWalk = pActivePointWalk->pNext;
|
||
|
} while( pActivePointWalk );
|
||
|
}
|
||
|
|
||
|
while( pDeadPointCollection != NULL )
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPointLL *pFree = pDeadPointCollection;
|
||
|
pDeadPointCollection = pDeadPointCollection->pNext;
|
||
|
pointAllocator.Free( pFree );
|
||
|
}
|
||
|
|
||
|
#ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS
|
||
|
while( pDeadLineCollection != NULL )
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_UnorderedLineLL *pFree = pDeadLineCollection;
|
||
|
pDeadLineCollection = pDeadLineCollection->pNext;
|
||
|
lineAllocator.Free( pFree );
|
||
|
}
|
||
|
|
||
|
while( pDeadPolygonCollection != NULL )
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pFree = pDeadPolygonCollection;
|
||
|
pDeadPolygonCollection = pDeadPolygonCollection->pNext;
|
||
|
polygonAllocator.Free( pFree );
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#if defined( DBGFLAG_ASSERT )
|
||
|
int iPatchMethod = -1;
|
||
|
#endif
|
||
|
|
||
|
|
||
|
//find point distances from the plane
|
||
|
ComputePlanarDistances( pAllPoints, iPointCount, pOutwardFacingPlanes[iCurrentPlane] );
|
||
|
|
||
|
//find "most dead" point. We'll be using that as a starting point for an algorithm that walks the edge of the new polygon
|
||
|
//this walk method lets us categorize points without getting into impossible situations where 2 diagonal corners of a quad are dead, but the other points are onplane/alive
|
||
|
GeneratePolyhedronFromPlanes_Point *pMostDeadPoint = &pAllPoints->point;
|
||
|
float fMostDeadPointDist = pAllPoints->point.fPlaneDist;
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPointLL *pActivePointWalk = pAllPoints->pNext;
|
||
|
do
|
||
|
{
|
||
|
if( pActivePointWalk->point.fPlaneDist > fMostDeadPointDist )
|
||
|
{
|
||
|
pMostDeadPoint = &pActivePointWalk->point;
|
||
|
fMostDeadPointDist = pActivePointWalk->point.fPlaneDist;
|
||
|
}
|
||
|
|
||
|
pActivePointWalk = pActivePointWalk->pNext;
|
||
|
} while( pActivePointWalk );
|
||
|
}
|
||
|
|
||
|
if( fMostDeadPointDist <= fOnPlaneEpsilon )
|
||
|
{
|
||
|
//no cuts made
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
Vector vSurfaceNormal( SubFloat( pOutwardFacingPlanes[iCurrentPlane], 0 ), SubFloat( pOutwardFacingPlanes[iCurrentPlane], 1 ), SubFloat( pOutwardFacingPlanes[iCurrentPlane], 2 ) );
|
||
|
GeneratePolyhedronFromPlanes_Polygon *pNewPolygon = AllocatePolygon( pAllPolygons, polygonAllocator, vSurfaceNormal );
|
||
|
pMostDeadPoint->planarity = POINT_DEAD;
|
||
|
{
|
||
|
MarkPlanarityControlStruct_t control( pNewPolygon, lineAllocator, pAllLines, pointAllocator, pAllPoints, iPointCount, fOnPlaneEpsilon );
|
||
|
#if defined( DBGFLAG_ASSERT )
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pStartLine = control.pActivePolyLine;
|
||
|
control.vCutPlane = pOutwardFacingPlanes[iCurrentPlane];
|
||
|
#endif
|
||
|
Recursive_MarkPlanarity_Dead( pMostDeadPoint->pConnectedLines, control );
|
||
|
|
||
|
//it's possible that the crawling algorithm didn't encounter any live points if the plane didn't cut any lines (All intersections between plane and mesh were at an existing vertex)
|
||
|
if( control.bAllPointsDead )
|
||
|
{
|
||
|
//doublecheck
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPointLL *pPointWalk = pAllPoints;
|
||
|
while( pPointWalk )
|
||
|
{
|
||
|
if( pPointWalk->point.planarity == POINT_ALIVE )
|
||
|
{
|
||
|
control.bAllPointsDead = false;
|
||
|
break;
|
||
|
}
|
||
|
pPointWalk = pPointWalk->pNext;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( control.bAllPointsDead ) //all the points either died or are on the plane, no polyhedron left at all
|
||
|
{
|
||
|
#ifdef DBGFLAG_ASSERT
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPointLL *pActivePointWalk = pAllPoints->pNext;
|
||
|
do
|
||
|
{
|
||
|
Assert( pActivePointWalk->point.planarity != POINT_ALIVE );
|
||
|
|
||
|
pActivePointWalk = pActivePointWalk->pNext;
|
||
|
} while( pActivePointWalk );
|
||
|
#endif
|
||
|
|
||
|
#if defined( ENABLE_DEBUG_POLYHEDRON_DUMPS )
|
||
|
Assert_DumpPolyhedron( g_bDumpNullPolyhedrons == false ); //if someone set it to true, we'll dump the polyhedron then halt
|
||
|
#endif
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
Assert( (control.pActivePolyLine->pNext == pStartLine) || (pStartLine->pLine->pPoints[0] != NULL) );
|
||
|
Assert( (pStartLine->pLine->pPoints[1] != NULL) || (pStartLine == control.pActivePolyLine) );
|
||
|
|
||
|
//search/mark dead polygons, there should be a way to do this in the crawl algorithm
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pPolygonWalk = pAllPolygons;
|
||
|
|
||
|
while( pPolygonWalk )
|
||
|
{
|
||
|
if( &pPolygonWalk->polygon != control.pNewPolygon )
|
||
|
{
|
||
|
bool bDead = true;
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pStartLine = pPolygonWalk->polygon.pLines;
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pWalkLine = pStartLine;
|
||
|
do
|
||
|
{
|
||
|
Assert( pWalkLine->pLine->pPolygons[pWalkLine->iReferenceIndex] == &pPolygonWalk->polygon );
|
||
|
if( pWalkLine->pLine->planarity & LINE_ALIVE )
|
||
|
{
|
||
|
bDead = false;
|
||
|
break;
|
||
|
}
|
||
|
pWalkLine = pWalkLine->pNext;
|
||
|
} while ( pWalkLine != pStartLine );
|
||
|
if( bDead )
|
||
|
{
|
||
|
pPolygonWalk->polygon.bDead = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pPolygonWalk = pPolygonWalk->pNext;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pFinal = control.pActivePolyLine;
|
||
|
|
||
|
if( pFinal->pNext->pLine->pPoints[1 - pFinal->pNext->iReferenceIndex] == NULL )
|
||
|
{
|
||
|
//last line redundant, copy partial data into first line
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pFirst = pFinal->pNext;
|
||
|
Assert_DumpPolyhedron( pFinal != pFirst );
|
||
|
Assert_DumpPolyhedron( pFirst->pLine->pPoints[pFirst->iReferenceIndex] != NULL );
|
||
|
Assert_DumpPolyhedron( pFinal->pLine->pPoints[1 - pFinal->iReferenceIndex] != NULL );
|
||
|
|
||
|
//link over redundant last line
|
||
|
pFirst->pPrev = pFinal->pPrev;
|
||
|
pFinal->pPrev->pNext = pFirst;
|
||
|
|
||
|
//fill in first point of first line
|
||
|
pFirst->pLine->pPoints[1 - pFirst->iReferenceIndex] = pFinal->pLine->pPoints[1 - pFinal->iReferenceIndex];
|
||
|
pFirst->pLine->PointLineLinks[1 - pFirst->iReferenceIndex].pNext = pFinal->pLine->PointLineLinks[1 - pFinal->iReferenceIndex].pNext;
|
||
|
pFirst->pLine->PointLineLinks[1 - pFirst->iReferenceIndex].pPrev = pFinal->pLine->PointLineLinks[1 - pFinal->iReferenceIndex].pPrev;
|
||
|
|
||
|
pFirst->pLine->PointLineLinks[1 - pFirst->iReferenceIndex].pNext->pPrev = &pFirst->pLine->PointLineLinks[1 - pFirst->iReferenceIndex];
|
||
|
pFirst->pLine->PointLineLinks[1 - pFirst->iReferenceIndex].pPrev->pNext = &pFirst->pLine->PointLineLinks[1 - pFirst->iReferenceIndex];
|
||
|
|
||
|
control.pNewPolygon->pLines = pFirst;
|
||
|
DestructLine( pFinal->pLine, destructors );
|
||
|
|
||
|
#if defined( DBGFLAG_ASSERT )
|
||
|
iPatchMethod = 1;
|
||
|
{
|
||
|
fltx4 vLineTemp = SubSIMD( pFirst->pLine->pPoints[1]->ptPosition, pFirst->pLine->pPoints[0]->ptPosition );
|
||
|
AssertMsg( (SubFloat( vLineTemp, 0 ) != 0.0f) || (SubFloat( vLineTemp, 1 ) != 0.0f) || (SubFloat( vLineTemp, 2 ) != 0.0f), "Created zero length line" );
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if( pFinal->pLine->pPoints[1 - pFinal->iReferenceIndex] != pFinal->pNext->pLine->pPoints[1 - pFinal->pNext->iReferenceIndex] )
|
||
|
{
|
||
|
//link up the last line to the first line
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pFirstLine = pFinal->pNext;
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_Line *pCompletedPolyLine = pFinal->pLine;
|
||
|
|
||
|
//We now should have everything we need to finish constructing pCompletedLine
|
||
|
{
|
||
|
//pCompletedLine->pLine->pPoints[0] should already have valid values that just need to be linked back in
|
||
|
pCompletedPolyLine->PointLineLinks[0].pNext->pPrev = &pCompletedPolyLine->PointLineLinks[0];
|
||
|
pCompletedPolyLine->PointLineLinks[0].pPrev->pNext = &pCompletedPolyLine->PointLineLinks[0];
|
||
|
|
||
|
pCompletedPolyLine->pPoints[1] = pFirstLine->pLine->pPoints[1 - pFirstLine->iReferenceIndex];
|
||
|
pCompletedPolyLine->PointLineLinks[1].pPrev = &pFirstLine->pLine->PointLineLinks[1 - pFirstLine->iReferenceIndex];
|
||
|
pCompletedPolyLine->PointLineLinks[1].pNext = pFirstLine->pLine->PointLineLinks[1 - pFirstLine->iReferenceIndex].pNext;
|
||
|
Assert_DumpPolyhedron( pCompletedPolyLine->PointLineLinks[1].pNext->pLine->planarity != LINE_DEAD );
|
||
|
pCompletedPolyLine->PointLineLinks[1].pNext->pPrev = &pCompletedPolyLine->PointLineLinks[1];
|
||
|
pCompletedPolyLine->PointLineLinks[1].pPrev->pNext = &pCompletedPolyLine->PointLineLinks[1];
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pLivingPolySide = pCompletedPolyLine->PointLineLinks[1].pNext;
|
||
|
pLivingPolySide = &pLivingPolySide->pLine->PolygonLineLinks[1 - pLivingPolySide->iReferenceIndex]; //convert the link from a point space to polygon space
|
||
|
|
||
|
//link outwardly into the patched up polygon
|
||
|
pCompletedPolyLine->pPolygons[0] = pLivingPolySide->pLine->pPolygons[pLivingPolySide->iReferenceIndex];
|
||
|
pCompletedPolyLine->PolygonLineLinks[0].pPrev = &pLivingPolySide->pLine->PolygonLineLinks[pLivingPolySide->iReferenceIndex];
|
||
|
pCompletedPolyLine->PolygonLineLinks[0].pNext = pCompletedPolyLine->PolygonLineLinks[0].pPrev->pNext;
|
||
|
pCompletedPolyLine->PolygonLineLinks[0].pNext->pPrev = &pCompletedPolyLine->PolygonLineLinks[0];
|
||
|
pCompletedPolyLine->PolygonLineLinks[0].pPrev->pNext = &pCompletedPolyLine->PolygonLineLinks[0];
|
||
|
Assert_DumpPolyhedron( pCompletedPolyLine->pPolygons[0] != pCompletedPolyLine->pPolygons[1] );
|
||
|
|
||
|
#if defined( DBGFLAG_ASSERT )
|
||
|
iPatchMethod = 2;
|
||
|
{
|
||
|
fltx4 vLineTemp = SubSIMD( pCompletedPolyLine->pPoints[1]->ptPosition, pCompletedPolyLine->pPoints[0]->ptPosition );
|
||
|
AssertMsg( (SubFloat( vLineTemp, 0 ) != 0.0f) || (SubFloat( vLineTemp, 1 ) != 0.0f) || (SubFloat( vLineTemp, 2 ) != 0.0f), "Created zero length line" );
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//first and last lines were on-plane
|
||
|
control.pNewPolygon->pLines = control.pActivePolyLine->pNext;
|
||
|
control.pActivePolyLine->pNext->pPrev = control.pActivePolyLine->pPrev;
|
||
|
control.pActivePolyLine->pPrev->pNext = control.pActivePolyLine->pNext;
|
||
|
DestructLine( control.pActivePolyLine->pLine, destructors );
|
||
|
|
||
|
#if defined( DBGFLAG_ASSERT )
|
||
|
iPatchMethod = 3;
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if defined( DBGFLAG_ASSERT )
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_UnorderedLineLL *pLineWalk = pAllLines;
|
||
|
do
|
||
|
{
|
||
|
Assert( pLineWalk->line.pPoints[0] != NULL && pLineWalk->line.pPoints[1] != NULL );
|
||
|
pLineWalk = pLineWalk->pNext;
|
||
|
} while (pLineWalk);
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef DBGFLAG_ASSERT
|
||
|
destructors.PlaneCutHistory.AddToTail( (float *)&pOutwardFacingPlanes[iCurrentPlane] );
|
||
|
#endif
|
||
|
|
||
|
//remove dead lines
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_UnorderedLineLL *pActiveLineWalk = pAllLines;
|
||
|
do
|
||
|
{
|
||
|
if( pActiveLineWalk->line.planarity == LINE_DEAD )
|
||
|
{
|
||
|
UnlinkLine( pActiveLineWalk );
|
||
|
|
||
|
//move the line to the dead list
|
||
|
pActiveLineWalk = DestructLine( pActiveLineWalk, destructors );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pActiveLineWalk = pActiveLineWalk->pNext;
|
||
|
}
|
||
|
} while( pActiveLineWalk );
|
||
|
}
|
||
|
|
||
|
//remove dead polygons
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pActivePolygonWalk = pAllPolygons;
|
||
|
do
|
||
|
{
|
||
|
if( pActivePolygonWalk->polygon.bDead )
|
||
|
{
|
||
|
pActivePolygonWalk = DestructPolygon( pActivePolygonWalk, destructors );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pActivePolygonWalk = pActivePolygonWalk->pNext;
|
||
|
}
|
||
|
} while( pActivePolygonWalk );
|
||
|
}
|
||
|
|
||
|
//remove dead points
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPointLL *pActivePointWalk = pAllPoints;
|
||
|
do
|
||
|
{
|
||
|
if( pActivePointWalk->point.planarity == POINT_DEAD )
|
||
|
{
|
||
|
pActivePointWalk = DestructPoint( pActivePointWalk, destructors );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pActivePointWalk = pActivePointWalk->pNext;
|
||
|
}
|
||
|
} while( pActivePointWalk );
|
||
|
}
|
||
|
|
||
|
#if defined( DEBUG_POLYHEDRON_CONVERSION )
|
||
|
if( g_pPolyhedronCarvingDebugStepCallback != NULL )
|
||
|
{
|
||
|
CPolyhedron *pTestPolyhedron = ConvertLinkedGeometryToPolyhedron( pAllPolygons, pAllLines, pAllPoints, false, vShiftResultPositions );
|
||
|
Assert_DumpPolyhedron( pTestPolyhedron );
|
||
|
|
||
|
if( !g_pPolyhedronCarvingDebugStepCallback( pTestPolyhedron ) )
|
||
|
{
|
||
|
VMatrix matScaleCentered;
|
||
|
matScaleCentered.Identity();
|
||
|
matScaleCentered[0][0] = matScaleCentered[1][1] = matScaleCentered[2][2] = 10.0f;
|
||
|
matScaleCentered.SetTranslation( -pTestPolyhedron->Center() * 10.0f );
|
||
|
|
||
|
DumpPolyhedronToGLView( pTestPolyhedron, "AssertPolyhedron.txt", &matScaleCentered, "wb" );
|
||
|
AssertMsg_DumpPolyhedron( false, "Outside conversion failed" );
|
||
|
}
|
||
|
|
||
|
pTestPolyhedron->Release();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//When clipping a 2D polyhedron, the result of any clipping is a faux 3rd polygon which is degenerate (2 lines, both connected to the same set of points). Remove it now
|
||
|
if( pNewPolygon->pLines->pNext == pNewPolygon->pLines->pPrev )
|
||
|
{
|
||
|
AssertMsg_DumpPolyhedron( (pAllPolygons->pNext != NULL) && //more than 1
|
||
|
(pAllPolygons->pNext->pNext != NULL) && //more than 2
|
||
|
(pAllPolygons->pNext->pNext->pNext == NULL), //exactly 3 polygons, and no more
|
||
|
"This case should only pop up if the input to the last cutting pass was a 2 sided polyhedron" );
|
||
|
|
||
|
RemoveDegeneratePolygon( pNewPolygon, destructors );
|
||
|
pNewPolygon = NULL;
|
||
|
}
|
||
|
|
||
|
//remove super-short lines
|
||
|
{
|
||
|
//ideally this behavior should be integrated with the Recursive_MarkPlanarity_Dead() behavior to avoid creating them in the first place.
|
||
|
//But choosing safety over performance for now since Recursive_MarkPlanarity_Dead() is stable right now
|
||
|
GeneratePolyhedronFromPlanes_UnorderedLineLL *pActiveLineWalk = pAllLines;
|
||
|
do
|
||
|
{
|
||
|
if( pActiveLineWalk->line.bNewLengthThisPass )
|
||
|
{
|
||
|
fltx4 vDiff = SubSIMD( pActiveLineWalk->line.pPoints[0]->ptPosition, pActiveLineWalk->line.pPoints[1]->ptPosition );
|
||
|
vDiff = MulSIMD( vDiff, vDiff );
|
||
|
float fLengthSqr = SubFloat( vDiff, 0 ) + SubFloat( vDiff, 1 ) + SubFloat( vDiff, 2 );
|
||
|
if( fLengthSqr <= fOnPlaneEpsilonSquared ) //eq for 0.0 epsilon case
|
||
|
{
|
||
|
//this line needs to go. But removing it might have repercussions
|
||
|
GeneratePolyhedronFromPlanes_Polygon *pPolygons[2] = { pActiveLineWalk->line.pPolygons[0], pActiveLineWalk->line.pPolygons[1] };
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_Line *pShortLine = &pActiveLineWalk->line;
|
||
|
GeneratePolyhedronFromPlanes_Point *pDeadPoint = pShortLine->pPoints[1];
|
||
|
GeneratePolyhedronFromPlanes_Point *pKeepPoint = pShortLine->pPoints[0];
|
||
|
|
||
|
//reposition point 0 to be the midpoint? In my experience it's best to just leave it alone when dealing with small distances.
|
||
|
|
||
|
//relink point 1 links to point 0
|
||
|
{
|
||
|
//update point pointers
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pPointLineWalkHead = pDeadPoint->pConnectedLines;
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pPointLineWalk = pPointLineWalkHead;
|
||
|
do
|
||
|
{
|
||
|
pPointLineWalk->pLine->pPoints[1- pPointLineWalk->iReferenceIndex] = pKeepPoint;
|
||
|
pPointLineWalk->pLine->pPolygons[pPointLineWalk->iReferenceIndex]->bMovedExistingPoints = true;//also inform touching polygons that we're changing their geometry
|
||
|
pPointLineWalk = pPointLineWalk->pNext;
|
||
|
} while( pPointLineWalk != pPointLineWalkHead );
|
||
|
|
||
|
//except the line we're killing
|
||
|
pShortLine->pPoints[1] = pDeadPoint;
|
||
|
}
|
||
|
|
||
|
//insert all lines from point 1 as a fan between point 0 prev and the short line.
|
||
|
pShortLine->PointLineLinks[1].pNext->pPrev = pShortLine->PointLineLinks[0].pPrev;
|
||
|
pShortLine->PointLineLinks[0].pPrev->pNext = pShortLine->PointLineLinks[1].pNext;
|
||
|
|
||
|
pShortLine->PointLineLinks[1].pPrev->pNext = &pShortLine->PointLineLinks[0];
|
||
|
pShortLine->PointLineLinks[0].pPrev = pShortLine->PointLineLinks[1].pPrev;
|
||
|
|
||
|
pShortLine->PointLineLinks[1].pNext = &pShortLine->PointLineLinks[1];
|
||
|
pShortLine->PointLineLinks[1].pPrev = &pShortLine->PointLineLinks[1];
|
||
|
}
|
||
|
|
||
|
#if defined( DBGFLAG_ASSERT ) || defined( ENABLE_DEBUG_POLYHEDRON_DUMPS )
|
||
|
pDeadPoint->planarity = POINT_DEAD; //to avoid future asserts, also for coloration. Not useful otherwise as we're about to delete it.
|
||
|
#endif
|
||
|
|
||
|
|
||
|
UnlinkLine( pShortLine );
|
||
|
pActiveLineWalk = DestructLine( pShortLine, destructors );
|
||
|
DestructPoint( pDeadPoint, destructors );
|
||
|
|
||
|
for( int i = 0; i != 2; ++i )
|
||
|
{
|
||
|
if( pPolygons[i]->pLines->pNext == pPolygons[i]->pLines->pPrev )
|
||
|
{
|
||
|
//this polygon is dead
|
||
|
if( pAllPolygons->pNext->pNext == NULL )
|
||
|
{
|
||
|
//It's conceivably possible to either start or collapse down to a 2D polyhedron with every line cut save one.
|
||
|
//We can't have 1 polygon, and I don't want to mentally run down what would happen below if we tried to collapse down to 1.
|
||
|
//So just bail now
|
||
|
#if defined( ENABLE_DEBUG_POLYHEDRON_DUMPS )
|
||
|
Assert_DumpPolyhedron( g_bDumpNullPolyhedrons == false ); //if someone set it to true, we'll dump the polyhedron then halt
|
||
|
#endif
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if( pPolygons[i] == pNewPolygon )
|
||
|
{
|
||
|
pNewPolygon = NULL;
|
||
|
}
|
||
|
|
||
|
if( pActiveLineWalk )
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_UnorderedLineLL *pActiveLineWalkNext = pActiveLineWalk->pNext;
|
||
|
GeneratePolyhedronFromPlanes_Line *pTestDelete = &pActiveLineWalk->line;
|
||
|
|
||
|
if( pTestDelete == RemoveDegeneratePolygon( pPolygons[i], destructors ) )
|
||
|
{
|
||
|
pActiveLineWalk = pActiveLineWalkNext;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
RemoveDegeneratePolygon( pPolygons[i], destructors );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
continue; //pActiveLineWalk already updated
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pActiveLineWalk = pActiveLineWalk->pNext;
|
||
|
} while( pActiveLineWalk );
|
||
|
}
|
||
|
|
||
|
#if defined( DEBUG_POLYHEDRON_CONVERSION )
|
||
|
if( g_pPolyhedronCarvingDebugStepCallback != NULL )
|
||
|
{
|
||
|
CPolyhedron *pTestPolyhedron = ConvertLinkedGeometryToPolyhedron( pAllPolygons, pAllLines, pAllPoints, false, vShiftResultPositions );
|
||
|
Assert_DumpPolyhedron( pTestPolyhedron );
|
||
|
|
||
|
if( !g_pPolyhedronCarvingDebugStepCallback( pTestPolyhedron ) )
|
||
|
{
|
||
|
VMatrix matScaleCentered;
|
||
|
matScaleCentered.Identity();
|
||
|
matScaleCentered[0][0] = matScaleCentered[1][1] = matScaleCentered[2][2] = 10.0f;
|
||
|
matScaleCentered.SetTranslation( -pTestPolyhedron->Center() * 10.0f );
|
||
|
|
||
|
DumpPolyhedronToGLView( pTestPolyhedron, "AssertPolyhedron.txt", &matScaleCentered, "wb" );
|
||
|
AssertMsg_DumpPolyhedron( false, "Outside conversion failed" );
|
||
|
}
|
||
|
|
||
|
pTestPolyhedron->Release();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
AssertMsg( pAllPolygons->pNext != NULL, "A polyhedron must have at least 2 sides to be a 2D Polyhedron, and at least 4 to be a 3D polyhedron" );
|
||
|
|
||
|
//if any polygons had their geometry adjusted (or new poly uses existing on-plane points), we need to recompute the surface normal and check for degenerate points
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pActivePolygonWalk = pAllPolygons;
|
||
|
if( pAllPolygons->pNext->pNext != NULL )
|
||
|
{
|
||
|
//for 3D polyhedrons, remove degenerate points and recompute normals
|
||
|
do
|
||
|
{
|
||
|
if( pActivePolygonWalk->polygon.bMovedExistingPoints || pActivePolygonWalk->polygon.bHasNewPoints )
|
||
|
{
|
||
|
RemoveDegeneratePoints( &pActivePolygonWalk->polygon, destructors );
|
||
|
RecomputePolygonSurfaceNormal( &pActivePolygonWalk->polygon );
|
||
|
}
|
||
|
pActivePolygonWalk = pActivePolygonWalk->pNext;
|
||
|
} while( pActivePolygonWalk );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//for 2D polyhedrons, only recompute normals. RemoveDegeneratePoints() is not safe for 2D polyhedrons as it assumes the shape is 3 dimensional to avoid angle computations and checks
|
||
|
do
|
||
|
{
|
||
|
if( pActivePolygonWalk->polygon.bMovedExistingPoints || pActivePolygonWalk->polygon.bHasNewPoints )
|
||
|
{
|
||
|
RecomputePolygonSurfaceNormal( &pActivePolygonWalk->polygon );
|
||
|
}
|
||
|
pActivePolygonWalk = pActivePolygonWalk->pNext;
|
||
|
} while( pActivePolygonWalk );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if defined( DEBUG_POLYHEDRON_CONVERSION )
|
||
|
if( g_pPolyhedronCarvingDebugStepCallback != NULL )
|
||
|
{
|
||
|
CPolyhedron *pTestPolyhedron = ConvertLinkedGeometryToPolyhedron( pAllPolygons, pAllLines, pAllPoints, false, vShiftResultPositions );
|
||
|
Assert_DumpPolyhedron( pTestPolyhedron );
|
||
|
|
||
|
if( !g_pPolyhedronCarvingDebugStepCallback( pTestPolyhedron ) )
|
||
|
{
|
||
|
VMatrix matScaleCentered;
|
||
|
matScaleCentered.Identity();
|
||
|
matScaleCentered[0][0] = matScaleCentered[1][1] = matScaleCentered[2][2] = 10.0f;
|
||
|
matScaleCentered.SetTranslation( -pTestPolyhedron->Center() * 10.0f );
|
||
|
|
||
|
DumpPolyhedronToGLView( pTestPolyhedron, "AssertPolyhedron.txt", &matScaleCentered, "wb" );
|
||
|
AssertMsg_DumpPolyhedron( false, "Outside conversion failed" );
|
||
|
}
|
||
|
|
||
|
pTestPolyhedron->Release();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//remove points which, although seemingly useful. Actually create concave shapes
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pActivePolygonWalk = pAllPolygons;
|
||
|
do
|
||
|
{
|
||
|
if( pActivePolygonWalk->polygon.bHasNewPoints )
|
||
|
{
|
||
|
Vector vTestNormal = pActivePolygonWalk->polygon.vSurfaceNormal;
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pLineWalkHead = pActivePolygonWalk->polygon.pLines;
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pLineWalk = pLineWalkHead->pPrev;
|
||
|
Vector vLastLine;
|
||
|
|
||
|
fltx4 vLineTemp = SubSIMD( pLineWalk->pLine->pPoints[pLineWalk->iReferenceIndex]->ptPosition, pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->ptPosition );
|
||
|
pLineWalk = pLineWalk->pNext;
|
||
|
vLastLine.Init( SubFloat( vLineTemp, 0 ), SubFloat( vLineTemp, 1 ), SubFloat( vLineTemp, 2 ) );
|
||
|
do
|
||
|
{
|
||
|
vLineTemp = SubSIMD( pLineWalk->pLine->pPoints[pLineWalk->iReferenceIndex]->ptPosition, pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->ptPosition );
|
||
|
Vector vThisLine( SubFloat( vLineTemp, 0 ), SubFloat( vLineTemp, 1 ), SubFloat( vLineTemp, 2 ) );
|
||
|
Vector vCross = vThisLine.Cross( vLastLine );
|
||
|
if( vCross.Dot( vTestNormal ) <= 0.0f )
|
||
|
{
|
||
|
//this point is a troublemaker
|
||
|
GeneratePolyhedronFromPlanes_Point *pProblemPoint = pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex];
|
||
|
|
||
|
if( pLineWalk->pPrev == pLineWalkHead )
|
||
|
{
|
||
|
//about to delete our start/stop point. Avoid infinite loops
|
||
|
pLineWalkHead = pLineWalk;
|
||
|
}
|
||
|
|
||
|
#if defined( DBGFLAG_ASSERT ) || defined( ENABLE_DEBUG_POLYHEDRON_DUMPS )
|
||
|
pProblemPoint->planarity = POINT_DEAD; //to avoid future asserts, also for coloration. Not useful otherwise as we're about to delete it.
|
||
|
#endif
|
||
|
|
||
|
#if defined( DBGFLAG_ASSERT )
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pDebugLineWalkHead = pProblemPoint->pConnectedLines;
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pDebugLineWalk = pDebugLineWalkHead;
|
||
|
do
|
||
|
{
|
||
|
Assert( (pDebugLineWalk->pLine->pPolygons[0] == &pActivePolygonWalk->polygon) ||
|
||
|
(pDebugLineWalk->pLine->pPolygons[1] == &pActivePolygonWalk->polygon) ||
|
||
|
(pDebugLineWalk->pLine->pPolygons[0]->vSurfaceNormal.Dot( pDebugLineWalk->pLine->pPolygons[1]->vSurfaceNormal ) > 0.95f) );
|
||
|
|
||
|
pDebugLineWalk = pDebugLineWalk->pNext;
|
||
|
} while( pDebugLineWalk != pDebugLineWalkHead );
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//what we're going to do is eliminate the problem point and every line connected to it save one.
|
||
|
//That one saved line will take the place of both lines on the new poly edge connected to this point
|
||
|
//As part of eliminating all the lines connected to this point, we need to merge polygons separated by those lines.
|
||
|
//To do that we'll just take the first polygon we encounter and stitch the other polygon lines into it.
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pPatchStop = &pLineWalk->pLine->PolygonLineLinks[1 - pLineWalk->iReferenceIndex]; //where the polygon patching algorithm should stop
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pPatchWalk = pLineWalk->pPrev; //rewind to prev line
|
||
|
pPatchWalk = &pPatchWalk->pLine->PolygonLineLinks[1 - pPatchWalk->iReferenceIndex]; //convert to the non-new polygon walk space, pPatchWalk is the on-polygon line we'll be killing
|
||
|
GeneratePolyhedronFromPlanes_Polygon *pPatchPolygon = pPatchWalk->pLine->pPolygons[pPatchWalk->iReferenceIndex];
|
||
|
|
||
|
//pPatchStop doubles as the surviving on-polygon line. Go ahead and do all the fix ups necessary to allow us to clean up the dead lines as we come across them
|
||
|
pPatchStop->pLine->pPoints[pPatchStop->iReferenceIndex] = pPatchWalk->pLine->pPoints[pPatchWalk->iReferenceIndex];
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pRelink = &pPatchStop->pLine->PointLineLinks[pPatchStop->iReferenceIndex];
|
||
|
//unlink pPatchStop from dead point
|
||
|
pRelink->pNext->pPrev = pRelink->pPrev;
|
||
|
pRelink->pPrev->pNext = pRelink->pNext;
|
||
|
|
||
|
//relink into pPatchWalk's living point (insert arbitrarily before pPatchWalk)
|
||
|
pRelink->pNext = &pPatchWalk->pLine->PointLineLinks[pPatchWalk->iReferenceIndex];
|
||
|
pRelink->pPrev = pRelink->pNext->pPrev;
|
||
|
pRelink->pNext->pPrev = pRelink;
|
||
|
pRelink->pPrev->pNext = pRelink;
|
||
|
}
|
||
|
|
||
|
pPatchWalk = pPatchWalk->pNext; //forward to the next line that won't be dead when we're done. From here on it's a clockwise walk on this polygon until we reach a line we'll kill, then we switch to the next
|
||
|
|
||
|
//go ahead and kill the dead on-polygon line
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_Line *pKillLine = pPatchWalk->pPrev->pLine;
|
||
|
#if defined( DBGFLAG_ASSERT ) || defined( ENABLE_DEBUG_POLYHEDRON_DUMPS )
|
||
|
pKillLine->planarity = LINE_DEAD; //to avoid future asserts, also for coloration. Not useful otherwise as we're about to delete it.
|
||
|
#endif
|
||
|
UnlinkLine( pKillLine );
|
||
|
DestructLine( pKillLine, destructors );
|
||
|
}
|
||
|
|
||
|
do
|
||
|
{
|
||
|
if( pPatchWalk->pLine->pPoints[pPatchWalk->iReferenceIndex] == pProblemPoint )
|
||
|
{
|
||
|
//done walking this polygon, stitch it to the next
|
||
|
GeneratePolyhedronFromPlanes_Line *pKillLine = pPatchWalk->pLine;
|
||
|
GeneratePolyhedronFromPlanes_Polygon *pKillPolygon = pPatchWalk->pLine->pPolygons[pPatchWalk->iReferenceIndex];
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pPatchPrev = pPatchWalk->pPrev;
|
||
|
pPatchWalk = pPatchWalk->pLine->PolygonLineLinks[1 - pPatchWalk->iReferenceIndex].pNext; //flip to the other side's polygon, then forward to the next line we'll keep
|
||
|
|
||
|
//patching will be more straightforward if we kill all the dead stuff now
|
||
|
#if defined( DBGFLAG_ASSERT ) || defined( ENABLE_DEBUG_POLYHEDRON_DUMPS )
|
||
|
pKillLine->planarity = LINE_DEAD; //to avoid future asserts, also for coloration. Not useful otherwise as we're about to delete it.
|
||
|
#endif
|
||
|
UnlinkLine( pKillLine );
|
||
|
DestructLine( pKillLine, destructors );
|
||
|
if( pKillPolygon != pPatchPolygon )
|
||
|
{
|
||
|
#if defined( DBGFLAG_ASSERT ) || defined( ENABLE_DEBUG_POLYHEDRON_DUMPS )
|
||
|
pKillPolygon->bDead = true; //to avoid future asserts, also for coloration. Not useful otherwise as we're about to delete it.
|
||
|
#endif
|
||
|
DestructPolygon( pKillPolygon, destructors );
|
||
|
}
|
||
|
|
||
|
//link the far end of the polygon to our start (which should continually be the first living pPatchWalk line on pPatchPolygon
|
||
|
pPatchWalk->pPrev->pNext = pPatchPrev->pNext;
|
||
|
pPatchPrev->pNext->pPrev = pPatchWalk->pPrev;
|
||
|
|
||
|
pPatchPrev->pNext = pPatchWalk;
|
||
|
pPatchWalk->pPrev = pPatchPrev; //at this point, the two polygons's line linkages are one large polygon except that the new polygon's line are pointing at the dead polygon which will be fixed as we continue to walk it
|
||
|
}
|
||
|
pPatchWalk->pLine->pPolygons[pPatchWalk->iReferenceIndex] = pPatchPolygon;
|
||
|
|
||
|
pPatchWalk = pPatchWalk->pNext;
|
||
|
} while( pPatchWalk != pPatchStop );
|
||
|
|
||
|
//final patch is just slightly different than other patches
|
||
|
{
|
||
|
#if defined( DBGFLAG_ASSERT ) || defined( ENABLE_DEBUG_POLYHEDRON_DUMPS )
|
||
|
pPatchStop->pLine->pPolygons[pPatchStop->iReferenceIndex]->bDead = true; //to avoid future asserts, also for coloration. Not useful otherwise as we're about to delete it.
|
||
|
#endif
|
||
|
DestructPolygon( pPatchStop->pLine->pPolygons[pPatchStop->iReferenceIndex], destructors );
|
||
|
pPatchStop->pLine->pPolygons[pPatchStop->iReferenceIndex] = pPatchPolygon;
|
||
|
}
|
||
|
|
||
|
DestructPoint( pProblemPoint, destructors );
|
||
|
|
||
|
Assert( pAllPolygons->pNext != NULL );
|
||
|
if( pAllPolygons->pNext->pNext != NULL )
|
||
|
{
|
||
|
RemoveDegeneratePoints( pPatchPolygon, destructors );
|
||
|
}
|
||
|
|
||
|
//vLastLine and vThisLine are invalidated. Luckily we just need to recompute vThisLine and it'll copy to vLastLine
|
||
|
vLineTemp = SubSIMD( pLineWalk->pLine->pPoints[pLineWalk->iReferenceIndex]->ptPosition, pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->ptPosition );
|
||
|
vThisLine.Init( SubFloat( vLineTemp, 0 ), SubFloat( vLineTemp, 1 ), SubFloat( vLineTemp, 2 ) );
|
||
|
}
|
||
|
|
||
|
vLastLine = vThisLine;
|
||
|
pLineWalk = pLineWalk->pNext;
|
||
|
} while ( pLineWalk != pLineWalkHead );
|
||
|
}
|
||
|
|
||
|
pActivePolygonWalk = pActivePolygonWalk->pNext;
|
||
|
} while( pActivePolygonWalk );
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef DBGFLAG_ASSERT
|
||
|
//verify that repairs are complete
|
||
|
{
|
||
|
DBG_RESETWORKINGSTATECOLORS();
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pDebugPolygonWalk = pAllPolygons;
|
||
|
do
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pLineStart = pDebugPolygonWalk->polygon.pLines;
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pLineWalk = pLineStart;
|
||
|
GeneratePolyhedronFromPlanes_Point *pCheckPoint = pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex];
|
||
|
|
||
|
int iCount = 0;
|
||
|
//walk around the polygon according to its line links
|
||
|
do
|
||
|
{
|
||
|
Assert_DumpPolyhedron( pLineWalk->pLine->planarity != LINE_DEAD );
|
||
|
Assert_DumpPolyhedron( (pLineWalk->pLine->pPoints[0]->planarity != POINT_DEAD) && (pLineWalk->pLine->pPoints[1]->planarity != POINT_DEAD) );
|
||
|
AssertMsg_DumpPolyhedron( pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex] == pCheckPoint, "Line endpoint mismatch" ); //last line's endpoint does not match up with this lines start point
|
||
|
AssertMsg_DumpPolyhedron( pLineWalk->pLine->pPolygons[pLineWalk->iReferenceIndex] == &pDebugPolygonWalk->polygon, "Line links to wrong polygon" );
|
||
|
|
||
|
#if 1
|
||
|
fltx4 f4Diff1 = SubSIMD( pLineWalk->pLine->pPoints[pLineWalk->iReferenceIndex]->ptPosition, pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->ptPosition );
|
||
|
fltx4 f4Diff2 = SubSIMD( pLineWalk->pNext->pLine->pPoints[pLineWalk->pNext->iReferenceIndex]->ptPosition, pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->ptPosition );
|
||
|
Vector vDiff1( SubFloat( f4Diff1, 0 ), SubFloat( f4Diff1, 1 ), SubFloat( f4Diff1, 2 ) );
|
||
|
Vector vDiff2( SubFloat( f4Diff2, 0 ), SubFloat( f4Diff2, 1 ), SubFloat( f4Diff2, 2 ) );
|
||
|
|
||
|
Vector vCross = vDiff2.Cross( vDiff1 );
|
||
|
float fDot = vCross.Dot( pDebugPolygonWalk->polygon.vSurfaceNormal );
|
||
|
AssertMsg_DumpPolyhedron( fDot >= 0.0f, "Concave polygon" );
|
||
|
#endif
|
||
|
|
||
|
|
||
|
pCheckPoint = pLineWalk->pLine->pPoints[pLineWalk->iReferenceIndex];
|
||
|
pLineWalk = pLineWalk->pNext;
|
||
|
++iCount;
|
||
|
} while( pLineWalk != pLineStart );
|
||
|
AssertMsg_DumpPolyhedron( iCount >= 3, "Degenerate polygon" );
|
||
|
|
||
|
|
||
|
|
||
|
#if 0
|
||
|
fltx4 vSurfaceNormal;
|
||
|
SubFloat( vSurfaceNormal, 0 ) = pDebugPolygonWalk->polygon.vSurfaceNormal.x;
|
||
|
SubFloat( vSurfaceNormal, 1 ) = pDebugPolygonWalk->polygon.vSurfaceNormal.y;
|
||
|
SubFloat( vSurfaceNormal, 2 ) = pDebugPolygonWalk->polygon.vSurfaceNormal.z;
|
||
|
SubFloat( vSurfaceNormal, 3 ) = 0.0f;
|
||
|
#endif
|
||
|
|
||
|
pLineStart = &pLineStart->pLine->PointLineLinks[1 - pLineStart->iReferenceIndex];
|
||
|
pLineWalk = pLineStart;
|
||
|
//walk around the polygon again using point traversal
|
||
|
do
|
||
|
{
|
||
|
AssertMsg_DumpPolyhedron( pLineWalk->pLine->pPolygons[pLineWalk->iReferenceIndex] == &pDebugPolygonWalk->polygon, "bad line/polygon linkage" );
|
||
|
|
||
|
#if 0
|
||
|
//at each point of each polygon, make sure every line connected to that point goes away from our normal
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pConcavityWalk = &pLineWalk->pLine->PointLineLinks[pLineWalk->iReferenceIndex];
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pConcavityWalkStop = pConcavityWalk->pPrev;
|
||
|
pConcavityWalk = pConcavityWalk->pNext;
|
||
|
while ( pConcavityWalk != pConcavityWalkStop )
|
||
|
{
|
||
|
fltx4 vLine = SubSIMD( pConcavityWalk->pLine->pPoints[pConcavityWalk->iReferenceIndex]->ptPosition, pConcavityWalk->pLine->pPoints[1 - pConcavityWalk->iReferenceIndex]->ptPosition );
|
||
|
fltx4 vMul = MulSIMD( vSurfaceNormal, vLine );
|
||
|
float fDot = SubFloat( vMul, 0 ) + SubFloat( vMul, 1 ) + SubFloat( vMul, 2 );
|
||
|
AssertMsg_DumpPolyhedron( fDot <= kfPointRounding, "Concave polyhedron" );
|
||
|
pConcavityWalk = pConcavityWalk->pNext;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
pLineWalk = pLineWalk->pLine->PointLineLinks[pLineWalk->iReferenceIndex].pPrev;
|
||
|
} while( pLineWalk != pLineStart );
|
||
|
|
||
|
pDebugPolygonWalk = pDebugPolygonWalk->pNext;
|
||
|
} while( pDebugPolygonWalk );
|
||
|
|
||
|
bool bTwoPolygons = (pAllPolygons->pNext->pNext == NULL);
|
||
|
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPointLL *pDebugPointWalk = pAllPoints;
|
||
|
do
|
||
|
{
|
||
|
int iCount = 0;
|
||
|
AssertMsg_DumpPolyhedron( pDebugPointWalk->point.pConnectedLines, "Point connected to no lines after cut" );
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pLineStart = pDebugPointWalk->point.pConnectedLines;
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pLineWalk = pLineStart;
|
||
|
GeneratePolyhedronFromPlanes_Point *pTestPoint = &pDebugPointWalk->point;
|
||
|
do
|
||
|
{
|
||
|
fltx4 f4Diff = SubSIMD( pTestPoint->ptPosition, pLineWalk->pLine->pPoints[pLineWalk->iReferenceIndex]->ptPosition );
|
||
|
Vector vDiff( SubFloat( f4Diff, 0 ), SubFloat( f4Diff, 1 ), SubFloat( f4Diff, 2 ) );
|
||
|
float fLengthSqr = vDiff.LengthSqr();
|
||
|
AssertMsg_DumpPolyhedron( fLengthSqr > 0.0f, "Generated a point on top of another" );
|
||
|
AssertMsg_DumpPolyhedron( pLineWalk->pLine->pPolygons[pLineWalk->iReferenceIndex] == pLineWalk->pNext->pLine->pPolygons[1 - pLineWalk->pNext->iReferenceIndex], "Bad line/polygon linkage" );
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pSubLineWalk = pLineWalk->pNext;
|
||
|
while( pSubLineWalk != pLineStart )
|
||
|
{
|
||
|
AssertMsg_DumpPolyhedron( (pLineWalk->pLine->pPolygons[pLineWalk->iReferenceIndex] != pSubLineWalk->pLine->pPolygons[pSubLineWalk->iReferenceIndex]) &&
|
||
|
(pLineWalk->pLine->pPolygons[1 - pLineWalk->iReferenceIndex] != pSubLineWalk->pLine->pPolygons[1 - pSubLineWalk->iReferenceIndex]), "Point connected to two other points on the same polygon" );
|
||
|
|
||
|
pSubLineWalk = pSubLineWalk->pNext;
|
||
|
}
|
||
|
pLineWalk = pLineWalk->pNext;
|
||
|
++iCount;
|
||
|
} while( pLineWalk != pLineStart );
|
||
|
|
||
|
AssertMsg_DumpPolyhedron( bTwoPolygons || iCount >= 3, "Degenerate point" ); //there can be two-sided polyhedrons where this is not a degenerate case
|
||
|
|
||
|
pDebugPointWalk = pDebugPointWalk->pNext;
|
||
|
} while( pDebugPointWalk );
|
||
|
|
||
|
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_UnorderedLineLL *pDebugLineWalk = pAllLines;
|
||
|
do
|
||
|
{
|
||
|
AssertMsg_DumpPolyhedron( (pDebugLineWalk->line.pPolygons[0] != NULL) && (pDebugLineWalk->line.pPolygons[1] != NULL), "There's a polygon missing" );
|
||
|
AssertMsg_DumpPolyhedron( pDebugLineWalk->line.pPoints[0] && pDebugLineWalk->line.pPoints[1], "Line missing a point" );
|
||
|
|
||
|
#if 0
|
||
|
fltx4 f4Line = SubSIMD( pDebugLineWalk->line.pPoints[0]->ptPosition, pDebugLineWalk->line.pPoints[1]->ptPosition );
|
||
|
Vector vLine( SubFloat( f4Line, 0 ), SubFloat( f4Line, 1 ), SubFloat( f4Line, 2 ) );
|
||
|
float fLength = vLine.Length();
|
||
|
AssertMsg_DumpPolyhedron( fLength > FLT_EPSILON, "Ridiculously short line" );
|
||
|
|
||
|
if( fLength > fOnPlaneEpsilon )
|
||
|
{
|
||
|
if( fLength > 1.0f )
|
||
|
vLine *= 1.0f / fLength;
|
||
|
|
||
|
float fDots[2] = { vLine.Dot( pDebugLineWalk->line.pPolygons[0]->vSurfaceNormal ), vLine.Dot( pDebugLineWalk->line.pPolygons[1]->vSurfaceNormal ) };
|
||
|
AssertMsg_DumpPolyhedron( (fabs( fDots[0] ) < (1.0f/128.0f) ) &&
|
||
|
(fabs( fDots[1] ) < (1.0f/128.0f) ),
|
||
|
"Line is not orthogonal to plane normal it's surrounding" );
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
pDebugLineWalk = pDebugLineWalk->pNext;
|
||
|
} while( pDebugLineWalk );
|
||
|
|
||
|
pStartPoint = NULL;
|
||
|
}
|
||
|
|
||
|
CPolyhedron *pHistoryPolyhedron = ConvertLinkedGeometryToPolyhedron( pAllPolygons, pAllLines, pAllPoints, false, vShiftResultPositions );
|
||
|
|
||
|
#if defined( DEBUG_POLYHEDRON_CONVERSION )
|
||
|
//last bit of debugging from whatever outside source wants this stupid thing
|
||
|
if( g_pPolyhedronCarvingDebugStepCallback != NULL )
|
||
|
{
|
||
|
if( !g_pPolyhedronCarvingDebugStepCallback( pHistoryPolyhedron ) )
|
||
|
{
|
||
|
VMatrix matScaleCentered;
|
||
|
matScaleCentered.Identity();
|
||
|
matScaleCentered[0][0] = matScaleCentered[1][1] = matScaleCentered[2][2] = 10.0f;
|
||
|
matScaleCentered.SetTranslation( -pHistoryPolyhedron->Center() * 10.0f );
|
||
|
|
||
|
DumpPolyhedronToGLView( pHistoryPolyhedron, "AssertPolyhedron.txt", &matScaleCentered, "wb" );
|
||
|
AssertMsg_DumpPolyhedron( false, "Outside conversion failed" );
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//maintain the cut history
|
||
|
destructors.DebugCutHistory.AddToTail( pHistoryPolyhedron );
|
||
|
destructors.DebugCutPlaneIndex.AddToTail( iCurrentPlane );
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
return ConvertLinkedGeometryToPolyhedron( pAllPolygons, pAllLines, pAllPoints, bUseTemporaryMemory, vShiftResultPositions );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
#define STARTPOINTTOLINELINKS(iPointNum, lineindex1, iOtherPointIndex1, lineindex2, iOtherPointIndex2, lineindex3, iOtherPointIndex3 )\
|
||
|
StartingPointList[iPointNum].point.pConnectedLines = &StartingLineList[lineindex1].line.PointLineLinks[1 - iOtherPointIndex1];\
|
||
|
StartingPointList[iPointNum].point.pConnectedLines->pNext = &StartingLineList[lineindex2].line.PointLineLinks[1 - iOtherPointIndex2];\
|
||
|
StartingPointList[iPointNum].point.pConnectedLines->pPrev = &StartingLineList[lineindex3].line.PointLineLinks[1 - iOtherPointIndex3];\
|
||
|
StartingPointList[iPointNum].point.pConnectedLines->pNext->pNext = StartingPointList[iPointNum].point.pConnectedLines->pPrev;\
|
||
|
StartingPointList[iPointNum].point.pConnectedLines->pNext->pPrev = StartingPointList[iPointNum].point.pConnectedLines;\
|
||
|
StartingPointList[iPointNum].point.pConnectedLines->pPrev->pNext = StartingPointList[iPointNum].point.pConnectedLines;\
|
||
|
StartingPointList[iPointNum].point.pConnectedLines->pPrev->pPrev = StartingPointList[iPointNum].point.pConnectedLines->pNext;
|
||
|
|
||
|
#define STARTBOXCONNECTION( linenum, point1, point2, poly1, poly2 )\
|
||
|
StartingLineList[linenum].line.pPoints[0] = &StartingPointList[point1].point;\
|
||
|
StartingLineList[linenum].line.pPoints[1] = &StartingPointList[point2].point;\
|
||
|
StartingLineList[linenum].line.pPolygons[0] = &StartingPolygonList[poly1].polygon;\
|
||
|
StartingLineList[linenum].line.pPolygons[1] = &StartingPolygonList[poly2].polygon;
|
||
|
|
||
|
#define STARTPOLYGONTOLINELINKS( polynum, lineindex1, iThisPolyIndex1, lineindex2, iThisPolyIndex2, lineindex3, iThisPolyIndex3, lineindex4, iThisPolyIndex4 )\
|
||
|
StartingPolygonList[polynum].polygon.pLines = &StartingLineList[lineindex1].line.PolygonLineLinks[iThisPolyIndex1];\
|
||
|
StartingPolygonList[polynum].polygon.pLines->pNext = &StartingLineList[lineindex2].line.PolygonLineLinks[iThisPolyIndex2];\
|
||
|
StartingPolygonList[polynum].polygon.pLines->pPrev = &StartingLineList[lineindex4].line.PolygonLineLinks[iThisPolyIndex4];\
|
||
|
StartingPolygonList[polynum].polygon.pLines->pNext->pPrev = StartingPolygonList[polynum].polygon.pLines;\
|
||
|
StartingPolygonList[polynum].polygon.pLines->pPrev->pNext = StartingPolygonList[polynum].polygon.pLines;\
|
||
|
StartingPolygonList[polynum].polygon.pLines->pNext->pNext = &StartingLineList[lineindex3].line.PolygonLineLinks[iThisPolyIndex3];\
|
||
|
StartingPolygonList[polynum].polygon.pLines->pPrev->pPrev = StartingPolygonList[polynum].polygon.pLines->pNext->pNext;\
|
||
|
StartingPolygonList[polynum].polygon.pLines->pNext->pNext->pPrev = StartingPolygonList[polynum].polygon.pLines->pNext;\
|
||
|
StartingPolygonList[polynum].polygon.pLines->pPrev->pPrev->pNext = StartingPolygonList[polynum].polygon.pLines->pPrev;
|
||
|
|
||
|
CPolyhedron *GeneratePolyhedronFromPlanes( const float *pOutwardFacingPlanes, int iPlaneCount, float fOnPlaneEpsilon, bool bUseTemporaryMemory )
|
||
|
{
|
||
|
//this is version 2 of the polyhedron generator, version 1 made individual polygons and joined points together, some guesswork is involved and it therefore isn't a solid method
|
||
|
//this version will start with a cube and hack away at it (retaining point connection information) to produce a polyhedron with no guesswork involved, this method should be rock solid
|
||
|
|
||
|
//the polygon clipping functions we're going to use want inward facing planes
|
||
|
const size_t kFltX4Align = sizeof( fltx4 ) - 1;
|
||
|
uint8 *pAlignedAlloc = (uint8 *)stackalloc( (sizeof( fltx4 ) * iPlaneCount) + kFltX4Align );
|
||
|
pAlignedAlloc = (uint8 *)(((size_t)(pAlignedAlloc + kFltX4Align)) & ~kFltX4Align);
|
||
|
fltx4 *pAlteredPlanes = (fltx4 *)pAlignedAlloc;
|
||
|
for( int i = 0; i != iPlaneCount; ++i )
|
||
|
{
|
||
|
SubFloat( pAlteredPlanes[i], 0 ) = -pOutwardFacingPlanes[(i * 4) + 0];
|
||
|
SubFloat( pAlteredPlanes[i], 1 ) = -pOutwardFacingPlanes[(i * 4) + 1];
|
||
|
SubFloat( pAlteredPlanes[i], 2 ) = -pOutwardFacingPlanes[(i * 4) + 2];
|
||
|
SubFloat( pAlteredPlanes[i], 3 ) = -pOutwardFacingPlanes[(i * 4) + 3];
|
||
|
}
|
||
|
|
||
|
//our first goal is to find the size of a cube big enough to encapsulate all points that will be in the final polyhedron
|
||
|
Vector vAABBMinsVec, vAABBMaxsVec;
|
||
|
if( FindConvexShapeLooseAABB( pAlteredPlanes, iPlaneCount, &vAABBMinsVec, &vAABBMaxsVec ) == false )
|
||
|
return NULL; //no shape to work with apparently
|
||
|
|
||
|
fltx4 vAABBMins;
|
||
|
SubFloat( vAABBMins, 0 ) = vAABBMinsVec.x;
|
||
|
SubFloat( vAABBMins, 1 ) = vAABBMinsVec.y;
|
||
|
SubFloat( vAABBMins, 2 ) = vAABBMinsVec.z;
|
||
|
SubFloat( vAABBMins, 3 ) = 1.0f;
|
||
|
|
||
|
fltx4 vAABBMaxs;
|
||
|
SubFloat( vAABBMaxs, 0 ) = vAABBMaxsVec.x;
|
||
|
SubFloat( vAABBMaxs, 1 ) = vAABBMaxsVec.y;
|
||
|
SubFloat( vAABBMaxs, 2 ) = vAABBMaxsVec.z;
|
||
|
SubFloat( vAABBMaxs, 3 ) = 1.0f;
|
||
|
|
||
|
#if defined( USE_WORLD_CENTERED_POSITIONS )
|
||
|
fltx4 vPointOffset = MulSIMD( AddSIMD( vAABBMins, vAABBMaxs ), ReplicateX4( -0.5f ) );
|
||
|
vPointOffset = FloorSIMD( vPointOffset );
|
||
|
SubFloat( vPointOffset, 3 ) = -1.0f;
|
||
|
#endif
|
||
|
|
||
|
for( int i = 0; i != iPlaneCount; ++i )
|
||
|
{
|
||
|
pAlteredPlanes[i] = NegSIMD( pAlteredPlanes[i] );
|
||
|
}
|
||
|
|
||
|
#if defined( USE_WORLD_CENTERED_POSITIONS )
|
||
|
//shift all the planes toward origin
|
||
|
for( int i = 0; i != iPlaneCount; ++i )
|
||
|
{
|
||
|
fltx4 vMul = MulSIMD( pAlteredPlanes[i], vPointOffset );
|
||
|
SubFloat( pAlteredPlanes[i], 3 ) = SubFloat( vMul, 0 ) + SubFloat( vMul, 1 ) + SubFloat( vMul, 2 ) - SubFloat( vMul, 3 );
|
||
|
}
|
||
|
//shift the AABB (and all starting points) toward the origin
|
||
|
vAABBMins = AddSIMD( vAABBMins, vPointOffset );
|
||
|
vAABBMaxs = AddSIMD( vAABBMaxs, vPointOffset );
|
||
|
fltx4 vResultOffset = NegSIMD( vPointOffset );
|
||
|
SubFloat( vResultOffset, 3 ) = 0.0f;
|
||
|
#else
|
||
|
fltx4 vResultOffset = LoadZeroSIMD();
|
||
|
#endif
|
||
|
|
||
|
//grow the bounding box to a larger size since it's probably inaccurate a bit
|
||
|
{
|
||
|
fltx4 vGrow = MulSIMD( SubSIMD( vAABBMaxs, vAABBMins ), Four_PointFives );
|
||
|
vGrow = AddSIMD( vGrow, ReplicateX4( 10.0f ) );
|
||
|
SubFloat( vGrow, 3 ) = 0.0f;
|
||
|
|
||
|
vAABBMins = SubSIMD( vAABBMins, vGrow );
|
||
|
vAABBMaxs = AddSIMD( vAABBMaxs, vGrow );
|
||
|
}
|
||
|
|
||
|
vAABBMins = FloorSIMD( vAABBMins );
|
||
|
vAABBMaxs = FloorSIMD( vAABBMaxs );
|
||
|
|
||
|
vAABBMinsVec.x = SubFloat( vAABBMins, 0 );
|
||
|
vAABBMinsVec.y = SubFloat( vAABBMins, 1 );
|
||
|
vAABBMinsVec.z = SubFloat( vAABBMins, 2 );
|
||
|
vAABBMaxsVec.x = SubFloat( vAABBMaxs, 0 );
|
||
|
vAABBMaxsVec.y = SubFloat( vAABBMaxs, 1 );
|
||
|
vAABBMaxsVec.z = SubFloat( vAABBMaxs, 2 );
|
||
|
|
||
|
//generate our starting cube using the 2x AABB so we can start hacking away at it
|
||
|
|
||
|
//create our starting box on the stack
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPolygonLL StartingPolygonList[6]; //6 polygons
|
||
|
GeneratePolyhedronFromPlanes_UnorderedLineLL StartingLineList[12]; //12 lines
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPointLL StartingPointList[8]; //8 points
|
||
|
|
||
|
for( int i = 0; i != 12; ++i )
|
||
|
{
|
||
|
StartingLineList[i].line.InitLineLinks();
|
||
|
}
|
||
|
|
||
|
|
||
|
//I had to work all this out on a whiteboard if it seems completely unintuitive.
|
||
|
{
|
||
|
SubFloat( StartingPointList[0].point.ptPosition, 0 ) = vAABBMinsVec.x;
|
||
|
SubFloat( StartingPointList[0].point.ptPosition, 1 ) = vAABBMinsVec.y;
|
||
|
SubFloat( StartingPointList[0].point.ptPosition, 2 ) = vAABBMinsVec.z;
|
||
|
SubFloat( StartingPointList[0].point.ptPosition, 3 ) = -1.0f;
|
||
|
STARTPOINTTOLINELINKS( 0, 0, 1, 4, 1, 3, 0 );
|
||
|
|
||
|
SubFloat( StartingPointList[1].point.ptPosition, 0 ) = vAABBMinsVec.x;
|
||
|
SubFloat( StartingPointList[1].point.ptPosition, 1 ) = vAABBMaxsVec.y;
|
||
|
SubFloat( StartingPointList[1].point.ptPosition, 2 ) = vAABBMinsVec.z;
|
||
|
SubFloat( StartingPointList[1].point.ptPosition, 3 ) = -1.0f;
|
||
|
STARTPOINTTOLINELINKS( 1, 0, 0, 1, 1, 5, 1 );
|
||
|
|
||
|
SubFloat( StartingPointList[2].point.ptPosition, 0 ) = vAABBMinsVec.x;
|
||
|
SubFloat( StartingPointList[2].point.ptPosition, 1 ) = vAABBMinsVec.y;
|
||
|
SubFloat( StartingPointList[2].point.ptPosition, 2 ) = vAABBMaxsVec.z;
|
||
|
SubFloat( StartingPointList[2].point.ptPosition, 3 ) = -1.0f;
|
||
|
STARTPOINTTOLINELINKS( 2, 4, 0, 8, 1, 11, 0 );
|
||
|
|
||
|
SubFloat( StartingPointList[3].point.ptPosition, 0 ) = vAABBMinsVec.x;
|
||
|
SubFloat( StartingPointList[3].point.ptPosition, 1 ) = vAABBMaxsVec.y;
|
||
|
SubFloat( StartingPointList[3].point.ptPosition, 2 ) = vAABBMaxsVec.z;
|
||
|
SubFloat( StartingPointList[3].point.ptPosition, 3 ) = -1.0f;
|
||
|
STARTPOINTTOLINELINKS( 3, 5, 0, 9, 1, 8, 0 );
|
||
|
|
||
|
SubFloat( StartingPointList[4].point.ptPosition, 0 ) = vAABBMaxsVec.x;
|
||
|
SubFloat( StartingPointList[4].point.ptPosition, 1 ) = vAABBMinsVec.y;
|
||
|
SubFloat( StartingPointList[4].point.ptPosition, 2 ) = vAABBMinsVec.z;
|
||
|
SubFloat( StartingPointList[4].point.ptPosition, 3 ) = -1.0f;
|
||
|
STARTPOINTTOLINELINKS( 4, 2, 0, 3, 1, 7, 1 );
|
||
|
|
||
|
SubFloat( StartingPointList[5].point.ptPosition, 0 ) = vAABBMaxsVec.x;
|
||
|
SubFloat( StartingPointList[5].point.ptPosition, 1 ) = vAABBMaxsVec.y;
|
||
|
SubFloat( StartingPointList[5].point.ptPosition, 2 ) = vAABBMinsVec.z;
|
||
|
SubFloat( StartingPointList[5].point.ptPosition, 3 ) = -1.0f;
|
||
|
STARTPOINTTOLINELINKS( 5, 1, 0, 2, 1, 6, 1 );
|
||
|
|
||
|
SubFloat( StartingPointList[6].point.ptPosition, 0 ) = vAABBMaxsVec.x;
|
||
|
SubFloat( StartingPointList[6].point.ptPosition, 1 ) = vAABBMinsVec.y;
|
||
|
SubFloat( StartingPointList[6].point.ptPosition, 2 ) = vAABBMaxsVec.z;
|
||
|
SubFloat( StartingPointList[6].point.ptPosition, 3 ) = -1.0f;
|
||
|
STARTPOINTTOLINELINKS( 6, 7, 0, 11, 1, 10, 0 );
|
||
|
|
||
|
SubFloat( StartingPointList[7].point.ptPosition, 0 ) = vAABBMaxsVec.x;
|
||
|
SubFloat( StartingPointList[7].point.ptPosition, 1 ) = vAABBMaxsVec.y;
|
||
|
SubFloat( StartingPointList[7].point.ptPosition, 2 ) = vAABBMaxsVec.z;
|
||
|
SubFloat( StartingPointList[7].point.ptPosition, 3 ) = -1.0f;
|
||
|
STARTPOINTTOLINELINKS( 7, 6, 0, 10, 1, 9, 0 );
|
||
|
|
||
|
STARTBOXCONNECTION( 0, 0, 1, 0, 5 );
|
||
|
STARTBOXCONNECTION( 1, 1, 5, 1, 5 );
|
||
|
STARTBOXCONNECTION( 2, 5, 4, 2, 5 );
|
||
|
STARTBOXCONNECTION( 3, 4, 0, 3, 5 );
|
||
|
STARTBOXCONNECTION( 4, 0, 2, 3, 0 );
|
||
|
STARTBOXCONNECTION( 5, 1, 3, 0, 1 );
|
||
|
STARTBOXCONNECTION( 6, 5, 7, 1, 2 );
|
||
|
STARTBOXCONNECTION( 7, 4, 6, 2, 3 );
|
||
|
STARTBOXCONNECTION( 8, 2, 3, 4, 0 );
|
||
|
STARTBOXCONNECTION( 9, 3, 7, 4, 1 );
|
||
|
STARTBOXCONNECTION( 10, 7, 6, 4, 2 );
|
||
|
STARTBOXCONNECTION( 11, 6, 2, 4, 3 );
|
||
|
|
||
|
|
||
|
STARTBOXCONNECTION( 0, 0, 1, 5, 0 );
|
||
|
STARTBOXCONNECTION( 1, 1, 5, 5, 1 );
|
||
|
STARTBOXCONNECTION( 2, 5, 4, 5, 2 );
|
||
|
STARTBOXCONNECTION( 3, 4, 0, 5, 3 );
|
||
|
STARTBOXCONNECTION( 4, 0, 2, 0, 3 );
|
||
|
STARTBOXCONNECTION( 5, 1, 3, 1, 0 );
|
||
|
STARTBOXCONNECTION( 6, 5, 7, 2, 1 );
|
||
|
STARTBOXCONNECTION( 7, 4, 6, 3, 2 );
|
||
|
STARTBOXCONNECTION( 8, 2, 3, 0, 4 );
|
||
|
STARTBOXCONNECTION( 9, 3, 7, 1, 4 );
|
||
|
STARTBOXCONNECTION( 10, 7, 6, 2, 4 );
|
||
|
STARTBOXCONNECTION( 11, 6, 2, 3, 4 );
|
||
|
|
||
|
StartingPolygonList[0].polygon.vSurfaceNormal.Init( -1.0f, 0.0f, 0.0f );
|
||
|
//StartingPolygonList[0].polygon.fNormalDist = -vAABBMinsVec.x;
|
||
|
StartingPolygonList[1].polygon.vSurfaceNormal.Init( 0.0f, 1.0f, 0.0f );
|
||
|
//StartingPolygonList[1].polygon.fNormalDist = vAABBMaxsVec.y;
|
||
|
StartingPolygonList[2].polygon.vSurfaceNormal.Init( 1.0f, 0.0f, 0.0f );
|
||
|
//StartingPolygonList[2].polygon.fNormalDist = vAABBMaxsVec.x;
|
||
|
StartingPolygonList[3].polygon.vSurfaceNormal.Init( 0.0f, -1.0f, 0.0f );
|
||
|
//StartingPolygonList[3].polygon.fNormalDist = -vAABBMinsVec.y;
|
||
|
StartingPolygonList[4].polygon.vSurfaceNormal.Init( 0.0f, 0.0f, 1.0f );
|
||
|
//StartingPolygonList[4].polygon.fNormalDist = vAABBMaxsVec.z;
|
||
|
StartingPolygonList[5].polygon.vSurfaceNormal.Init( 0.0f, 0.0f, -1.0f );
|
||
|
//StartingPolygonList[5].polygon.fNormalDist = -vAABBMinsVec.z;
|
||
|
|
||
|
|
||
|
STARTPOLYGONTOLINELINKS( 0, 0, 1, 5, 1, 8, 0, 4, 0 );
|
||
|
STARTPOLYGONTOLINELINKS( 1, 1, 1, 6, 1, 9, 0, 5, 0 );
|
||
|
STARTPOLYGONTOLINELINKS( 2, 2, 1, 7, 1, 10, 0, 6, 0 );
|
||
|
STARTPOLYGONTOLINELINKS( 3, 3, 1, 4, 1, 11, 0, 7, 0 );
|
||
|
STARTPOLYGONTOLINELINKS( 4, 8, 1, 9, 1, 10, 1, 11, 1 );
|
||
|
STARTPOLYGONTOLINELINKS( 5, 0, 0, 3, 0, 2, 0, 1, 0 );
|
||
|
|
||
|
|
||
|
{
|
||
|
StartingPolygonList[0].pNext = &StartingPolygonList[1];
|
||
|
StartingPolygonList[0].pPrev = NULL;
|
||
|
|
||
|
StartingPolygonList[1].pNext = &StartingPolygonList[2];
|
||
|
StartingPolygonList[1].pPrev = &StartingPolygonList[0];
|
||
|
|
||
|
StartingPolygonList[2].pNext = &StartingPolygonList[3];
|
||
|
StartingPolygonList[2].pPrev = &StartingPolygonList[1];
|
||
|
|
||
|
StartingPolygonList[3].pNext = &StartingPolygonList[4];
|
||
|
StartingPolygonList[3].pPrev = &StartingPolygonList[2];
|
||
|
|
||
|
StartingPolygonList[4].pNext = &StartingPolygonList[5];
|
||
|
StartingPolygonList[4].pPrev = &StartingPolygonList[3];
|
||
|
|
||
|
StartingPolygonList[5].pNext = NULL;
|
||
|
StartingPolygonList[5].pPrev = &StartingPolygonList[4];
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
{
|
||
|
StartingLineList[0].pNext = &StartingLineList[1];
|
||
|
StartingLineList[0].pPrev = NULL;
|
||
|
|
||
|
StartingLineList[1].pNext = &StartingLineList[2];
|
||
|
StartingLineList[1].pPrev = &StartingLineList[0];
|
||
|
|
||
|
StartingLineList[2].pNext = &StartingLineList[3];
|
||
|
StartingLineList[2].pPrev = &StartingLineList[1];
|
||
|
|
||
|
StartingLineList[3].pNext = &StartingLineList[4];
|
||
|
StartingLineList[3].pPrev = &StartingLineList[2];
|
||
|
|
||
|
StartingLineList[4].pNext = &StartingLineList[5];
|
||
|
StartingLineList[4].pPrev = &StartingLineList[3];
|
||
|
|
||
|
StartingLineList[5].pNext = &StartingLineList[6];
|
||
|
StartingLineList[5].pPrev = &StartingLineList[4];
|
||
|
|
||
|
StartingLineList[6].pNext = &StartingLineList[7];
|
||
|
StartingLineList[6].pPrev = &StartingLineList[5];
|
||
|
|
||
|
StartingLineList[7].pNext = &StartingLineList[8];
|
||
|
StartingLineList[7].pPrev = &StartingLineList[6];
|
||
|
|
||
|
StartingLineList[8].pNext = &StartingLineList[9];
|
||
|
StartingLineList[8].pPrev = &StartingLineList[7];
|
||
|
|
||
|
StartingLineList[9].pNext = &StartingLineList[10];
|
||
|
StartingLineList[9].pPrev = &StartingLineList[8];
|
||
|
|
||
|
StartingLineList[10].pNext = &StartingLineList[11];
|
||
|
StartingLineList[10].pPrev = &StartingLineList[9];
|
||
|
|
||
|
StartingLineList[11].pNext = NULL;
|
||
|
StartingLineList[11].pPrev = &StartingLineList[10];
|
||
|
}
|
||
|
|
||
|
{
|
||
|
StartingPointList[0].pNext = &StartingPointList[1];
|
||
|
StartingPointList[0].pPrev = NULL;
|
||
|
|
||
|
StartingPointList[1].pNext = &StartingPointList[2];
|
||
|
StartingPointList[1].pPrev = &StartingPointList[0];
|
||
|
|
||
|
StartingPointList[2].pNext = &StartingPointList[3];
|
||
|
StartingPointList[2].pPrev = &StartingPointList[1];
|
||
|
|
||
|
StartingPointList[3].pNext = &StartingPointList[4];
|
||
|
StartingPointList[3].pPrev = &StartingPointList[2];
|
||
|
|
||
|
StartingPointList[4].pNext = &StartingPointList[5];
|
||
|
StartingPointList[4].pPrev = &StartingPointList[3];
|
||
|
|
||
|
StartingPointList[5].pNext = &StartingPointList[6];
|
||
|
StartingPointList[5].pPrev = &StartingPointList[4];
|
||
|
|
||
|
StartingPointList[6].pNext = &StartingPointList[7];
|
||
|
StartingPointList[6].pPrev = &StartingPointList[5];
|
||
|
|
||
|
StartingPointList[7].pNext = NULL;
|
||
|
StartingPointList[7].pPrev = &StartingPointList[6];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CPolyhedron *pRetVal = ClipLinkedGeometry( StartingPolygonList, StartingLineList, StartingPointList, 8, pAlteredPlanes, iPlaneCount, fOnPlaneEpsilon, bUseTemporaryMemory, vResultOffset );
|
||
|
|
||
|
#if defined( DEBUG_POLYHEDRON_CONVERSION )
|
||
|
//last bit of debugging from whatever outside source wants this stupid thing
|
||
|
if( (g_pPolyhedronCarvingDebugStepCallback != NULL) && (pRetVal != NULL) )
|
||
|
{
|
||
|
AssertMsg( g_pPolyhedronCarvingDebugStepCallback( pRetVal ), "Outside conversion failed" );
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
return pRetVal;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
#ifdef DBGFLAG_ASSERT
|
||
|
void DumpAABBToGLView( const Vector &vCenter, const Vector &vExtents, const Vector &vColor, FILE *pFile )
|
||
|
{
|
||
|
#ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS
|
||
|
Vector vMins = vCenter - vExtents;
|
||
|
Vector vMaxs = vCenter + vExtents;
|
||
|
|
||
|
//x min side
|
||
|
fprintf( pFile, "4\n" );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMins.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMaxs.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMaxs.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMins.z, vColor.x, vColor.y, vColor.z );
|
||
|
|
||
|
fprintf( pFile, "4\n" );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMins.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMaxs.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMaxs.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMins.z, vColor.x, vColor.y, vColor.z );
|
||
|
|
||
|
//x max side
|
||
|
fprintf( pFile, "4\n" );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMins.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMaxs.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMaxs.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMins.z, vColor.x, vColor.y, vColor.z );
|
||
|
|
||
|
fprintf( pFile, "4\n" );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMins.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMaxs.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMaxs.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMins.z, vColor.x, vColor.y, vColor.z );
|
||
|
|
||
|
|
||
|
//y min side
|
||
|
fprintf( pFile, "4\n" );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMins.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMaxs.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMaxs.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMins.z, vColor.x, vColor.y, vColor.z );
|
||
|
|
||
|
fprintf( pFile, "4\n" );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMins.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMaxs.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMaxs.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMins.z, vColor.x, vColor.y, vColor.z );
|
||
|
|
||
|
|
||
|
|
||
|
//y max side
|
||
|
fprintf( pFile, "4\n" );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMins.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMaxs.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMaxs.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMins.z, vColor.x, vColor.y, vColor.z );
|
||
|
|
||
|
fprintf( pFile, "4\n" );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMins.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMaxs.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMaxs.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMins.z, vColor.x, vColor.y, vColor.z );
|
||
|
|
||
|
|
||
|
|
||
|
//z min side
|
||
|
fprintf( pFile, "4\n" );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMins.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMins.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMins.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMins.z, vColor.x, vColor.y, vColor.z );
|
||
|
|
||
|
fprintf( pFile, "4\n" );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMins.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMins.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMins.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMins.z, vColor.x, vColor.y, vColor.z );
|
||
|
|
||
|
|
||
|
//z max side
|
||
|
fprintf( pFile, "4\n" );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMaxs.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMaxs.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMaxs.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMaxs.z, vColor.x, vColor.y, vColor.z );
|
||
|
|
||
|
fprintf( pFile, "4\n" );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMins.y, vMaxs.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMaxs.x, vMaxs.y, vMaxs.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMaxs.y, vMaxs.z, vColor.x, vColor.y, vColor.z );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vMins.x, vMins.y, vMaxs.z, vColor.x, vColor.y, vColor.z );
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void DumpLineToGLView( const Vector &vPoint1, const Vector &vColor1, const Vector &vPoint2, const Vector &vColor2, float fThickness, FILE *pFile )
|
||
|
{
|
||
|
#ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS
|
||
|
Vector vDirection = vPoint2 - vPoint1;
|
||
|
vDirection.NormalizeInPlace();
|
||
|
|
||
|
Vector vPseudoPerpandicular = vec3_origin;
|
||
|
|
||
|
if( vDirection.x != 0.0f )
|
||
|
vPseudoPerpandicular.z = 1.0f;
|
||
|
else
|
||
|
vPseudoPerpandicular.x = 1.0f;
|
||
|
|
||
|
Vector vWidth = vDirection.Cross( vPseudoPerpandicular );
|
||
|
vWidth.NormalizeInPlace();
|
||
|
|
||
|
Vector vHeight = vDirection.Cross( vWidth );
|
||
|
vHeight.NormalizeInPlace();
|
||
|
|
||
|
fThickness *= 0.5f; //we use half thickness in both directions
|
||
|
vDirection *= fThickness;
|
||
|
vWidth *= fThickness;
|
||
|
vHeight *= fThickness;
|
||
|
|
||
|
Vector vLinePoints[8];
|
||
|
vLinePoints[0] = vPoint1 - vDirection - vWidth - vHeight;
|
||
|
vLinePoints[1] = vPoint1 - vDirection - vWidth + vHeight;
|
||
|
vLinePoints[2] = vPoint1 - vDirection + vWidth - vHeight;
|
||
|
vLinePoints[3] = vPoint1 - vDirection + vWidth + vHeight;
|
||
|
|
||
|
vLinePoints[4] = vPoint2 + vDirection - vWidth - vHeight;
|
||
|
vLinePoints[5] = vPoint2 + vDirection - vWidth + vHeight;
|
||
|
vLinePoints[6] = vPoint2 + vDirection + vWidth - vHeight;
|
||
|
vLinePoints[7] = vPoint2 + vDirection + vWidth + vHeight;
|
||
|
|
||
|
const Vector *pLineColors[8] = { &vColor1, &vColor1, &vColor1, &vColor1, &vColor2, &vColor2, &vColor2, &vColor2 };
|
||
|
|
||
|
|
||
|
#define DPTGLV_LINE_WRITEPOINT(index) fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vLinePoints[index].x, vLinePoints[index].y, vLinePoints[index].z, pLineColors[index]->x, pLineColors[index]->y, pLineColors[index]->z );
|
||
|
#define DPTGLV_LINE_DOUBLESIDEDQUAD(index1,index2,index3,index4)\
|
||
|
fprintf( pFile, "4\n" );\
|
||
|
DPTGLV_LINE_WRITEPOINT(index1);\
|
||
|
DPTGLV_LINE_WRITEPOINT(index2);\
|
||
|
DPTGLV_LINE_WRITEPOINT(index3);\
|
||
|
DPTGLV_LINE_WRITEPOINT(index4);\
|
||
|
fprintf( pFile, "4\n" );\
|
||
|
DPTGLV_LINE_WRITEPOINT(index4);\
|
||
|
DPTGLV_LINE_WRITEPOINT(index3);\
|
||
|
DPTGLV_LINE_WRITEPOINT(index2);\
|
||
|
DPTGLV_LINE_WRITEPOINT(index1);
|
||
|
|
||
|
|
||
|
DPTGLV_LINE_DOUBLESIDEDQUAD(0,4,6,2);
|
||
|
DPTGLV_LINE_DOUBLESIDEDQUAD(3,7,5,1);
|
||
|
DPTGLV_LINE_DOUBLESIDEDQUAD(1,5,4,0);
|
||
|
DPTGLV_LINE_DOUBLESIDEDQUAD(2,6,7,3);
|
||
|
DPTGLV_LINE_DOUBLESIDEDQUAD(0,2,3,1);
|
||
|
DPTGLV_LINE_DOUBLESIDEDQUAD(5,7,6,4);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
#ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS
|
||
|
|
||
|
void DumpWorkingStatePolygons( GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pPolygons, bool bIsDeadList, const VMatrix *pTransform, FILE *pFile )
|
||
|
{
|
||
|
while( pPolygons )
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_Polygon *pPoly = &pPolygons->polygon;
|
||
|
|
||
|
#ifdef DBGFLAG_ASSERT
|
||
|
if( pPoly->pLines == (void *)0xCCCCCCCC )
|
||
|
{
|
||
|
//this function gets called when something is already wrong. No need to crash instead of reporting something useful
|
||
|
pPolygons = pPolygons->pNext;
|
||
|
continue;
|
||
|
}
|
||
|
#endif
|
||
|
if( (pPoly->pLines == NULL) || (pPoly->pLines == pPoly->pLines->pNext) )
|
||
|
{
|
||
|
pPolygons = pPolygons->pNext;
|
||
|
continue; //less than 3 points in the polygon so far, undrawable
|
||
|
}
|
||
|
|
||
|
Vector vColor; //dead is dark grey, missing a side is red, complete is green, new is blue
|
||
|
if( pPoly->debugdata.vWorkingStateColorOverride != vec3_origin )
|
||
|
{
|
||
|
vColor = pPoly->debugdata.vWorkingStateColorOverride;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if( bIsDeadList )
|
||
|
{
|
||
|
vColor = Vector( 0.25f, 0.25f, 0.25f );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/*if(pPoly->bMissingASide)
|
||
|
{
|
||
|
vColor = Vector( 1.0f, 0.0f, 0.0f );
|
||
|
|
||
|
Vector vPatchLineColor( 1.0f, 0.0f, 1.0f );
|
||
|
fltx4 f4P1 = pPoly->pLines->pLine->pPoints[pPoly->pLines->iReferenceIndex]->ptPosition;
|
||
|
fltx4 f4P2 = pPoly->pLines->pNext->pLine->pPoints[1 - pPoly->pLines->pNext->iReferenceIndex]->ptPosition;
|
||
|
Vector vP1( SubFloat( f4P1, 0 ), SubFloat( f4P1, 1 ), SubFloat( f4P1, 2 ) );
|
||
|
Vector vP2( SubFloat( f4P2, 0 ), SubFloat( f4P2, 1 ), SubFloat( f4P2, 2 ) );
|
||
|
Vector vP1 = (*pTransform) * vP1;
|
||
|
Vector vP2 = (*pTransform) * vP2;
|
||
|
DumpLineToGLView( vP1, vPatchLineColor, vP2, vPatchLineColor, 0.1f, pFile );
|
||
|
}
|
||
|
else*/
|
||
|
{
|
||
|
if( pPoly->debugdata.bIsNew )
|
||
|
vColor = Vector( 0.0f, 0.0f, 1.0f );
|
||
|
else
|
||
|
vColor = Vector( 0.0f, 1.0f, 0.0f );
|
||
|
}
|
||
|
}
|
||
|
vColor *= 0.33f;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pLineWalk = pPoly->pLines;
|
||
|
GeneratePolyhedronFromPlanes_LineLL *pLineWalkStart = pLineWalk;
|
||
|
int iVertexCount = 1;
|
||
|
do
|
||
|
{
|
||
|
++iVertexCount;
|
||
|
pLineWalk = pLineWalk->pNext;
|
||
|
} while(pLineWalk != pLineWalkStart);
|
||
|
|
||
|
fprintf( pFile, "%i\n", iVertexCount );
|
||
|
fltx4 f4vertex = pLineWalk->pLine->pPoints[1 - pLineWalk->iReferenceIndex]->ptPosition;
|
||
|
Vector vertex( SubFloat( f4vertex, 0 ), SubFloat( f4vertex, 1 ), SubFloat( f4vertex, 2 ) );
|
||
|
vertex = (*pTransform) * vertex;
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vertex.x, vertex.y, vertex.z, vColor.x, vColor.y, vColor.z );
|
||
|
do
|
||
|
{
|
||
|
f4vertex = pLineWalk->pLine->pPoints[pLineWalk->iReferenceIndex]->ptPosition;
|
||
|
vertex.Init( SubFloat( f4vertex, 0 ), SubFloat( f4vertex, 1 ), SubFloat( f4vertex, 2 ) );
|
||
|
vertex = (*pTransform) * vertex;
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vertex.x, vertex.y, vertex.z, vColor.x, vColor.y, vColor.z );
|
||
|
pLineWalk = pLineWalk->pNext;
|
||
|
} while(pLineWalk != pLineWalkStart);
|
||
|
|
||
|
pPolygons = pPolygons->pNext;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void DumpWorkingStateLines( GeneratePolyhedronFromPlanes_UnorderedLineLL *pLines, const VMatrix *pTransform, FILE *pFile )
|
||
|
{
|
||
|
while( pLines )
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_Line *pLine = &pLines->line;
|
||
|
|
||
|
if( (pLine->pPoints[0] == NULL) || (pLine->pPoints[1] == NULL) )
|
||
|
{
|
||
|
pLines = pLines->pNext;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
#ifdef DBGFLAG_ASSERT
|
||
|
if( (pLine->pPoints[0] == (void *)0xCCCCCCCC) || (pLine->pPoints[1] == (void *)0xCCCCCCCC) )
|
||
|
{
|
||
|
//this function gets called when something is already wrong. No need to crash instead of reporting something useful
|
||
|
pLines = pLines->pNext;
|
||
|
continue;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
Vector vColor; //dead is dark grey, cut is red, complete is green, re-lengthened is green+blue, onplane is yellow, new is blue
|
||
|
if( pLine->debugdata.vWorkingStateColorOverride != vec3_origin )
|
||
|
{
|
||
|
vColor = pLine->debugdata.vWorkingStateColorOverride;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if( pLine->debugdata.bIsNew )
|
||
|
{
|
||
|
if( pLine->planarity == LINE_DEAD )
|
||
|
{
|
||
|
//created and then ditched
|
||
|
vColor = Vector( 0.5f, 0.5f, 1.0f );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
vColor = Vector( 0.0f, 0.0f, 1.0f );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
switch( pLine->planarity )
|
||
|
{
|
||
|
case LINE_ONPLANE:
|
||
|
vColor = Vector( 1.0f, 1.0f, 0.0f );
|
||
|
break;
|
||
|
case LINE_ALIVE:
|
||
|
/*if( pLine->debugdata.initialPlanarity & LINE_DEAD )
|
||
|
{
|
||
|
vColor = Vector( 0.0f, 1.0f, 1.0f ); //re-lengthened
|
||
|
}
|
||
|
else*/
|
||
|
{
|
||
|
vColor = Vector( 0.0f, 1.0f, 0.0f ); //always alive
|
||
|
}
|
||
|
break;
|
||
|
case LINE_DEAD:
|
||
|
vColor = Vector( 0.25f, 0.25f, 0.25f );
|
||
|
break;
|
||
|
case LINE_CUT:
|
||
|
vColor = Vector( 1.0f, 0.0f, 0.0f );
|
||
|
break;
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fltx4 f4Pos0 = pLine->pPoints[0]->ptPosition;
|
||
|
Vector vPos0( SubFloat( f4Pos0, 0 ), SubFloat( f4Pos0, 1 ), SubFloat( f4Pos0, 2 ) );
|
||
|
fltx4 f4Pos1 = pLine->pPoints[1]->ptPosition;
|
||
|
Vector vPos1( SubFloat( f4Pos1, 0 ), SubFloat( f4Pos1, 1 ), SubFloat( f4Pos1, 2 ) );
|
||
|
DumpLineToGLView( (*pTransform) * vPos0, vColor, (*pTransform) * vPos1, vColor, 0.1f, pFile );
|
||
|
|
||
|
pLines = pLines->pNext;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void DumpWorkingStatePoints( GeneratePolyhedronFromPlanes_UnorderedPointLL *pPoints, const VMatrix *pTransform, FILE *pFile )
|
||
|
{
|
||
|
while( pPoints )
|
||
|
{
|
||
|
GeneratePolyhedronFromPlanes_Point *pPoint = &pPoints->point;
|
||
|
|
||
|
Vector vColor; //dead is red, alive is green, onplane is red+green, new is blue
|
||
|
if( pPoint->debugdata.vWorkingStateColorOverride != vec3_origin )
|
||
|
{
|
||
|
vColor = pPoint->debugdata.vWorkingStateColorOverride;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if( pPoint->debugdata.bIsNew )
|
||
|
{
|
||
|
if( pPoint->planarity == POINT_DEAD )
|
||
|
{
|
||
|
//created and then merged away
|
||
|
vColor = Vector( 0.5f, 0.5f, 1.0f );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
vColor = Vector( 0.0f, 0.0f, 1.0f );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
switch( pPoint->planarity )
|
||
|
{
|
||
|
case POINT_ONPLANE:
|
||
|
vColor = Vector( 1.0f, 1.0f, 0.0f );
|
||
|
break;
|
||
|
case POINT_ALIVE:
|
||
|
vColor = Vector( 0.0f, 1.0f, 0.0f ); //always alive
|
||
|
break;
|
||
|
case POINT_DEAD:
|
||
|
vColor = Vector( 1.0f, 0.0f, 0.0f );
|
||
|
break;
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fltx4 f4Pos = pPoint->ptPosition;
|
||
|
Vector vPos( SubFloat( f4Pos, 0 ), SubFloat( f4Pos, 1 ), SubFloat( f4Pos, 2 ) );
|
||
|
DumpAABBToGLView( (*pTransform) * vPos, Vector( 0.15f, 0.15f, 0.15f ), vColor, pFile );
|
||
|
|
||
|
pPoints = pPoints->pNext;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//dumps color coded information about a polyhedron that's in the middle of a cut (debugging only)
|
||
|
void DumpWorkingStatePolyhedron( GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pAllPolygons, GeneratePolyhedronFromPlanes_UnorderedPolygonLL *pDeadPolygons,
|
||
|
GeneratePolyhedronFromPlanes_UnorderedLineLL *pAllLines, GeneratePolyhedronFromPlanes_UnorderedLineLL *pDeadLines,
|
||
|
GeneratePolyhedronFromPlanes_UnorderedPointLL *pAllPoints, GeneratePolyhedronFromPlanes_UnorderedPointLL *pDeadPoints,
|
||
|
const char *pFilename, const VMatrix *pTransform )
|
||
|
{
|
||
|
if( pTransform == NULL )
|
||
|
pTransform = &s_matIdentity;
|
||
|
|
||
|
printf("Writing %s...\n", pFilename );
|
||
|
|
||
|
FILE *pFile = fopen( pFilename, "wb" );
|
||
|
if( pFile == NULL )
|
||
|
return;
|
||
|
|
||
|
DumpWorkingStatePolygons( pAllPolygons, false, pTransform, pFile );
|
||
|
//DumpWorkingStatePolygons( pDeadPolygons, true, pTransform, pFile ); //they're undrawable, they have less than 3 lines
|
||
|
|
||
|
DumpWorkingStateLines( pAllLines, pTransform, pFile );
|
||
|
DumpWorkingStateLines( pDeadLines, pTransform, pFile );
|
||
|
|
||
|
DumpWorkingStatePoints( pAllPoints, pTransform, pFile );
|
||
|
DumpWorkingStatePoints( pDeadPoints, pTransform, pFile );
|
||
|
|
||
|
fclose( pFile );
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
void DumpPolyhedronToGLView( const CPolyhedron *pPolyhedron, const char *pFilename, const VMatrix *pTransform, const char *szfileOpenOptions )
|
||
|
{
|
||
|
#ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS
|
||
|
Assert( pPolyhedron && (pPolyhedron->iVertexCount > 2) );
|
||
|
|
||
|
if( pTransform == NULL )
|
||
|
pTransform = &s_matIdentity;
|
||
|
|
||
|
printf("Writing %s...\n", pFilename );
|
||
|
|
||
|
FILE *pFile = fopen( pFilename, szfileOpenOptions );
|
||
|
|
||
|
const Vector vOne( 1.0f, 1.0f, 1.0f );
|
||
|
|
||
|
//randomizing an array of colors to help spot shared/unshared vertices
|
||
|
Vector *pColors = (Vector *)stackalloc( sizeof( Vector ) * pPolyhedron->iVertexCount );
|
||
|
int counter;
|
||
|
for( counter = 0; counter != pPolyhedron->iVertexCount; ++counter )
|
||
|
{
|
||
|
pColors[counter].Init( rand()/32768.0f, rand()/32768.0f, rand()/32768.0f );
|
||
|
}
|
||
|
|
||
|
Vector *pTransformedPoints = (Vector *)stackalloc( pPolyhedron->iVertexCount * sizeof( Vector ) );
|
||
|
for ( counter = 0; counter != pPolyhedron->iVertexCount; ++counter )
|
||
|
{
|
||
|
pTransformedPoints[counter] = (*pTransform) * pPolyhedron->pVertices[counter];
|
||
|
}
|
||
|
|
||
|
for ( counter = 0; counter != pPolyhedron->iPolygonCount; ++counter )
|
||
|
{
|
||
|
fprintf( pFile, "%i\n", pPolyhedron->pPolygons[counter].iIndexCount );
|
||
|
int counter2;
|
||
|
for( counter2 = 0; counter2 != pPolyhedron->pPolygons[counter].iIndexCount; ++counter2 )
|
||
|
{
|
||
|
Polyhedron_IndexedLineReference_t *pLineReference = &pPolyhedron->pIndices[pPolyhedron->pPolygons[counter].iFirstIndex + counter2];
|
||
|
|
||
|
Vector *pVertex = &pTransformedPoints[pPolyhedron->pLines[pLineReference->iLineIndex].iPointIndices[pLineReference->iEndPointIndex]];
|
||
|
Vector *pColor = &pColors[pPolyhedron->pLines[pLineReference->iLineIndex].iPointIndices[pLineReference->iEndPointIndex]];
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n",pVertex->x, pVertex->y, pVertex->z, pColor->x, pColor->y, pColor->z );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for( counter = 0; counter != pPolyhedron->iLineCount; ++counter )
|
||
|
{
|
||
|
Vector vPoints[2] = { pTransformedPoints[pPolyhedron->pLines[counter].iPointIndices[0]], pTransformedPoints[pPolyhedron->pLines[counter].iPointIndices[1]] };
|
||
|
const float fShortenEnds = 0.0f;
|
||
|
if( fShortenEnds != 0.0f )
|
||
|
{
|
||
|
Vector vDiff = vPoints[0] - vPoints[1];
|
||
|
vDiff.NormalizeInPlace();
|
||
|
vPoints[0] -= vDiff * fShortenEnds;
|
||
|
vPoints[1] += vDiff * fShortenEnds;
|
||
|
}
|
||
|
|
||
|
DumpLineToGLView( vPoints[0], vOne - pColors[pPolyhedron->pLines[counter].iPointIndices[0]],
|
||
|
vPoints[1], vOne - pColors[pPolyhedron->pLines[counter].iPointIndices[1]],
|
||
|
0.1f, pFile );
|
||
|
}
|
||
|
|
||
|
for( counter = 0; counter != pPolyhedron->iVertexCount; ++counter )
|
||
|
{
|
||
|
const Vector vPointHalfSize(0.15f, 0.15f, 0.15f );
|
||
|
DumpAABBToGLView( pTransformedPoints[counter], vPointHalfSize, pColors[counter], pFile );
|
||
|
}
|
||
|
|
||
|
fclose( pFile );
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
void DumpPlaneToGlView( const float *pPlane, float fGrayScale, const char *pszFileName, const VMatrix *pTransform )
|
||
|
{
|
||
|
#ifdef ENABLE_DEBUG_POLYHEDRON_DUMPS
|
||
|
if( pTransform == NULL )
|
||
|
pTransform = &s_matIdentity;
|
||
|
|
||
|
FILE *pFile = fopen( pszFileName, "ab" );
|
||
|
|
||
|
//transform the plane
|
||
|
Vector vNormal = pTransform->ApplyRotation( *(Vector *)pPlane );
|
||
|
float fDist = pPlane[3] * vNormal.NormalizeInPlace(); //possible scaling going on
|
||
|
fDist += vNormal.Dot( pTransform->GetTranslation() );
|
||
|
|
||
|
Vector vPlaneVerts[4];
|
||
|
|
||
|
PolyFromPlane( vPlaneVerts, vNormal, fDist, 100000.0f );
|
||
|
|
||
|
fprintf( pFile, "4\n" );
|
||
|
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vPlaneVerts[0].x, vPlaneVerts[0].y, vPlaneVerts[0].z, fGrayScale, fGrayScale, fGrayScale );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vPlaneVerts[1].x, vPlaneVerts[1].y, vPlaneVerts[1].z, fGrayScale, fGrayScale, fGrayScale );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vPlaneVerts[2].x, vPlaneVerts[2].y, vPlaneVerts[2].z, fGrayScale, fGrayScale, fGrayScale );
|
||
|
fprintf( pFile, "%6.3f %6.3f %6.3f %.2f %.2f %.2f\n", vPlaneVerts[3].x, vPlaneVerts[3].y, vPlaneVerts[3].z, fGrayScale, fGrayScale, fGrayScale );
|
||
|
|
||
|
fclose( pFile );
|
||
|
#endif
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|