/* Copyright (c) 2002-2012 Croteam Ltd. All rights reserved. */ #include "stdh.h" #include #include #include #include #include // 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_Vertex0ed1.oed_Vertex0) return +1; else if (ed0.oed_Vertex1ed1.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 &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.999oed_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; iVertexoed_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 applSortedPlanes; applSortedPlanes.New(ctPlanes); // for all vertices for(INDEX iPlane=0; iPlaneopl_Remap = applSortedPlanes[iSortedPlane]->opl_Remap; } } } else { // for all pairs of planes {for(INDEX iPlane1=0; iPlane1opo_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 apvxSortedVertices; apvxSortedVertices.New(ctVertices); // for all vertices for(INDEX iVertex=0; iVertexovx_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; iVertex1oed_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; iSortedVertexovx_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 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 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 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 &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 aedxEdgeLines; CStaticArray 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 &oedEdges) { // create an empty array for newly created edges CDynamicArray 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 &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 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 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 &aedxEdgeLines, CStaticArray &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 aedxEdgeLines; CStaticArray 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; iSortedEdgeopo_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 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 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 apedSortedEdges; apedSortedEdges.New(ctEdges); // for all edges for(INDEX iEdge=0; iEdgeoptimize.oed_Remap = apedSortedEdges[iSortedEdge]->optimize.oed_Remap; } } /* Create inverse pointers. */ // for all edges in sorted array for(INDEX iNormalEdge=0; iNormalEdgeoptimize.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 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 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; iPolygonopo_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 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; iVertexoed_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); }} }