/* Copyright (c) 2002-2012 Croteam Ltd. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "stdh.h" #include #include #include #include #include #include inline void Clear(CObjectEdge *poed) {}; /* * Default constructor. */ CObject3D::CObject3D() { }; /* * Destructor. */ CObject3D::~CObject3D() { Clear(); }; void CObject3D::Clear(void) { ob_aoscSectors.Clear(); // clear sectors array } /* * Create indices for all sectors. */ void CObject3D::CreateSectorIndices(void) { ob_aoscSectors.Lock(); // get the number of sectors in object INDEX ctSectors = ob_aoscSectors.Count(); // set sectors indices for(INDEX iSector=0; iSectorArePolygonsPlanar()) return FALSE; } return TRUE; } /* * Project the whole object into some other space. */ void CObject3D::Project(CSimpleProjection3D_DOUBLE &pr) { ASSERT(GetFPUPrecision()==FPT_53BIT); // check if projection is mirrored const FLOAT3D &vObjectStretch = pr.ObjectStretchR(); BOOL bXInverted = vObjectStretch(1)<0; BOOL bYInverted = vObjectStretch(2)<0; BOOL bZInverted = vObjectStretch(3)<0; BOOL bInverted = bXInverted!=bYInverted!=bZInverted; // for all sectors FOREACHINDYNAMICARRAY(ob_aoscSectors, CObjectSector, itsc) { // for all vertices in sector FOREACHINDYNAMICARRAY(itsc->osc_aovxVertices, CObjectVertex, itvx) { // project the vertex pr.ProjectCoordinate(*itvx, *itvx); } /* NOTE: We must project polygons _before_ planes, since projecting of texture mapping coefficients requires unprojected plane! */ // for all polygons in sector FOREACHINDYNAMICARRAY(itsc->osc_aopoPolygons, CObjectPolygon, itpo) { // project mapping pr.ProjectMapping(itpo->opo_amdMappings[0], *itpo->opo_Plane, itpo->opo_amdMappings[0]); pr.ProjectMapping(itpo->opo_amdMappings[1], *itpo->opo_Plane, itpo->opo_amdMappings[1]); pr.ProjectMapping(itpo->opo_amdMappings[2], *itpo->opo_Plane, itpo->opo_amdMappings[2]); pr.ProjectMapping(itpo->opo_amdMappings[3], *itpo->opo_Plane, itpo->opo_amdMappings[3]); // if projection is inverted if (bInverted) { // invert all polygon edges {FOREACHINDYNAMICARRAY(itpo->opo_PolygonEdges, CObjectPolygonEdge, itope) { CObjectPolygonEdge &ope = *itope; ope.ope_Backward = !ope.ope_Backward; }} } } // for all planes in sector FOREACHINDYNAMICARRAY(itsc->osc_aoplPlanes, CObjectPlane, itpl) { // project the plane pr.Project(*itpl, *itpl); } } } /* * Assignment operator. */ CObject3D &CObject3D::operator=(CObject3D &obOriginal) { // copy array of sectors from original object, sectors will copy their contents ob_aoscSectors = obOriginal.ob_aoscSectors; return *this; } /* * Create BSP trees for all sectors. */ void CObject3D::CreateSectorBSPs(void) { ASSERT(GetFPUPrecision()==FPT_53BIT); // for each sector in object FOREACHINDYNAMICARRAY(ob_aoscSectors, CObjectSector, itosc) { // create its BSP tree itosc->CreateBSP(); } } /* * Remove sectors with no polygons. */ void CObject3D::RemoveEmptySectors(void) { // create a container for empty sectors CDynamicContainer coscEmpty; // for all sectors in object {FOREACHINDYNAMICARRAY(ob_aoscSectors, CObjectSector, itosc) { // if it has no polygons if (itosc->osc_aopoPolygons.Count() == 0) { // add the sector to the container of empty sectors coscEmpty.Add(&itosc.Current()); } }} // for all empty sectors {FOREACHINDYNAMICCONTAINER(coscEmpty, CObjectSector, itoscEmpty) { // delete the sector from object ob_aoscSectors.Delete(&itoscEmpty.Current()); }} } /* * Remove unused and replicated elements. */ void CObject3D::Optimize(void) { ASSERT(GetFPUPrecision()==FPT_53BIT); // for all sectors in the object {FOREACHINDYNAMICARRAY(ob_aoscSectors, CObjectSector, itosc) { // optimize the sector itosc->Optimize(); }} // remove sectors that have no polygons RemoveEmptySectors(); } /* * Turn all sectors in object inside-out. (not recommended for multi sector objects) */ void CObject3D::Inverse(void) { ASSERT(GetFPUPrecision()==FPT_53BIT); // for all sectors in object {FOREACHINDYNAMICARRAY(ob_aoscSectors, CObjectSector, itosc) { // inverse the sector itosc->Inverse(); }} } /* Recalculate all planes from vertices. (used when stretching vertices) */ void CObject3D::RecalculatePlanes(void) { ASSERT(GetFPUPrecision()==FPT_53BIT); // for all sectors in object {FOREACHINDYNAMICARRAY(ob_aoscSectors, CObjectSector, itosc) { // recalculate all planes in sector itosc->RecalculatePlanes(); }} } /* * Turn all portals to walls. */ void CObject3D::TurnPortalsToWalls(void) { // for all sectors in object {FOREACHINDYNAMICARRAY(ob_aoscSectors, CObjectSector, itosc) { // for all polygons {FOREACHINDYNAMICARRAY(itosc->osc_aopoPolygons, CObjectPolygon, itopo) { // clear the portal flag itopo->opo_ulFlags &= ~OPOF_PORTAL; }} }} } /* * Find bounding box of the object. */ void CObject3D::GetBoundingBox(DOUBLEaabbox3D &boxObject) { ASSERT(GetFPUPrecision()==FPT_53BIT); // clear the bounding box boxObject = DOUBLEaabbox3D(); // for each sector in the object FOREACHINDYNAMICARRAY(ob_aoscSectors, CObjectSector, itosc) { // get box of the sector DOUBLEaabbox3D boxSector; itosc->GetBoundingBox(boxSector); // add the sector box to the bounding box boxObject |= boxSector; } } /* Dump the object 3D to debug window. */ void CObject3D::DebugDump(void) { #ifndef NDEBUG _RPT0(_CRT_WARN, "Object3D dump BEGIN:\n"); _RPT1(_CRT_WARN, "Sectors: %d\n", ob_aoscSectors.Count()); {FOREACHINDYNAMICARRAY(ob_aoscSectors, CObjectSector, itosc) { _RPT1(_CRT_WARN, "SC%d:\n", ob_aoscSectors.Index(&itosc.Current())); _RPT1(_CRT_WARN, "Vertices: %d\n", itosc->osc_aovxVertices.Count()); {FOREACHINDYNAMICARRAY(itosc->osc_aovxVertices, CObjectVertex, itovx) { _RPT4(_CRT_WARN, "VX%d: (%f, %f, %f)\n", itosc->osc_aovxVertices.Index(itovx), (*itovx)(1), (*itovx)(2), (*itovx)(3)); }} _RPT1(_CRT_WARN, "Planes: %d\n", itosc->osc_aoplPlanes.Count()); {FOREACHINDYNAMICARRAY(itosc->osc_aoplPlanes, CObjectPlane, itopl) { _RPT4(_CRT_WARN, "PL%d: (%g, %g, %g)", itosc->osc_aoplPlanes.Index(&itopl.Current()), itopl.Current()(1), itopl.Current()(2), itopl.Current()(3)); _RPT1(_CRT_WARN, ":%g\n", itopl->Distance()); }} _RPT1(_CRT_WARN, "Edges: %d\n", itosc->osc_aoedEdges.Count()); itosc->osc_aovxVertices.Lock(); {FOREACHINDYNAMICARRAY(itosc->osc_aoedEdges, CObjectEdge, itoed) { _RPT3(_CRT_WARN, "ED%d: VX%d -> VX%d\n", itosc->osc_aoedEdges.Index(&itoed.Current()), itosc->osc_aovxVertices.Index(itoed->oed_Vertex0), itosc->osc_aovxVertices.Index(itoed->oed_Vertex1)); }} itosc->osc_aovxVertices.Unlock(); _RPT1(_CRT_WARN, "Polygons: %d\n", itosc->osc_aopoPolygons.Count()); itosc->osc_aovxVertices.Lock(); itosc->osc_aoedEdges.Lock(); itosc->osc_aoplPlanes.Lock(); {FOREACHINDYNAMICARRAY(itosc->osc_aopoPolygons, CObjectPolygon, itopo) { _RPT3(_CRT_WARN, "PO%d (PL%d): Edges: %d\n ", itosc->osc_aopoPolygons.Index(&itopo.Current()), itosc->osc_aoplPlanes.Index(itopo->opo_Plane), itopo->opo_PolygonEdges.Count()); {FOREACHINDYNAMICARRAY(itopo->opo_PolygonEdges, CObjectPolygonEdge, itope) { _RPT1(_CRT_WARN, "ED%d", itosc->osc_aoedEdges.Index(itope->ope_Edge)); CObjectVertex *povx0, *povx1; if (itope->ope_Backward) { povx0 = itope->ope_Edge->oed_Vertex1; povx1 = itope->ope_Edge->oed_Vertex0; } else { povx0 = itope->ope_Edge->oed_Vertex0; povx1 = itope->ope_Edge->oed_Vertex1; } _RPT4(_CRT_WARN, " (VX%d (%f,%f,%f)", itosc->osc_aovxVertices.Index(povx0), (*povx0)(1), (*povx0)(2), (*povx0)(3)); _RPT4(_CRT_WARN, "->VX%d (%f,%f,%f)) ", itosc->osc_aovxVertices.Index(povx1), (*povx1)(1), (*povx1)(2), (*povx1)(3)); }} _RPT0(_CRT_WARN, "\n"); }} itosc->osc_aoplPlanes.Unlock(); itosc->osc_aoedEdges.Unlock(); itosc->osc_aovxVertices.Unlock(); }} _RPT0(_CRT_WARN, "Object3D dump END:\n"); #endif // NDEBUG }