Serious-Engine/Sources/Engine/Math/ObjectSector.cpp
2016-03-11 15:57:17 +02:00

1936 lines
60 KiB
C++

/* Copyright (c) 2002-2012 Croteam Ltd. All rights reserved. */
#include "stdh.h"
#include <Engine/Math/Object3D.h>
#include <Engine/Templates/BSP_internal.h>
#include <Engine/Templates/StaticArray.cpp>
#include <Engine/Templates/DynamicArray.cpp>
#include <Engine/Templates/DynamicContainer.cpp>
// define this for full debugging of all algorithms
#define FULL_DEBUG
// epsilon value used for vertex comparison
//#define VTX_EPSILON (0.015625f) // 1/2^6 ~= 1.5 cm
#define VTX_EPSILON DOUBLE((1.0/65536.0)/16.0*mth_fCSGEpsilon) // 1/2^16
//#define VTX_EPSILON DOUBLE(0.00390625) // 1/2^8
// grid for snapping vertices
#define VTX_SNAP (VTX_EPSILON/2.0)
// epsilon value used for plane comparison
#define PLX_EPSILON (VTX_EPSILON/16.0*mth_fCSGEpsilon)
// grid for snapping planes
#define PLX_SNAP (PLX_EPSILON/2.0)
// epsilon value used for edge line comparison
//#define EDX_EPSILON (1E-3f) // 1E-3 == 1 mm
//#define EDX_EPSILON (0.015625f) // 1/2^6 ~= 1.5 cm
//#define EDX_EPSILON DOUBLE(1.0/65536.0) // 1/2^16
#define EDX_EPSILON DOUBLE(0.00390625*mth_fCSGEpsilon) // 1/2^8
// use O(nlogn) instead O(n2) algorithms for object optimization
extern INDEX wld_bFastObjectOptimization = 1.0f;
extern FLOAT mth_fCSGEpsilon = 1.0f;
/*
* Compare two vertices.
*/
inline int CompareVertices(const CObjectVertex &vx0, const CObjectVertex &vx1)
{
if (vx0(1)-vx1(1) < -VTX_EPSILON) return -1;
else if (vx0(1)-vx1(1) > +VTX_EPSILON) return 1;
else if (vx0(2)-vx1(2) < -VTX_EPSILON) return -1;
else if (vx0(2)-vx1(2) > +VTX_EPSILON) return 1;
else if (vx0(3)-vx1(3) < -VTX_EPSILON) return -1;
else if (vx0(3)-vx1(3) > +VTX_EPSILON) return 1;
else return 0;
}
/*
* Compare two vertices for quick-sort.
*/
static int qsort_CompareVertices(const void *pvVertex0, const void *pvVertex1)
{
CObjectVertex &vx0 = **(CObjectVertex **)pvVertex0;
CObjectVertex &vx1 = **(CObjectVertex **)pvVertex1;
return CompareVertices(vx0, vx1);
}
/*
* Compare two along line vertices.
*/
static INDEX sortvertices_iMaxAxis;
inline int CompareVerticesAlongLine(const CObjectVertex &vx0, const CObjectVertex &vx1)
{
if (vx0(sortvertices_iMaxAxis)-vx1(sortvertices_iMaxAxis) < -VTX_EPSILON) return -1;
else if (vx0(sortvertices_iMaxAxis)-vx1(sortvertices_iMaxAxis) > +VTX_EPSILON) return +1;
else return 0;
}
/*
* Compare two vertices along a line for quick-sort.
*/
static int qsort_CompareVerticesAlongLine(const void *pvVertex0, const void *pvVertex1)
{
CObjectVertex &vx0 = **(CObjectVertex **)pvVertex0;
CObjectVertex &vx1 = **(CObjectVertex **)pvVertex1;
return CompareVerticesAlongLine(vx0, vx1);
}
/*
* Compare two vertices along a line for quick-sort - reversely.
*/
static int qsort_CompareVerticesAlongLineReversely(const void *pvVertex0, const void *pvVertex1)
{
CObjectVertex &vx0 = **(CObjectVertex **)pvVertex0;
CObjectVertex &vx1 = **(CObjectVertex **)pvVertex1;
return -CompareVerticesAlongLine(vx0, vx1);
}
/*
* Compare two planes.
*/
inline int ComparePlanes(const CObjectPlane &pl0, const CObjectPlane &pl1)
{
if (pl0(1)-pl1(1) < -PLX_EPSILON) return -1;
else if (pl0(1)-pl1(1) > +PLX_EPSILON) return +1;
else if (pl0(2)-pl1(2) < -PLX_EPSILON) return -1;
else if (pl0(2)-pl1(2) > +PLX_EPSILON) return +1;
else if (pl0(3)-pl1(3) < -PLX_EPSILON) return -1;
else if (pl0(3)-pl1(3) > +PLX_EPSILON) return +1;
else if (pl0.Distance()-pl1.Distance() < -PLX_EPSILON) return -1;
else if (pl0.Distance()-pl1.Distance() > +PLX_EPSILON) return +1;
else return 0;
}
/*
* Compare two planes for quick-sort.
*/
static int qsort_ComparePlanes(const void *pvPlane0, const void *pvPlane1)
{
CObjectPlane &pl0 = **(CObjectPlane **)pvPlane0;
CObjectPlane &pl1 = **(CObjectPlane **)pvPlane1;
return ComparePlanes(pl0, pl1);
}
/*
* Compare two edges.
*/
inline int CompareEdges(const CObjectEdge &ed0, const CObjectEdge &ed1)
{
if (ed0.oed_Vertex0<ed1.oed_Vertex0) return -1;
else if (ed0.oed_Vertex0>ed1.oed_Vertex0) return +1;
else if (ed0.oed_Vertex1<ed1.oed_Vertex1) return -1;
else if (ed0.oed_Vertex1>ed1.oed_Vertex1) return +1;
else return 0;
}
/*
* Compare two edges for quick-sort.
*/
static int qsort_CompareEdges(const void *pvEdge0, const void *pvEdge1)
{
CObjectEdge &ed0 = **(CObjectEdge **)pvEdge0;
CObjectEdge &ed1 = **(CObjectEdge **)pvEdge1;
return CompareEdges(ed0, ed1);
}
/*
* Compare two edge lines.
*/
inline int CompareEdgeLines(const CEdgeEx &edx0, const CEdgeEx &edx1)
{
if (edx0.edx_vDirection(1)-edx1.edx_vDirection(1) < -EDX_EPSILON) return -1;
else if (edx0.edx_vDirection(1)-edx1.edx_vDirection(1) > +EDX_EPSILON) return +1;
else if (edx0.edx_vDirection(2)-edx1.edx_vDirection(2) < -EDX_EPSILON) return -1;
else if (edx0.edx_vDirection(2)-edx1.edx_vDirection(2) > +EDX_EPSILON) return +1;
else if (edx0.edx_vDirection(3)-edx1.edx_vDirection(3) < -EDX_EPSILON) return -1;
else if (edx0.edx_vDirection(3)-edx1.edx_vDirection(3) > +EDX_EPSILON) return +1;
else if (edx0.edx_vReferencePoint(1)-edx1.edx_vReferencePoint(1) < -EDX_EPSILON) return -1;
else if (edx0.edx_vReferencePoint(1)-edx1.edx_vReferencePoint(1) > +EDX_EPSILON) return +1;
else if (edx0.edx_vReferencePoint(2)-edx1.edx_vReferencePoint(2) < -EDX_EPSILON) return -1;
else if (edx0.edx_vReferencePoint(2)-edx1.edx_vReferencePoint(2) > +EDX_EPSILON) return +1;
else if (edx0.edx_vReferencePoint(3)-edx1.edx_vReferencePoint(3) < -EDX_EPSILON) return -1;
else if (edx0.edx_vReferencePoint(3)-edx1.edx_vReferencePoint(3) > +EDX_EPSILON) return +1;
else return 0;
}
//#define EDX_EPSILON_LOOSE DOUBLE(0.00390625) // 1/2^8
/*
* Compare two edge lines.
*/
/*
inline int CompareEdgeLines_loosely(const CEdgeEx &edx0, const CEdgeEx &edx1)
{
if (edx0.edx_vDirection(1)-edx1.edx_vDirection(1) < -EDX_EPSILON_LOOSE) return -1;
else if (edx0.edx_vDirection(1)-edx1.edx_vDirection(1) > +EDX_EPSILON_LOOSE) return +1;
else if (edx0.edx_vDirection(2)-edx1.edx_vDirection(2) < -EDX_EPSILON_LOOSE) return -1;
else if (edx0.edx_vDirection(2)-edx1.edx_vDirection(2) > +EDX_EPSILON_LOOSE) return +1;
else if (edx0.edx_vDirection(3)-edx1.edx_vDirection(3) < -EDX_EPSILON_LOOSE) return -1;
else if (edx0.edx_vDirection(3)-edx1.edx_vDirection(3) > +EDX_EPSILON_LOOSE) return +1;
else if (edx0.edx_vReferencePoint(1)-edx1.edx_vReferencePoint(1) < -EDX_EPSILON_LOOSE) return -1;
else if (edx0.edx_vReferencePoint(1)-edx1.edx_vReferencePoint(1) > +EDX_EPSILON_LOOSE) return +1;
else if (edx0.edx_vReferencePoint(2)-edx1.edx_vReferencePoint(2) < -EDX_EPSILON_LOOSE) return -1;
else if (edx0.edx_vReferencePoint(2)-edx1.edx_vReferencePoint(2) > +EDX_EPSILON_LOOSE) return +1;
else if (edx0.edx_vReferencePoint(3)-edx1.edx_vReferencePoint(3) < -EDX_EPSILON_LOOSE) return -1;
else if (edx0.edx_vReferencePoint(3)-edx1.edx_vReferencePoint(3) > +EDX_EPSILON_LOOSE) return +1;
else return 0;
}*/
/*
* Compare two edge lines for quick-sort.
*/
static int qsort_CompareEdgeLines(const void *pvEdgeEx0, const void *pvEdgeEx1)
{
CEdgeEx &edx0 = **(CEdgeEx **)pvEdgeEx0;
CEdgeEx &edx1 = **(CEdgeEx **)pvEdgeEx1;
return CompareEdgeLines(edx0, edx1);
}
/*
* Find an edge with given vertices in sorted array of pointers to edges.
*/
BOOL FindEdge( CStaticArray<CObjectEdge *> &apedSorted,
CObjectVertex &vx0, CObjectVertex &vx1,
CObjectEdge **ppedResult)
{
// create a ghost edge with given vertices
CObjectEdge edWanted(vx0, vx1);
CObjectEdge *pedWanted = &edWanted;
// invoke binary search on the array with ghost edge
CObjectEdge **ppedFound = (CObjectEdge **) bsearch(
&pedWanted, &(apedSorted[0]), apedSorted.Count(), sizeof(CObjectEdge *),
qsort_CompareEdges);
// if some edge was found
if (ppedFound!=NULL) {
// return it
*ppedResult= *ppedFound;
return TRUE;
// otherwise
} else {
// there is no such edge
return FALSE;
}
}
/*
* Get start and end vertices.
*/
void CObjectPolygonEdge::GetVertices(CObjectVertex *&povxStart, CObjectVertex *&povxEnd)
{
ASSERT(ope_Edge!=NULL);
if (ope_Backward) {
povxStart = ope_Edge->oed_Vertex1;
povxEnd = ope_Edge->oed_Vertex0;
} else {
povxStart = ope_Edge->oed_Vertex0;
povxEnd = ope_Edge->oed_Vertex1;
}
}
/*
* Default constructor.
*/
CObjectSector::CObjectSector(void) :
osc_colAmbient(0), osc_colColor(0), osc_strName("")
{
osc_ulFlags[0] = 0;
osc_ulFlags[1] = 0;
osc_ulFlags[2] = 0;
}
/*
* Destructor.
*/
CObjectSector::~CObjectSector(void)
{
}
/*
* Initialize the structure for the given edge.
*/
inline void CEdgeEx::Initialize(CObjectEdge *poedEdge)
{
// remember edge
edx_poedEdge = poedEdge;
// initialize for edge's end vertices
DOUBLE3D vPoint0 = *poedEdge->oed_Vertex0;
DOUBLE3D vPoint1 = *poedEdge->oed_Vertex1;
Initialize(&vPoint0, &vPoint1);
}
inline void CEdgeEx::Initialize(const DOUBLE3D *pvPoint0, const DOUBLE3D *pvPoint1)
{
// if the vertices are reversed
if (CompareVertices(*pvPoint0, *pvPoint1)<0) {
// swap them
Swap(pvPoint0, pvPoint1);
// mark edge as reversed
edx_bReverse = TRUE;
// if the vertices are not reversed
} else {
// mark edge as not reversed
edx_bReverse = FALSE;
}
const DOUBLE3D &vPoint0=*pvPoint0;
const DOUBLE3D &vPoint1=*pvPoint1;
// normalize the direction
edx_vDirection = (vPoint1-vPoint0).Normalize();
DOUBLE fDirectionLen = edx_vDirection.Length();
ASSERT(0.999<fDirectionLen && fDirectionLen<1.001);
/* calculate the reference point on the line from any point on line (p) and direction (d):
r = p - ((p.d)/(d.d))*d
*/
edx_vReferencePoint = vPoint0 -
edx_vDirection * ((vPoint0 % edx_vDirection))/(edx_vDirection % edx_vDirection);
}
/* Test if a point is on the edge line. */
inline BOOL CEdgeEx::PointIsOnLine(const DOUBLE3D &vPointToTest) const
{
// if it is the reference point
if (CompareVertices(edx_vReferencePoint, vPointToTest)==0) {
// it is on the line
return TRUE;
}
// create edge line from line reference point to given point
CEdgeEx edxPointToTest;
edxPointToTest.Initialize(&edx_vReferencePoint, &vPointToTest);
// test if it is same as this edge line
return CompareEdgeLines(*this, edxPointToTest)==0;
}
void CObjectSector::Clear(void)
{
osc_aovxVertices.Clear();
osc_aoplPlanes.Clear();
osc_aoedEdges.Clear();
osc_aopoPolygons.Clear();
osc_aomtMaterials.Clear();
}
/*
* Lock all object sector dynamic arrays.
*/
void CObjectSector::LockAll( void)
{
osc_aovxVertices.Lock();
osc_aoplPlanes.Lock();
osc_aoedEdges.Lock();
osc_aopoPolygons.Lock();
osc_aomtMaterials.Lock();
}
/*
* Unlocks all object sector dynamic arrays.
*/
void CObjectSector::UnlockAll( void)
{
osc_aovxVertices.Unlock();
osc_aoplPlanes.Unlock();
osc_aoedEdges.Unlock();
osc_aopoPolygons.Unlock();
osc_aomtMaterials.Unlock();
}
/*
* Create indices for all members of all arrays.
*/
void CObjectSector::CreateIndices(void)
{
// lock all arrays
LockAll();
// get the number of vertices in object
INDEX ctVertices = osc_aovxVertices.Count();
// set vertex indices
for(INDEX iVertex=0; iVertex<ctVertices; iVertex++) {
osc_aovxVertices[iVertex].ovx_Index = iVertex;
}
// get the number of planes in object
INDEX ctPlanes = osc_aoplPlanes.Count();
// set plane indices
for(INDEX iPlane=0; iPlane<ctPlanes; iPlane++) {
osc_aoplPlanes[iPlane].opl_Index = iPlane;
}
// get the number of materials in object
INDEX ctMaterials = osc_aomtMaterials.Count();
// set plane indices
for(INDEX iMaterial=0; iMaterial<ctMaterials; iMaterial++) {
osc_aomtMaterials[iMaterial].omt_Index = iMaterial;
}
// get the number of edges in object
INDEX ctEdges = osc_aoedEdges.Count();
// set edge indices
for(INDEX iEdge=0; iEdge<ctEdges; iEdge++) {
osc_aoedEdges[iEdge].oed_Index = iEdge;
}
// get the number of polygons in object
INDEX ctPolygons = osc_aopoPolygons.Count();
// set polygon indices
for(INDEX iPolygon=0; iPolygon<ctPolygons; iPolygon++) {
osc_aopoPolygons[iPolygon].opo_Index = iPolygon;
}
// unlock all arrays
UnlockAll();
}
/*
* Create a new polygon in given sector.
*/
CObjectPolygon *CObjectSector::CreatePolygon(INDEX ctVertices, DOUBLE3D avVertices[],
CObjectMaterial &omaMaterial, ULONG ulFlags, BOOL bReverse)
{
// reset areas on axial planes
DOUBLE fXY=0.0, fXZ=0.0, fYZ=0.0;
// for each two vertices
{for (INDEX ivx=0; ivx<ctVertices; ivx++){
const DOUBLE3D &v0 = avVertices[ivx];
const DOUBLE3D &v1 = avVertices[(ivx+1)%ctVertices];
// add the edge to all three areas
fXY+=(v1(1)-v0(1))*(v0(2)+v1(2))/2.0;
fXZ+=(v1(1)-v0(1))*(v0(3)+v1(3))/2.0;
fYZ+=(v1(2)-v0(2))*(v0(3)+v1(3))/2.0;
}}
// find maximum absolute area
fXY = Abs(fXY); fXZ = Abs(fXZ); fYZ = Abs(fYZ);
DOUBLE fMaxArea = Max(Max(fXY, fXZ), fYZ);
// if maximum area is too small
if(fMaxArea<1E-8) {
// do not create polygon
return NULL;
}
// create a new polygon in object
CObjectPolygon &opoPolygon = *osc_aopoPolygons.New();
// create as much polygon edges as there are vertices in the polygon
CObjectPolygonEdge *popePolygonEdges = opoPolygon.opo_PolygonEdges.New(ctVertices);
// for each vertex
{for (INDEX iVertex=0; iVertex<ctVertices; iVertex++) {
DOUBLE3D &v0=avVertices[iVertex]; // that vertex
DOUBLE3D &v1=avVertices[(iVertex+1)%ctVertices]; // its next neighbour
// create an edge between it and its neighbour and add it to the polygon
if (bReverse) {
popePolygonEdges[iVertex] = CObjectPolygonEdge(&CreateEdge(v1, v0));
} else {
popePolygonEdges[iVertex] = CObjectPolygonEdge(&CreateEdge(v0, v1));
}
}}
// clear plane normal
DOUBLE3D vNormal = DOUBLE3D(0.0f,0.0f,0.0f);
// for all vertices in polygon
{for(INDEX iEdge=0; iEdge<ctVertices; iEdge++) {
// get two neighbouring edges
CObjectEdge &oed0 = *popePolygonEdges[iEdge].ope_Edge;
CObjectEdge &oed1 = *popePolygonEdges[(iEdge+1)%ctVertices].ope_Edge;
// make their vectors
DOUBLE3D vVector1 = *oed0.oed_Vertex0 - *oed0.oed_Vertex1;
DOUBLE3D vVector2 = *oed1.oed_Vertex1 - *oed1.oed_Vertex0;
// add vector product of edges to the plane normal
vNormal += vVector2*vVector1;
}}
// add one plane to planes array
CObjectPlane *pplPlane = osc_aoplPlanes.New();
// construct this plane from normal vector and one point
if (bReverse) {
*pplPlane= DOUBLEplane3D( -vNormal, *popePolygonEdges[0].ope_Edge->oed_Vertex0);
} else {
*pplPlane= DOUBLEplane3D( vNormal, *popePolygonEdges[0].ope_Edge->oed_Vertex0);
}
// set this polygon's plane pointer
opoPolygon.opo_Plane = pplPlane;
// set this polygon's material pointer and color
opoPolygon.opo_Material = &omaMaterial;
opoPolygon.opo_colorColor = omaMaterial.omt_Color;
// set flags
opoPolygon.opo_ulFlags = ulFlags;
return &opoPolygon;
}
/*
* Create a new polygon in given sector.
*/
CObjectPolygon *CObjectSector::CreatePolygon(INDEX ctVertices, INDEX aivVertices[],
CObjectMaterial &omaMaterial, ULONG ulFlags, BOOL bReverse)
{
// create a new polygon in object
CObjectPolygon &opoPolygon = *osc_aopoPolygons.New();
// create as much polygon edges as there are vertices in the polygon
CObjectPolygonEdge *popePolygonEdges = opoPolygon.opo_PolygonEdges.New(ctVertices);
// for each vertex
{for (INDEX iVertex=0; iVertex<ctVertices; iVertex++)
{
INDEX iNextVertex = (iVertex+1)%ctVertices;
// create an edge between it and its neighbour and add it to the polygon
if (bReverse)
{
popePolygonEdges[iVertex] = CObjectPolygonEdge(
&CreateEdge(aivVertices[ iVertex], aivVertices[ iNextVertex]) );
}
else
{
popePolygonEdges[iVertex] = CObjectPolygonEdge(
&CreateEdge(aivVertices[ iNextVertex], aivVertices[ iVertex]) );
}
}}
// clear plane normal
DOUBLE3D vNormal = DOUBLE3D(0.0f,0.0f,0.0f);
// for all vertices in polygon
{for(INDEX iEdge=0; iEdge<ctVertices; iEdge++) {
// get two neighbouring edges
CObjectEdge &oed0 = *popePolygonEdges[iEdge].ope_Edge;
CObjectEdge &oed1 = *popePolygonEdges[(iEdge+1)%ctVertices].ope_Edge;
// make their vectors
DOUBLE3D vVector1 = *oed0.oed_Vertex0 - *oed0.oed_Vertex1;
DOUBLE3D vVector2 = *oed1.oed_Vertex1 - *oed1.oed_Vertex0;
// add vector product of edges to the plane normal
vNormal += vVector2*vVector1;
}}
// add one plane to planes array
CObjectPlane *pplPlane = osc_aoplPlanes.New();
// construct this plane from normal vector and one point
if (bReverse) {
*pplPlane= DOUBLEplane3D( -vNormal, *popePolygonEdges[0].ope_Edge->oed_Vertex0);
} else {
*pplPlane= DOUBLEplane3D( vNormal, *popePolygonEdges[0].ope_Edge->oed_Vertex0);
}
// set this polygon's plane pointer
opoPolygon.opo_Plane = pplPlane;
// set this polygon's material pointer and color
opoPolygon.opo_Material = &omaMaterial;
opoPolygon.opo_colorColor = omaMaterial.omt_Color;
// set flags
opoPolygon.opo_ulFlags = ulFlags;
return &opoPolygon;
}
/*
* Check the optimization algorithm.
*/
void CObjectSector::CheckOptimizationAlgorithm(void)
{
// for vertices
FOREACHINDYNAMICARRAY(osc_aovxVertices, CObjectVertex, itvx1) {
FOREACHINDYNAMICARRAY(osc_aovxVertices, CObjectVertex, itvx2) {
CObjectVertex &vx1 = itvx1.Current();
CObjectVertex &vx2 = itvx2.Current();
// !!!!why this fails sometimes ?(on spheres) ASSERT( (&vx1 == &vx2) || (CompareVertices(vx1, vx2)!=0) );
}
}
// for planes
FOREACHINDYNAMICARRAY(osc_aoplPlanes, CObjectPlane, itpl1) {
FOREACHINDYNAMICARRAY(osc_aoplPlanes, CObjectPlane, itpl2) {
CObjectPlane &pl1 = itpl1.Current();
CObjectPlane &pl2 = itpl2.Current();
ASSERT( (&pl1 == &pl2) || (ComparePlanes(pl1, pl2)!=0));
}
}
// for edges
{FOREACHINDYNAMICARRAY(osc_aoedEdges, CObjectEdge, ited1) {
FOREACHINDYNAMICARRAY(osc_aoedEdges, CObjectEdge, ited2) {
CObjectEdge &ed1 = ited1.Current();
CObjectEdge &ed2 = ited2.Current();
ASSERT( (&ed1 == &ed2)
|| (ed1.oed_Vertex0 != ed2.oed_Vertex0) || (ed1.oed_Vertex1 != ed2.oed_Vertex1) );
}
}}
// for inverse edges
INDEX iInversesFound = 0;
{FOREACHINDYNAMICARRAY(osc_aoedEdges, CObjectEdge, ited1) {
FOREACHINDYNAMICARRAY(osc_aoedEdges, CObjectEdge, ited2) {
CObjectEdge &ed1 = ited1.Current();
CObjectEdge &ed2 = ited2.Current();
if (&ed1 == &ed2) continue;
BOOL inv1, inv2;
inv1 = (&ed1 == ed2.optimize.oed_InverseEdge);
inv2 = (&ed2 == ed1.optimize.oed_InverseEdge);
// check against cross linked inverses
ASSERT(!(inv1&&inv2));
if (inv1) {
iInversesFound++;
}
// check against inverses that have not been found
ASSERT( (inv1 || inv2) || (ed1.oed_Vertex0 != ed2.oed_Vertex1) || (ed1.oed_Vertex1 != ed2.oed_Vertex0) );
}
}}
// for each polygon
{FOREACHINDYNAMICARRAY(osc_aopoPolygons, CObjectPolygon, itopo) {
// for each edge in polygon
{FOREACHINDYNAMICARRAY(itopo->opo_PolygonEdges, CObjectPolygonEdge, itope) {
CObjectEdge &oedThis = *itope->ope_Edge;
CObjectVertex *povxStartThis, *povxEndThis;
// get start and end vertices
itope->GetVertices(povxStartThis, povxEndThis);
// if start vertex is not on the plane
DOUBLE fDistance = itopo->opo_Plane->PointDistance(*povxStartThis);
if (Abs(fDistance)>(VTX_EPSILON*16.0)) {
// report it
_RPT4(_CRT_WARN, "(%f, %f, %f) is 1/%f from plane ",
(*povxStartThis)(1),
(*povxStartThis)(2),
(*povxStartThis)(3),
1.0/fDistance);
_RPT4(_CRT_WARN, "(%f, %f, %f):%f\n",
(*itopo->opo_Plane)(1),
(*itopo->opo_Plane)(2),
(*itopo->opo_Plane)(3),
itopo->opo_Plane->Distance());
}
}}
}}
}
BOOL CObjectSector::ArePolygonsPlanar(void)
{
// for each polygon
{FOREACHINDYNAMICARRAY(osc_aopoPolygons, CObjectPolygon, itopo) {
// for each edge in polygon
{FOREACHINDYNAMICARRAY(itopo->opo_PolygonEdges, CObjectPolygonEdge, itope) {
CObjectEdge &oedThis = *itope->ope_Edge;
CObjectVertex *povxStartThis, *povxEndThis;
// get start and end vertices
itope->GetVertices(povxStartThis, povxEndThis);
// if start vertex is not on the plane
DOUBLE fDistance = itopo->opo_Plane->PointDistance(*povxStartThis);
if (Abs(fDistance)>(VTX_EPSILON*16.0)) {
return FALSE;
}
}}
}}
return TRUE;
}
/*
* Remap different planes with same coordinates to use only one of each.
*/
void CObjectSector::RemapClonedPlanes(void)
{
// if there are no planes in the sector
if (osc_aoplPlanes.Count()==0) {
// do nothing (optimization algorithms assume at least one plane present)
return;
}
osc_aoplPlanes.Lock();
/* Prepare sorted array for remapping planes. */
// create an array of pointers for sorting planes
INDEX ctPlanes = osc_aoplPlanes.Count();
CStaticArray<CObjectPlane *> applSortedPlanes;
applSortedPlanes.New(ctPlanes);
// for all vertices
for(INDEX iPlane=0; iPlane<ctPlanes; iPlane++) {
// set the pointer in sorting array
applSortedPlanes[iPlane] = &osc_aoplPlanes[iPlane];
// set remap pointer to itself
osc_aoplPlanes[iPlane].opl_Remap = &osc_aoplPlanes[iPlane];
}
if( wld_bFastObjectOptimization) {
// sort the array of pointers, so that same planes get next to each other
qsort(&applSortedPlanes[0], ctPlanes, sizeof(CObjectPlane *), qsort_ComparePlanes);
/* Create remapping pointers. */
// for all planes in sorted array, except the last one
for(INDEX iSortedPlane=0; iSortedPlane<ctPlanes-1; iSortedPlane++) {
// if the next plane is same as this one
if ( ComparePlanes(*applSortedPlanes[iSortedPlane],
*applSortedPlanes[iSortedPlane+1]) == 0 ) {
// set its remap pointer to same as this remap pointer
applSortedPlanes[iSortedPlane+1]->opl_Remap = applSortedPlanes[iSortedPlane]->opl_Remap;
}
}
} else {
// for all pairs of planes
{for(INDEX iPlane1=0; iPlane1<ctPlanes; iPlane1++) {
CObjectPlane &pl1 = osc_aoplPlanes[iPlane1];
if (pl1.opl_Remap != &pl1) {
continue;
}
for(INDEX iPlane2=iPlane1+1; iPlane2<ctPlanes; iPlane2++) {
CObjectPlane &pl2 = osc_aoplPlanes[iPlane2];
if (ComparePlanes(pl1, pl2)==0) {
pl2.opl_Remap = pl1.opl_Remap;
}
}
}}
}
/* Remap all references to planes. */
// for all polygons in object
{FOREACHINDYNAMICARRAY(osc_aopoPolygons, CObjectPolygon, itopo) {
// remap plane pointers
itopo->opo_Plane = itopo->opo_Plane->opl_Remap;
}}
osc_aoplPlanes.Unlock();
}
/*
* Remap different vertices with same coordinates to use only one of each.
*/
static BOOL bBug=FALSE;
void CObjectSector::RemapClonedVertices(void)
{
// if there are no vertices in the sector
if (osc_aovxVertices.Count()==0) {
// do nothing (optimization algorithms assume at least one vertex present)
return;
}
osc_aovxVertices.Lock();
/* Prepare sorted array for remapping vertices. */
// create an array of pointers for sorting vertices
INDEX ctVertices = osc_aovxVertices.Count();
CStaticArray<CObjectVertex *> apvxSortedVertices;
apvxSortedVertices.New(ctVertices);
// for all vertices
for(INDEX iVertex=0; iVertex<ctVertices; iVertex++) {
// set the pointer in sorting array
apvxSortedVertices[iVertex] = &osc_aovxVertices[iVertex];
// set remap pointer to itself
osc_aovxVertices[iVertex].ovx_Remap = &osc_aovxVertices[iVertex];
}
if( wld_bFastObjectOptimization) {
// sort the array of pointers, so that same vertices get next to each other
qsort(&apvxSortedVertices[0], ctVertices, sizeof(CObjectVertex *), qsort_CompareVertices);
/* Create remapping pointers. */
// for all vertices in sorted array, except the last one
for(INDEX iSortedVertex=0; iSortedVertex<ctVertices-1; iSortedVertex++) {
// if the next vertex is same as this one
if ( CompareVertices(*apvxSortedVertices[iSortedVertex],
*apvxSortedVertices[iSortedVertex+1]) == 0 ) {
// set its remap pointer to same as this remap pointer
apvxSortedVertices[iSortedVertex+1]->ovx_Remap = apvxSortedVertices[iSortedVertex]->ovx_Remap;
// otherwise
} else {
#ifndef NDEBUG
// check that it is really ok
CObjectVertex &vx1 = *apvxSortedVertices[iSortedVertex];
CObjectVertex &vx2 = *apvxSortedVertices[iSortedVertex+1];
ASSERT( (&vx1 == &vx2) || (CompareVertices(vx1, vx2)!=0));
#endif
}
}
} else {
// for all pairs of vertices
{for(INDEX iVertex1=0; iVertex1<ctVertices; iVertex1++) {
CObjectVertex &vx1 = osc_aovxVertices[iVertex1];
if (vx1.ovx_Remap != &vx1) {
continue;
}
for(INDEX iVertex2=iVertex1+1; iVertex2<ctVertices; iVertex2++) {
CObjectVertex &vx2 = osc_aovxVertices[iVertex2];
if (CompareVertices(vx1, vx2)==0) {
vx2.ovx_Remap = vx1.ovx_Remap;
}
}
}}
}
/* Remap all references to vertices. */
// for all edges in object
{FOREACHINDYNAMICARRAY(osc_aoedEdges, CObjectEdge, ited) {
// remap vertex pointers
ited->oed_Vertex0 = ited->oed_Vertex0->ovx_Remap;
ited->oed_Vertex1 = ited->oed_Vertex1->ovx_Remap;
}}
osc_aovxVertices.Unlock();
#if 0
#ifndef NDEBUG
if (bBug) goto skipbug;
// for vertices
{FOREACHINDYNAMICARRAY(osc_aovxVertices, CObjectVertex, itvx1) {
FOREACHINDYNAMICARRAY(osc_aovxVertices, CObjectVertex, itvx2) {
CObjectVertex &vx1 = *(itvx1->ovx_Remap);
CObjectVertex &vx2 = *(itvx2->ovx_Remap);
//ASSERT( (&vx1 == &vx2) || (CompareVertices(vx1, vx2)!=0));
if (!( (&vx1 == &vx2) || (CompareVertices(vx1, vx2)!=0))) {
goto bug;
}
}
}}
goto skipbug;
bug:
bBug = TRUE;
// for all vertices in sorted array, except the last one
{for(INDEX iSortedVertex=0; iSortedVertex<ctVertices-1; iSortedVertex++) {
CPrintF("(%f,%f,%f)-(%f,%f,%f) ",
(*apvxSortedVertices[iSortedVertex])(1),
(*apvxSortedVertices[iSortedVertex])(2),
(*apvxSortedVertices[iSortedVertex])(3),
(*apvxSortedVertices[iSortedVertex+1])(1),
(*apvxSortedVertices[iSortedVertex+1])(2),
(*apvxSortedVertices[iSortedVertex+1])(3)
);
CPrintF("%d\n", CompareVertices(*apvxSortedVertices[iSortedVertex],
*apvxSortedVertices[iSortedVertex+1])
);
}}
skipbug:;
#endif //NDEBUG
#endif 0
}
/*
* Remove vertices that are not used by any edge.
*/
void CObjectSector::RemoveUnusedVertices(void)
{
// if there are no vertices in the sector
if (osc_aovxVertices.Count()==0) {
// do nothing (optimization algorithms assume at least one vertex present)
return;
}
// clear all vertex tags
{FOREACHINDYNAMICARRAY(osc_aovxVertices, CObjectVertex, itovx) {
itovx->ovx_Tag = FALSE;
}}
// mark all vertices that are used by some edge
{FOREACHINDYNAMICARRAY(osc_aoedEdges, CObjectEdge, ited) {
ited->oed_Vertex0->ovx_Tag = TRUE;
ited->oed_Vertex1->ovx_Tag = TRUE;
}}
// find number of used vertices
INDEX ctUsedVertices = 0;
{FOREACHINDYNAMICARRAY(osc_aovxVertices, CObjectVertex, itovx) {
if (itovx->ovx_Tag) {
ctUsedVertices++;
}
}}
// create a new array with as much vertices as we have counted in last pass
CDynamicArray<CObjectVertex> aovxNew;
CObjectVertex *povxUsed = aovxNew.New(ctUsedVertices);
// for each vertex
{FOREACHINDYNAMICARRAY(osc_aovxVertices, CObjectVertex, itovx) {
// if it is used
if (itovx->ovx_Tag) {
// copy it to new array
*povxUsed = itovx.Current();
// set its remap pointer into new array
itovx->ovx_Remap = povxUsed;
povxUsed++;
// if it is not used
} else {
// clear its remap pointer (for debugging)
#ifndef NDEBUG
itovx->ovx_Remap = NULL;
#endif
}
}}
// for all edges in object
{FOREACHINDYNAMICARRAY(osc_aoedEdges, CObjectEdge, ited) {
// remap vertex pointers
ited->oed_Vertex0 = ited->oed_Vertex0->ovx_Remap;
ited->oed_Vertex1 = ited->oed_Vertex1->ovx_Remap;
}}
// use new array of vertices instead of the old one
osc_aovxVertices.Clear();
osc_aovxVertices.MoveArray(aovxNew);
}
/*
* Remove edges that are not used by any polygon.
*/
void CObjectSector::RemoveUnusedEdges(void)
{
// if there are no edges in the sector
if (osc_aoedEdges.Count()==0) {
// do nothing (optimization algorithms assume at least one edge present)
return;
}
// clear all edge tags
{FOREACHINDYNAMICARRAY(osc_aoedEdges, CObjectEdge, itoed) {
itoed->oed_Tag = FALSE;
}}
// mark all edges that are used by some polygon
{FOREACHINDYNAMICARRAY(osc_aopoPolygons, CObjectPolygon, itopo) {
{FOREACHINDYNAMICARRAY(itopo->opo_PolygonEdges, CObjectPolygonEdge, itope) {
itope->ope_Edge->oed_Tag = TRUE;
}}
}}
// find number of used edges
INDEX ctUsedEdges = 0;
{FOREACHINDYNAMICARRAY(osc_aoedEdges, CObjectEdge, itoed) {
if (itoed->oed_Tag) {
ctUsedEdges++;
}
}}
// create a new array with as much edges as we have counted in last pass
CDynamicArray<CObjectEdge> aoedNew;
CObjectEdge *poedUsed = aoedNew.New(ctUsedEdges);
// for each edge
{FOREACHINDYNAMICARRAY(osc_aoedEdges, CObjectEdge, itoed) {
// if it is used
if (itoed->oed_Tag) {
// copy it to new array
*poedUsed = itoed.Current();
// set its remap pointer into new array
itoed->optimize.oed_Remap = poedUsed;
poedUsed++;
// if it is not used
} else {
// clear its remap pointer (for debugging)
#ifndef NDEBUG
itoed->optimize.oed_Remap = NULL;
#endif
}
}}
// remap edge pointers in all polygons
{FOREACHINDYNAMICARRAY(osc_aopoPolygons, CObjectPolygon, itopo) {
{FOREACHINDYNAMICARRAY(itopo->opo_PolygonEdges, CObjectPolygonEdge, itope) {
itope->ope_Edge = itope->ope_Edge->optimize.oed_Remap;
}}
}}
// use new array of edges instead the old one
osc_aoedEdges.Clear();
osc_aoedEdges.MoveArray(aoedNew);
}
/*
* Remove planes that are not used by any polygon.
*/
void CObjectSector::RemoveUnusedPlanes(void)
{
// if there are no planes in the sector
if (osc_aoplPlanes.Count()==0) {
// do nothing (optimization algorithms assume at least one plane present)
return;
}
// clear all plane tags
{FOREACHINDYNAMICARRAY(osc_aoplPlanes, CObjectPlane, itopl) {
itopl->opl_Tag = FALSE;
}}
// mark all planes that are used by some polygon
{FOREACHINDYNAMICARRAY(osc_aopoPolygons, CObjectPolygon, itopo) {
itopo->opo_Plane->opl_Tag = TRUE;
}}
// find number of used planes
INDEX ctUsedPlanes = 0;
{FOREACHINDYNAMICARRAY(osc_aoplPlanes, CObjectPlane, itopl) {
if (itopl->opl_Tag) {
ctUsedPlanes++;
}
}}
// create a new array with as much planes as we have counted in last pass
CDynamicArray<CObjectPlane> aoplNew;
CObjectPlane *poplUsed = aoplNew.New(ctUsedPlanes);
// for each plane
{FOREACHINDYNAMICARRAY(osc_aoplPlanes, CObjectPlane, itopl) {
// if it is used
if (itopl->opl_Tag) {
// copy it to new array
*poplUsed = itopl.Current();
// set its remap pointer into new array
itopl->opl_Remap = poplUsed;
poplUsed++;
// if it is not used
} else {
// clear its remap pointer (for debugging)
#ifndef NDEBUG
itopl->opl_Remap = NULL;
#endif
}
}}
// remap plane pointers in all polygons
{FOREACHINDYNAMICARRAY(osc_aopoPolygons, CObjectPolygon, itopo) {
itopo->opo_Plane = itopo->opo_Plane->opl_Remap;
}}
// use new array of planes instead the old one
osc_aoplPlanes.Clear();
osc_aoplPlanes.MoveArray(aoplNew);
}
/*
* Split an edge with a run of vertices.
* NOTE: 'sortvertices_iMaxAxis' must be set correctly!
*/
void CObjectSector::SplitEdgeWithVertices(CObjectEdge &oedOriginal,
CStaticArray<CObjectVertex *> &apvxVertices)
{
CObjectVertex *pvx0 = oedOriginal.oed_Vertex0;
CObjectVertex *pvx1 = oedOriginal.oed_Vertex1;
BOOL bReversed;
// edge must have length
ASSERT(CompareVertices(*pvx0, *pvx1)!=0);
// if the edge is going in the direction of the run
if (CompareVerticesAlongLine(*pvx0, *pvx1)<0) {
// mark that it is not reversed
bReversed = FALSE;
// if the edge is going in opposite direction of the run
} else {
// swap vertices
Swap(pvx0, pvx1);
// mark that it is reversed
bReversed = TRUE;
}
// init the edge as remap pointer
oedOriginal.colinear1.oed_FirstChild = NULL;
INDEX iVertex; // current vertex in run
// skip until start vertex is found
for (iVertex=0; apvxVertices[iVertex]!=pvx0; iVertex++) {
NOTHING;
}
// for all vertices after start vertex and stopping with end vertex
for (iVertex++; apvxVertices[iVertex-1]!=pvx1; iVertex++) {
// if vertex is not same as last one
if (apvxVertices[iVertex]!=apvxVertices[iVertex-1]) {
// create a new edge between the two vertices
CObjectEdge *poedNewChild = osc_aoedEdges.New();
if (bReversed) {
*poedNewChild = CObjectEdge(*apvxVertices[iVertex], *apvxVertices[iVertex-1]);
} else {
*poedNewChild = CObjectEdge(*apvxVertices[iVertex-1], *apvxVertices[iVertex]);
}
ASSERT(CompareVertices(*poedNewChild->oed_Vertex0, *poedNewChild->oed_Vertex1)!=0);
// link it as a child of this edge
poedNewChild->colinear1.oed_NextSibling = oedOriginal.colinear1.oed_FirstChild;
oedOriginal.colinear1.oed_FirstChild = poedNewChild;
}
}
}
/*
* Join polygon edges that are collinear are continuing.
*/
void CObjectSector::JoinContinuingPolygonEdges(void)
{
osc_aoedEdges.Lock();
// create array of extended edge infosd
CStaticArray<CEdgeEx> aedxEdgeLines;
CStaticArray<CEdgeEx *> apedxSortedEdgeLines;
INDEX ctEdges = osc_aoedEdges.Count();
CreateEdgeLines(aedxEdgeLines, apedxSortedEdgeLines, ctEdges);
/* NOTE: The array doesn't have to be sorted because only
edges in one polygon are considered.
*/
// for each polygon
{FOREACHINDYNAMICARRAY(osc_aopoPolygons, CObjectPolygon, itopo) {
// join continuing edges in it
itopo->JoinContinuingEdges(osc_aoedEdges);
}}
osc_aoedEdges.Unlock();
}
/*
* Join polygon edges that are collinear and continuing.
*/
/*
* NOTE: Edge line infos must be prepared before calling this.
*/
void CObjectPolygon::JoinContinuingEdges(CDynamicArray<CObjectEdge> &oedEdges)
{
// create an empty array for newly created edges
CDynamicArray<CObjectPolygonEdge> aopeNew;
// set the counter of edges to current number of edges
INDEX ctEdges = opo_PolygonEdges.Count();
// for each edge
{FOREACHINDYNAMICARRAY(opo_PolygonEdges, CObjectPolygonEdge, itope) {
CObjectEdge &oedThis = *itope->ope_Edge;
// if not already marked for removal
if (&oedThis != NULL) {
CObjectVertex *povxStartThis, *povxEndThis;
// get start and end vertices
itope->GetVertices(povxStartThis, povxEndThis);
// mark the original edge for removal
itope->ope_Edge = NULL;
BOOL bChangeDone; // termination condition flag
// repeat
do {
// mark that nothing is changed
bChangeDone = FALSE;
// for each edge
{FOREACHINDYNAMICARRAY(opo_PolygonEdges, CObjectPolygonEdge, itope2) {
CObjectEdge &oedOther = *itope2->ope_Edge;
// if not already marked for removal
if (&oedOther != NULL) {
// if the two edges are collinear
if ( CompareEdgeLines(*oedThis.colinear2.oed_pedxLine, *oedOther.colinear2.oed_pedxLine)==0) {
CObjectVertex *povxStartOther, *povxEndOther;
// get start and end vertices
itope2->GetVertices(povxStartOther, povxEndOther);
// if the other edge is continuing to this one
if ( povxStartOther == povxEndThis ) {
// extend the current edge by it
povxEndThis = povxEndOther;
// mark it for removal
itope2->ope_Edge = NULL;
// mark that a change is done
bChangeDone = TRUE;
// if the other edge is continued by this one
} else if ( povxStartThis == povxEndOther) {
// extend the current edge by it
povxStartThis = povxStartOther;
// mark it for removal
itope2->ope_Edge = NULL;
// mark that a change is done
bChangeDone = TRUE;
}
}
}
}}
// until a pass is made without making any change
} while (bChangeDone);
// create new edge with given start and end vertices
CObjectEdge *poedNew = oedEdges.New();
*poedNew = CObjectEdge(*povxStartThis, *povxEndThis);
// add it to new array
*aopeNew.New() = CObjectPolygonEdge(poedNew);
}
}}
// replace old array with the new one
opo_PolygonEdges.Clear();
opo_PolygonEdges.MoveArray(aopeNew);
}
/*
* Split a run of collinear edges.
*/
void CObjectSector::SplitCollinearEdgesRun(CStaticArray<CEdgeEx *> &apedxSortedEdgeLines,
INDEX iFirstInRun, INDEX iLastInRun)
{
if (iFirstInRun>iLastInRun) {
return; // this should not happen, but anyway!
}
CEdgeEx &edxLine = *apedxSortedEdgeLines[iFirstInRun]; // representative line of the run
/* set up array of vertex pointers */
/*
// for each vertex in sector
INDEX ctVerticesOnLine=0;
{FOREACHINDYNAMICARRAY(osc_aovxVertices, CObjectVertex, itovx) {
// if it is on the line
if (edxLine.PointIsOnLine(*itovx)) {
// mark it as on the line
itovx->ovx_Tag = TRUE;
ctVerticesOnLine++;
// if it is on the line
} else {
// mark it as on not the line
itovx->ovx_Tag = FALSE;
}
}}
// for all edges in run
{for(INDEX iEdgeInRun=iFirstInRun; iEdgeInRun<=iLastInRun; iEdgeInRun++) {
CObjectEdge &oed = *apedxSortedEdgeLines[iEdgeInRun]->edx_poedEdge;
// if first vertex is not marked
if (!oed.oed_Vertex0->ovx_Tag) {
// mark it
oed.oed_Vertex0->ovx_Tag = TRUE;
ctVerticesOnLine++;
}
// if second vertex is not marked
if (!oed.oed_Vertex1->ovx_Tag) {
// mark it
oed.oed_Vertex1->ovx_Tag = TRUE;
ctVerticesOnLine++;
}
}}
// if there are no vertices
if (ctVerticesOnLine==0) {
// do nothing
return;
}
// create array of vertex pointers
CStaticArray<CObjectVertex *> apvxSortedVertices;
apvxSortedVertices.New(ctVerticesOnLine);
// for each vertex in sector
INDEX iVertexOnLine=0;
{FOREACHINDYNAMICARRAY(osc_aovxVertices, CObjectVertex, itovx) {
// if it is marked
if (itovx->ovx_Tag) {
// set its pointer in the array
apvxSortedVertices[iVertexOnLine++] = &*itovx;
}
}}
ASSERT(iVertexOnLine == ctVerticesOnLine);
*/
// create array of vertex pointers
INDEX ctVerticesOnLine = (iLastInRun-iFirstInRun+1)*2;
// if there are no vertices
if (ctVerticesOnLine==0) {
// do nothing
return;
}
CStaticArray<CObjectVertex *> apvxSortedVertices;
apvxSortedVertices.New(ctVerticesOnLine);
// for all edges in run
{for(INDEX iEdgeInRun=iFirstInRun; iEdgeInRun<=iLastInRun; iEdgeInRun++) {
CObjectEdge &oed = *apedxSortedEdgeLines[iEdgeInRun]->edx_poedEdge;
// set vertex pointers
apvxSortedVertices[(iEdgeInRun-iFirstInRun)*2] = oed.oed_Vertex0;
apvxSortedVertices[(iEdgeInRun-iFirstInRun)*2+1] = oed.oed_Vertex1;
}}
/* sort the array of vertex pointers */
// get axis direction from any edge in the run
DOUBLE3D vDirection = apedxSortedEdgeLines[iFirstInRun]->edx_vDirection;
// find largest axis of direction vector
INDEX iMaxAxis = 0;
DOUBLE fMaxAxis = 0.0f;
for (INDEX iAxis=1; iAxis<=3; iAxis++) {
if ( Abs(vDirection(iAxis)) > Abs(fMaxAxis) ) {
fMaxAxis = vDirection(iAxis);
iMaxAxis = iAxis;
}
}
// set axis for sorting
sortvertices_iMaxAxis = iMaxAxis;
DOUBLE fMaxAxisSign = Sgn(fMaxAxis);
ASSERT(Abs(fMaxAxisSign) > +VTX_EPSILON);
// if axis is positive
if (fMaxAxisSign > +VTX_EPSILON) {
// sort vertex pointers along the line
if (ctVerticesOnLine>0) {
qsort(&apvxSortedVertices[0], ctVerticesOnLine, sizeof(CObjectVertex *),
qsort_CompareVerticesAlongLine);
}
// if axis is negative
} else if (fMaxAxisSign < -VTX_EPSILON) {
// sort vertex pointers along the line reversely
if (ctVerticesOnLine>0) {
qsort(&apvxSortedVertices[0], ctVerticesOnLine, sizeof(CObjectVertex *),
qsort_CompareVerticesAlongLine);
//qsort_CompareVerticesAlongLineReversely); ?
}
}
/* split each edge in turn */
// for all edges in run
{for(INDEX iEdgeInRun=iFirstInRun; iEdgeInRun<=iLastInRun; iEdgeInRun++) {
CObjectEdge &oed = *apedxSortedEdgeLines[iEdgeInRun]->edx_poedEdge;
// split the edge with the vertices
SplitEdgeWithVertices(oed, apvxSortedVertices);
}}
}
/*
* Create array of extended edge infos.
*/
void CObjectSector::CreateEdgeLines(CStaticArray<CEdgeEx> &aedxEdgeLines,
CStaticArray<CEdgeEx *> &apedxSortedEdgeLines, INDEX &ctEdges)
{
// create array of extended edge infos
ctEdges = osc_aoedEdges.Count();
aedxEdgeLines.New(ctEdges);
// create array of pointers to extended edge infos for sorting edges
apedxSortedEdgeLines.New(ctEdges);
// for all edges
for(INDEX iEdge=0; iEdge<ctEdges; iEdge++) {
// create extended info
aedxEdgeLines[iEdge].Initialize(&osc_aoedEdges[iEdge]);
// set the pointer in sorting array
apedxSortedEdgeLines[iEdge] = &aedxEdgeLines[iEdge];
// set back-reference in the edge
osc_aoedEdges[iEdge].colinear2.oed_pedxLine = &aedxEdgeLines[iEdge];
}
}
/*
* Find collinear edges and cross-split them with each other.
*/
void CObjectSector::SplitCollinearEdges(void)
{
osc_aoedEdges.Lock();
/* Prepare arrays for splitting edges. */
// create array of extended edge infosd
CStaticArray<CEdgeEx> aedxEdgeLines;
CStaticArray<CEdgeEx *> apedxSortedEdgeLines;
INDEX ctEdges = osc_aoedEdges.Count();
CreateEdgeLines(aedxEdgeLines, apedxSortedEdgeLines, ctEdges);
// sort the array of pointers, so that collinear edges get next to each other
if (ctEdges>0) {
qsort(&apedxSortedEdgeLines[0], ctEdges, sizeof(CEdgeEx *), qsort_CompareEdgeLines);
}
/* Create remapping pointers. */
// for all edges in normal sorted array, starting at second one
INDEX iFirstInLine = 0;
for(INDEX iSortedEdge=1; iSortedEdge<ctEdges; iSortedEdge++) {
// if the previous edge is not collinear with this one
if ( CompareEdgeLines(*apedxSortedEdgeLines[iSortedEdge],
*apedxSortedEdgeLines[iSortedEdge-1]) != 0 ) {
// split the last run of collinear edges
SplitCollinearEdgesRun(apedxSortedEdgeLines, iFirstInLine, iSortedEdge-1);
// start a new run of collinear edges
iFirstInLine = iSortedEdge;
}
}
// split the last run of collinear edges
SplitCollinearEdgesRun(apedxSortedEdgeLines, iFirstInLine, ctEdges-1);
/* Remap references to edges. */
// for all polygons in object
{FOREACHINDYNAMICARRAY(osc_aopoPolygons, CObjectPolygon, itpo2) {
/* create new array of polygon edges for child edges */
// for all of its edge pointers
INDEX ctNewEdges = 0;
{FOREACHINDYNAMICARRAY(itpo2->opo_PolygonEdges, CObjectPolygonEdge, itope) {
// count all children
for (CObjectEdge *poed = itope->ope_Edge->colinear1.oed_FirstChild;
poed!=NULL;
poed = poed->colinear1.oed_NextSibling) {
ctNewEdges++;
}
}}
// create a new array of edge references
CDynamicArray<CObjectPolygonEdge> aopoNewPolygonEdges;
if (ctNewEdges>0) {
aopoNewPolygonEdges.New(ctNewEdges);
}
aopoNewPolygonEdges.Lock();
/* set the array */
// for all of its edge pointers
INDEX iChildEdge = 0;
{FOREACHINDYNAMICARRAY(itpo2->opo_PolygonEdges, CObjectPolygonEdge, itope) {
// for all of its children
for (CObjectEdge *poed = itope->ope_Edge->colinear1.oed_FirstChild;
poed!=NULL;
poed = poed->colinear1.oed_NextSibling) {
// set the child polygon edge
aopoNewPolygonEdges[iChildEdge] = CObjectPolygonEdge(poed, itope->ope_Backward);
iChildEdge++;
}
}}
/* replace old array with the new one */
aopoNewPolygonEdges.Unlock();
itpo2->opo_PolygonEdges.Clear();
itpo2->opo_PolygonEdges.MoveArray(aopoNewPolygonEdges);
}}
osc_aoedEdges.Unlock();
}
/*
* Remove polygon edges that are used twice from all polygons.
*/
void CObjectSector::RemoveRedundantPolygonEdges(void)
{
// for each polygon
{FOREACHINDYNAMICARRAY(osc_aopoPolygons, CObjectPolygon, itopo) {
// remove redundant edges from it
itopo->RemoveRedundantEdges();
}}
}
/*
* Remove polygon edges that are used twice from a polygon.
*/
/*
* We will use ope_Edge set to NULL for marking an edge for removal.
*/
void CObjectPolygon::RemoveRedundantEdges(void)
{
// set the counter of edges to current number of edges
INDEX ctEdges = opo_PolygonEdges.Count();
// for each edge
{FOREACHINDYNAMICARRAY(opo_PolygonEdges, CObjectPolygonEdge, itope) {
// if not already marked for removal
if (itope->ope_Edge != NULL) {
// for each edge
{FOREACHINDYNAMICARRAY(opo_PolygonEdges, CObjectPolygonEdge, itope2) {
// if it uses same edge in opposite direction
if (itope2->ope_Edge == itope->ope_Edge
&& itope2->ope_Backward != itope->ope_Backward ) {
// mark them both for removal
itope2->ope_Edge = NULL;
itope->ope_Edge = NULL;
// mark that there are two edges less
ctEdges -= 2;
// don't test this edge any more
break;
}
}}
}
}}
// remove polygon edges that are mark as unused
RemoveMarkedEdges(ctEdges);
}
/*
* Remove polygon edges that are marked as unused (oed_Edge==NULL) from polygon.
*/
void CObjectPolygon::RemoveMarkedEdges(INDEX ctEdges)
{
// create a new array of edge references
CDynamicArray<CObjectPolygonEdge> aoedNewPolygonEdges;
if (ctEdges>0) {
aoedNewPolygonEdges.New(ctEdges);
}
aoedNewPolygonEdges.Lock();
// for each edge
INDEX iNewEdge = 0;
{FOREACHINDYNAMICARRAY(opo_PolygonEdges, CObjectPolygonEdge, itope) {
// if it is not marked for removal
if (itope->ope_Edge != NULL) {
// copy it
aoedNewPolygonEdges[iNewEdge++] = itope.Current();
}
}}
// replace old array with the new one
aoedNewPolygonEdges.Unlock();
opo_PolygonEdges.Clear();
opo_PolygonEdges.MoveArray(aoedNewPolygonEdges);
}
/*
* Remove polygon edges that have zero length from a polygon.
*/
void CObjectPolygon::RemoveDummyEdgeReferences(void)
{
INDEX ctUsedEdges = opo_PolygonEdges.Count();
// for all edges in polygon
{FOREACHINDYNAMICARRAY(opo_PolygonEdges, CObjectPolygonEdge, itope) {
// if it has zero length
if (itope->ope_Edge->oed_Vertex0 == itope->ope_Edge->oed_Vertex1) {
ASSERT(CompareVertices(*itope->ope_Edge->oed_Vertex0, *itope->ope_Edge->oed_Vertex1)==0);
// mark it for removal
itope->ope_Edge = NULL;
ctUsedEdges--;
} else {
// !!!! why this fails sometimes? ASSERT(CompareVertices(*itope->ope_Edge->oed_Vertex0, *itope->ope_Edge->oed_Vertex1)!=0);
}
}}
// remove all marked edges from the polygon
RemoveMarkedEdges(ctUsedEdges);
}
/*
* Find edges that have zero length and remove them from all polygons.
*/
void CObjectSector::RemoveDummyEdgeReferences(void)
{
// for each polygon
{FOREACHINDYNAMICARRAY(osc_aopoPolygons, CObjectPolygon, itopo) {
// remove zero length edges from it
itopo->RemoveDummyEdgeReferences();
}}
}
/*
* Remap different edges with same or reverse vertices to use only one of each.
*/
void CObjectSector::RemapClonedEdges(void)
{
// if there are no edges in the sector
if (osc_aoedEdges.Count()==0) {
// do nothing
return;
}
osc_aoedEdges.Lock();
/* Prepare sorted arrays for remapping edges. */
// create array of pointers for sorting edges
INDEX ctEdges = osc_aoedEdges.Count();
CStaticArray<CObjectEdge *> apedSortedEdges;
apedSortedEdges.New(ctEdges);
// for all edges
for(INDEX iEdge=0; iEdge<ctEdges; iEdge++) {
// set the pointers in sorting array
apedSortedEdges[iEdge] = &osc_aoedEdges[iEdge];
// set remap pointer to itself
osc_aoedEdges[iEdge].optimize.oed_Remap = &osc_aoedEdges[iEdge];
// clear the inverse pointer
osc_aoedEdges[iEdge].optimize.oed_InverseEdge = NULL;
// clear the edge tag, meaning that this edge is unused
osc_aoedEdges[iEdge].oed_Tag = FALSE;
}
// sort the array of pointers, so that same edges get next to each other
qsort(&apedSortedEdges[0], ctEdges, sizeof(CObjectEdge *), qsort_CompareEdges);
/* Create remapping pointers. */
// for all edges in normal sorted array, except the last one
for(INDEX iSortedEdge=0; iSortedEdge<ctEdges-1; iSortedEdge++) {
// if the next plane is same as this one
if ( CompareEdges(*apedSortedEdges[iSortedEdge],
*apedSortedEdges[iSortedEdge+1]) == 0 ) {
// set its remap pointer to same as this remap pointer
apedSortedEdges[iSortedEdge+1]->optimize.oed_Remap = apedSortedEdges[iSortedEdge]->optimize.oed_Remap;
}
}
/* Create inverse pointers. */
// for all edges in sorted array
for(INDEX iNormalEdge=0; iNormalEdge<ctEdges; iNormalEdge++) {
CObjectEdge &edNormal = *apedSortedEdges[iNormalEdge];
CObjectEdge *pedInverse;
// if this edge is not remapped
if (edNormal.optimize.oed_Remap == &edNormal) {
// if some edge with inverse vertices is found
if (FindEdge(apedSortedEdges, *edNormal.oed_Vertex1, *edNormal.oed_Vertex0, &pedInverse)) {
// take its remap edge
CObjectEdge &edInverse = *pedInverse->optimize.oed_Remap;
// if the inverse edge pointer is less than this pointer
if (&edInverse < &edNormal) {
// the inverse edge must not be remapped
ASSERT(&edInverse == edInverse.optimize.oed_Remap);
// set the inverse pointer in this edge to the inverse edge
edNormal.optimize.oed_InverseEdge = &edInverse;
}
}
}
}
#ifndef NDEBUG
// for all edges in object
{FOREACHINDYNAMICARRAY(osc_aoedEdges, CObjectEdge, ited) {
CObjectEdge &edInverse = *ited->optimize.oed_InverseEdge;
// check that no remapped edges have been marked as inverses
ASSERT( &edInverse==NULL || edInverse.optimize.oed_Remap == &edInverse );
}}
#endif // NDEBUG
/* Remap all references to edges. */
// for all polygons in object
{FOREACHINDYNAMICARRAY(osc_aopoPolygons, CObjectPolygon, itpo2) {
// for all of its edge pointers
FOREACHINDYNAMICARRAY(itpo2->opo_PolygonEdges, CObjectPolygonEdge, itope) {
// get the remapped edge pointer
CObjectEdge *pedNew= itope->ope_Edge->optimize.oed_Remap;
// if has an inverse edge
if (pedNew->optimize.oed_InverseEdge!=NULL) {
// use the inverse edge
pedNew = pedNew->optimize.oed_InverseEdge;
// mark that the direction has changed
itope->ope_Backward = !itope->ope_Backward;
}
// use the edge
itope->ope_Edge = pedNew;
}
}}
osc_aoedEdges.Unlock();
}
/*
* Remove unused polygons and polygons with less than 3 edges.
*/
void CObjectSector::RemoveDummyPolygons(void)
{
// for each polygon
INDEX ctUsedPolygons = 0;
{FOREACHINDYNAMICARRAY(osc_aopoPolygons, CObjectPolygon, itopo) {
// if it has more than 2 edges
if(itopo->opo_PolygonEdges.Count()>2) {
// mark it as used
itopo->opo_Tag = TRUE;
// count it
ctUsedPolygons++;
// if it has less than 3 edges
} else {
// mark it as unused
itopo->opo_Tag = FALSE;
}
}}
// create a new array of polygons
CDynamicArray<CObjectPolygon> aopoNew;
CObjectPolygon *popoNew = aopoNew.New(ctUsedPolygons);
// for each polygon in sector
{FOREACHINDYNAMICARRAY(osc_aopoPolygons, CObjectPolygon, itopo) {
// if it is used
if (itopo->opo_Tag) {
// add it to the new array
*popoNew++ = itopo.Current();
}
}}
// replace old array with the new one
osc_aopoPolygons.Clear();
osc_aopoPolygons.MoveArray(aopoNew);
}
/*
* Remove unused and replicated elements.
*/
void CObjectSector::Optimize(void)
{
/*
// for each vertex
{FOREACHINDYNAMICARRAY(osc_aovxVertices, CObjectVertex, itovx) {
// snap the vertex coordinates
Snap((*itovx)(1), VTX_SNAP);
Snap((*itovx)(2), VTX_SNAP);
Snap((*itovx)(3), VTX_SNAP);
}}
// for each plane
{FOREACHINDYNAMICARRAY(osc_aoplPlanes, CObjectPlane, itopl) {
// snap the plane coordinates
Snap((*itopl)(1), PLX_SNAP);
Snap((*itopl)(2), PLX_SNAP);
Snap((*itopl)(3), PLX_SNAP);
Snap((*itopl).Distance(), PLX_SNAP);
}}
*/
// remove polygons with less than 3 edges
RemoveDummyPolygons();
// remap different vertices with same coordinates
RemapClonedVertices();
// remove unused vertices
RemoveUnusedVertices();
// find edges that have zero length and remove them from all polygons
RemoveDummyEdgeReferences();
// remap different edges with same or reverse vertices
RemapClonedEdges();
// remove unused edges
RemoveUnusedEdges();
// find collinear edges and cross split them with each other
SplitCollinearEdges();
// find edges that are used twice in same polygon and remove them from the polygon
RemoveRedundantPolygonEdges();
// find edges that are collinear and continuing in some polygon and join them
JoinContinuingPolygonEdges();
// find edges that have zero length and remove them from all polygons
RemoveDummyEdgeReferences();
// remap different edges with same or reverse vertices
RemapClonedEdges();
// remove unused edges
RemoveUnusedEdges();
// remove unused vertices
RemoveUnusedVertices();
// find collinear edges and cross split them with each other
SplitCollinearEdges();
// find edges that have zero length and remove them from all polygons
RemoveDummyEdgeReferences();
// remap different edges with same or reverse vertices
RemapClonedEdges();
// remove unused edges
RemoveUnusedEdges();
// remove unused vertices
RemoveUnusedVertices();
// find edges that are used twice in same polygon and remove them from the polygon
RemoveRedundantPolygonEdges();
// remap different planes with same coordinates
RemapClonedPlanes();
// remove unused planes
RemoveUnusedPlanes();
// remove polygons with less than 3 edges
RemoveDummyPolygons();
#ifndef NDEBUG
// check the optimization algorithm
CheckOptimizationAlgorithm();
#endif
}
/*
* Assignment operator.
*/
CObjectSector &CObjectSector::operator=(CObjectSector &oscOriginal)
{
// remove current contents
Clear();
// copy basic properties
osc_colColor = oscOriginal.osc_colColor;
osc_colAmbient = oscOriginal.osc_colAmbient;
osc_ulFlags[0] = oscOriginal.osc_ulFlags[0];
osc_ulFlags[1] = oscOriginal.osc_ulFlags[1];
osc_ulFlags[2] = oscOriginal.osc_ulFlags[2];
osc_strName = oscOriginal.osc_strName;
// create indices in original sector
oscOriginal.CreateIndices();
// copy arrays of elements from original sector
osc_aovxVertices = oscOriginal.osc_aovxVertices;
osc_aoplPlanes = oscOriginal.osc_aoplPlanes;
osc_aomtMaterials = oscOriginal.osc_aomtMaterials;
osc_aoedEdges = oscOriginal.osc_aoedEdges;
osc_aopoPolygons = oscOriginal.osc_aopoPolygons;
// lock all arrays
LockAll();
// for all edges
FOREACHINDYNAMICARRAY(osc_aoedEdges, CObjectEdge, itoed) {
// use vertices in this object with same index
itoed->oed_Vertex0 = &osc_aovxVertices[itoed->oed_Vertex0->ovx_Index];
itoed->oed_Vertex1 = &osc_aovxVertices[itoed->oed_Vertex1->ovx_Index];
}
// for all polygons
FOREACHINDYNAMICARRAY(osc_aopoPolygons, CObjectPolygon, itopo) {
// use plane and material in this sector with same indices
itopo->opo_Plane = &osc_aoplPlanes[itopo->opo_Plane->opl_Index];
if (itopo->opo_Material!=NULL) {
itopo->opo_Material = &osc_aomtMaterials[itopo->opo_Material->omt_Index];
}
// for all polygon-edges in this polygon
FOREACHINDYNAMICARRAY(itopo->opo_PolygonEdges, CObjectPolygonEdge, itope) {
// use edge in this sector with same index
itope->ope_Edge = &osc_aoedEdges[itope->ope_Edge->oed_Index];
}
}
// unlock all arrays
UnlockAll();
return *this;
}
/*
* Find bounding box of the sector.
*/
void CObjectSector::GetBoundingBox(DOUBLEaabbox3D &boxSector)
{
// clear the bounding box
boxSector = DOUBLEaabbox3D();
// for each vertex in the sector
FOREACHINDYNAMICARRAY(osc_aovxVertices, CObjectVertex, itovx) {
// add the vertex to the bounding box
boxSector |= *itovx;
}
}
/*
* Create BSP tree for sector.
*/
void CObjectSector::CreateBSP(void)
{
/* prepare array of bsp polygons */
// get count of polygons in sector
const INDEX ctPolygons = osc_aopoPolygons.Count();
// create array of BSP polygons
CDynamicArray<DOUBLEbsppolygon3D> arbpo;
// create as much BSP polygons as there are polygons in this sector
arbpo.New(ctPolygons);
arbpo.Lock();
osc_aopoPolygons.Lock();
// for each polygon in this sector
for(INDEX iPolygon=0; iPolygon<ctPolygons; iPolygon++) {
CObjectPolygon &opo = osc_aopoPolygons[iPolygon];
DOUBLEbsppolygon3D &bpo = arbpo[iPolygon];
// copy the plane
(DOUBLEplane3D &)bpo = *opo.opo_Plane;
bpo.bpo_ulPlaneTag = (ULONG)opo.opo_Plane;
// get count of edges in this polygon
const INDEX ctEdges = opo.opo_PolygonEdges.Count();
// create that much edges in bsp polygon
DOUBLEbspedge3D *pbed = bpo.bpo_abedPolygonEdges.New(ctEdges);
opo.opo_PolygonEdges.Lock();
// for each edge in this polygon
for(INDEX iEdge=0; iEdge<ctEdges; iEdge++) {
CObjectPolygonEdge &ope = opo.opo_PolygonEdges[iEdge];
CObjectEdge &oed = *ope.ope_Edge;
// if the edge is reversed
if(ope.ope_Backward) {
// add bsp edge with reversed vertices
pbed[iEdge] = DOUBLEbspedge3D(*oed.oed_Vertex1, *oed.oed_Vertex0, (ULONG)&oed);
// otherwise
} else {
// add normal bsp edge
pbed[iEdge] = DOUBLEbspedge3D(*oed.oed_Vertex0, *oed.oed_Vertex1, (ULONG)&oed);
}
}
opo.opo_PolygonEdges.Unlock();
}
arbpo.Unlock();
osc_aopoPolygons.Unlock();
/* create bsp tree from array of bsp polygons */
osc_BSPTree.Create(arbpo);
}
/*
* Turn sector inside-out.
*/
void CObjectSector::Inverse(void)
{
// for all planes
FOREACHINDYNAMICARRAY(osc_aoplPlanes, CObjectPlane, itopl) {
// flip the plane
(DOUBLEplane3D &)*itopl = -(DOUBLEplane3D &)*itopl;
}
// for all polygons
FOREACHINDYNAMICARRAY(osc_aopoPolygons, CObjectPolygon, itopo) {
// for all polygon-edges in this polygon
FOREACHINDYNAMICARRAY(itopo->opo_PolygonEdges, CObjectPolygonEdge, itope) {
// reverse the polygon edge direction
itope->ope_Backward = !itope->ope_Backward;
}
}
}
/* Recalculate all planes from vertices. (used when stretching vertices) */
void CObjectSector::RecalculatePlanes(void)
{
// create a container for empty polygons
CDynamicContainer<CObjectPolygon> copoEmpty;
// for all polygons
{FOREACHINDYNAMICARRAY(osc_aopoPolygons, CObjectPolygon, itopo) {
CObjectPolygon &opo = *itopo;
// clear plane normal
DOUBLE3D vNormal = DOUBLE3D(0.0f,0.0f,0.0f);
// for all edges in polygon
INDEX ctVertices = opo.opo_PolygonEdges.Count();
opo.opo_PolygonEdges.Lock();
{for(INDEX iVertex=0; iVertex<ctVertices; iVertex++) {
// get its vertices in counterclockwise order
CObjectPolygonEdge &ope = opo.opo_PolygonEdges[iVertex];
CObjectVertex *povx0, *povx1;
if (ope.ope_Backward) {
povx0 = ope.ope_Edge->oed_Vertex1;
povx1 = ope.ope_Edge->oed_Vertex0;
} else {
povx0 = ope.ope_Edge->oed_Vertex0;
povx1 = ope.ope_Edge->oed_Vertex1;
}
DOUBLE3D vSum = *povx0+*povx1;
DOUBLE3D vDif = *povx0-*povx1;
// add the edge contribution to the normal vector
vNormal(1) += vDif(2)*vSum(3);
vNormal(2) += vDif(3)*vSum(1);
vNormal(3) += vDif(1)*vSum(2);
}}
// if the polygon area is too small
if (vNormal.Length()<1E-8) {
// mark it for removal
copoEmpty.Add(&opo);
// if the polygon area is ok
} else {
// add one plane to planes array
CObjectPlane *pplPlane = osc_aoplPlanes.New();
// construct this plane from normal vector and one point
*pplPlane= DOUBLEplane3D(vNormal, *opo.opo_PolygonEdges[0].ope_Edge->oed_Vertex0);
opo.opo_Plane = pplPlane;
}
opo.opo_PolygonEdges.Unlock();
}}
// for all empty polygons
{FOREACHINDYNAMICCONTAINER(copoEmpty, CObjectPolygon, itopoEmpty) {
// delete the polygon from sector
osc_aopoPolygons.Delete(itopoEmpty);
}}
}