/* 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 "Engine/StdH.h" #include #include #include #include #include #include #include #include #include #include template class CStaticArray; template class CStaticArray; template class CStaticArray; // set new absolute position for the vertex void CBrushVertex::SetAbsolutePosition(const DOUBLE3D &vAbsolute) { // get its brush entity CEntity *pen = bvx_pbscSector->bsc_pbmBrushMip->bm_pbrBrush->br_penEntity; if (pen==NULL) { ASSERT(FALSE); return; } // back-transform to relative coordinates DOUBLE3D vRelative = vAbsolute-FLOATtoDOUBLE(pen->en_plPlacement.pl_PositionVector); vRelative *= FLOATtoDOUBLE(!pen->en_mRotation); // remember new coordinates bvx_vdPreciseRelative = vRelative; bvx_vAbsolute = DOUBLEtoFLOAT(vAbsolute); bvx_vRelative = DOUBLEtoFLOAT(vRelative); if(bvx_pwvxWorking!=NULL) { bvx_pwvxWorking->wvx_vRelative = bvx_vRelative; } } /* * Calculate bounding box of this polygon. */ void CBrushPolygon::CalculateBoundingBox(void) { // NOTE: vertices are already transformed to absolute space // discard portal-sector links to this polygon extern BOOL _bDontDiscardLinks; if (!(bpo_pbscSector->bsc_ulTempFlags&BSCTF_PRELOADEDLINKS)&&!_bDontDiscardLinks) { bpo_rsOtherSideSectors.Clear(); } // clear the bounding box bpo_boxBoundingBox = FLOATaabbox3D(); // for all edges in polygon {FOREACHINSTATICARRAY(bpo_abpePolygonEdges, CBrushPolygonEdge, itbpe) { // add the edges vertices to the bounding box bpo_boxBoundingBox |= itbpe->bpe_pbedEdge->bed_pbvxVertex0->bvx_vAbsolute; bpo_boxBoundingBox |= itbpe->bpe_pbedEdge->bed_pbvxVertex1->bvx_vAbsolute; }} } /* Create a BSP polygon from this polygon. */ void CBrushPolygon::CreateBSPPolygon(BSPPolygon &bspo) { ASSERT(GetFPUPrecision()==FPT_53BIT); CBrushPolygon &brpo = *this; // set the plane of the bsp polygon ((DOUBLEplane3D &)bspo) = *brpo.bpo_pbplPlane->bpl_ppldPreciseAbsolute; bspo.bpo_ulPlaneTag = (size_t)brpo.bpo_pbscSector->bsc_abplPlanes.Index(brpo.bpo_pbplPlane); // create the array of edges in the bsp polygon INDEX ctEdges = brpo.bpo_abpePolygonEdges.Count(); bspo.bpo_abedPolygonEdges.New(ctEdges); // for all edges in the polygon bspo.bpo_abedPolygonEdges.Lock(); {for(INDEX iEdge=0; iEdge &bed = bspo.bpo_abedPolygonEdges[iEdge]; // create the bsp edge in the bsp polygon brped.GetVertexCoordinatesPreciseAbsolute(bed.bed_vVertex0, bed.bed_vVertex1); }} bspo.bpo_abedPolygonEdges.Unlock(); } void CBrushPolygon::CreateBSPPolygonNonPrecise(BSPPolygon &bspo) { CBrushPolygon &brpo = *this; // offset for epsilon testing const DOUBLE fOffset = -0.01; // set the plane of the bsp polygon ((DOUBLEplane3D &)bspo) = FLOATtoDOUBLE(brpo.bpo_pbplPlane->bpl_plAbsolute); bspo.bpo_ulPlaneTag = (size_t)brpo.bpo_pbscSector->bsc_abplPlanes.Index(brpo.bpo_pbplPlane); // calculate offset for points DOUBLE3D vOffset = FLOATtoDOUBLE(((FLOAT3D&)brpo.bpo_pbplPlane->bpl_plAbsolute))*-fOffset; // offset the plane bspo.Offset(fOffset); // create the array of edges in the bsp polygon INDEX ctEdges = brpo.bpo_abpePolygonEdges.Count(); bspo.bpo_abedPolygonEdges.New(ctEdges); // for all edges in the polygon bspo.bpo_abedPolygonEdges.Lock(); {for(INDEX iEdge=0; iEdge &bed = bspo.bpo_abedPolygonEdges[iEdge]; // create the offseted bsp edge in the bsp polygon FLOAT3D v0, v1; brped.GetVertexCoordinatesAbsolute(v0, v1); bed.bed_vVertex0 = FLOATtoDOUBLE(v0)+vOffset; bed.bed_vVertex1 = FLOATtoDOUBLE(v1)+vOffset; }} bspo.bpo_abedPolygonEdges.Unlock(); } /* * Select adjacent polygons with same color as this one. */ void CBrushPolygon::SelectSimilarByColor(CBrushPolygonSelection &selbpoSimilar) { // if this polygon is not selected if (!IsSelected(BPOF_SELECTED)) { // select this polygon selbpoSimilar.Select(*this); } // for all other unselected walls in brush mip that have same color FOREACHINDYNAMICARRAY(bpo_pbscSector->bsc_pbmBrushMip->bm_abscSectors, CBrushSector, itbsc) { FOREACHINSTATICARRAY(itbsc->bsc_abpoPolygons, CBrushPolygon, itbpoOther) { if ((!(itbpoOther->bpo_ulFlags&BPOF_PORTAL)||(itbpoOther->bpo_ulFlags&(BPOF_TRANSLUCENT|BPOF_TRANSPARENT))) && !itbpoOther->IsSelected(BPOF_SELECTED) && itbpoOther->bpo_colColor == bpo_colColor) { // if the other polygon touches this one if (TouchesInAnySector(*itbpoOther)) { // recursively select the other polygon itbpoOther->SelectSimilarByColor(selbpoSimilar); } } } } } /* * Select adjacent polygons with same texture as this one. */ void CBrushPolygon::SelectSimilarByTexture( CBrushPolygonSelection &selbpoSimilar, INDEX iTexture) { // if this polygon is not selected if (!IsSelected(BPOF_SELECTED)) { // select this polygon selbpoSimilar.Select(*this); } // for all other unselected walls in brush mip that have same texture FOREACHINDYNAMICARRAY(bpo_pbscSector->bsc_pbmBrushMip->bm_abscSectors, CBrushSector, itbsc) { FOREACHINSTATICARRAY(itbsc->bsc_abpoPolygons, CBrushPolygon, itbpoOther) { if ((!(itbpoOther->bpo_ulFlags&BPOF_PORTAL)||(itbpoOther->bpo_ulFlags&(BPOF_TRANSLUCENT|BPOF_TRANSPARENT))) && !itbpoOther->IsSelected(BPOF_SELECTED) && itbpoOther->bpo_abptTextures[iTexture].bpt_toTexture.GetData() == bpo_abptTextures[iTexture].bpt_toTexture.GetData()) { // if the other polygon touches this one if (TouchesInAnySector(*itbpoOther)) { // recursively select the other polygon itbpoOther->SelectSimilarByTexture(selbpoSimilar, iTexture); } } } } } /* * Select all polygons in sector with same texture as this one. */ void CBrushPolygon::SelectByTextureInSector(CBrushPolygonSelection &selbpoSimilar, INDEX iTexture) { // for all other unselected walls in sector that have same texture FOREACHINSTATICARRAY(bpo_pbscSector->bsc_abpoPolygons, CBrushPolygon, itbpo) { if ((!(itbpo->bpo_ulFlags&BPOF_PORTAL)||(itbpo->bpo_ulFlags&(BPOF_TRANSLUCENT|BPOF_TRANSPARENT)))&& !itbpo->IsSelected(BPOF_SELECTED) && itbpo->bpo_abptTextures[iTexture].bpt_toTexture.GetData() == bpo_abptTextures[iTexture].bpt_toTexture.GetData()) { // select this polygon selbpoSimilar.Select(*itbpo); } } } /* * Select all polygons in sector with same color as this one. */ void CBrushPolygon::SelectByColorInSector(CBrushPolygonSelection &selbpoSimilar) { // for all other unselected walls in sector that have same color FOREACHINSTATICARRAY(bpo_pbscSector->bsc_abpoPolygons, CBrushPolygon, itbpo) { if ((!(itbpo->bpo_ulFlags&BPOF_PORTAL)||(itbpo->bpo_ulFlags&(BPOF_TRANSLUCENT|BPOF_TRANSPARENT))) && !itbpo->IsSelected(BPOF_SELECTED) && itbpo->bpo_colColor == bpo_colColor) { // select this polygon selbpoSimilar.Select(*itbpo); } } } /* Clear the object. */ void CBrushPolygon::Clear(void) { bpo_abpePolygonEdges.Clear(); bpo_smShadowMap.Clear(); bpo_abptTextures[0].Clear(); bpo_abptTextures[1].Clear(); bpo_abptTextures[2].Clear(); DiscardShadingInfos(); }; // discard all cached shading info for models void CBrushPolygon::DiscardShadingInfos(void) { FORDELETELIST( CShadingInfo, si_lnInPolygon, bpo_lhShadingInfos, itsi) { itsi->si_penEntity->en_ulFlags &= ~ENF_VALIDSHADINGINFO; itsi->si_lnInPolygon.Remove(); itsi->si_pbpoPolygon = NULL; } } /* * Copy polygon within same sector. */ void CBrushPolygon::CopyFromSameSector(CBrushPolygon &bpoOriginal) { // copy simple data bpo_pbplPlane = bpoOriginal.bpo_pbplPlane; bpo_colColor = bpoOriginal.bpo_colColor; bpo_ulFlags = bpoOriginal.bpo_ulFlags; BOOL bCopyMapping = TRUE; bpo_abptTextures[0].CopyTextureProperties( bpoOriginal.bpo_abptTextures[0], bCopyMapping); bpo_abptTextures[1].CopyTextureProperties( bpoOriginal.bpo_abptTextures[1], bCopyMapping); bpo_abptTextures[2].CopyTextureProperties( bpoOriginal.bpo_abptTextures[2], bCopyMapping); bpo_mdShadow = bpoOriginal.bpo_mdShadow; bpo_pbscSector = bpoOriginal.bpo_pbscSector; // copy all edges bpo_abpePolygonEdges = bpoOriginal.bpo_abpePolygonEdges; } /* Copy polygon properties */ CBrushPolygon &CBrushPolygon::CopyProperties(CBrushPolygon &bpoOther, BOOL bCopyMapping) { bpo_ulFlags &= ~BPOF_MASK_FOR_COPYING; bpo_ulFlags |= (bpoOther.bpo_ulFlags&BPOF_MASK_FOR_COPYING); bpo_bppProperties = bpoOther.bpo_bppProperties; bpo_colShadow = bpoOther.bpo_colShadow; bpo_abptTextures[0].CopyTextureProperties( bpoOther.bpo_abptTextures[0], bCopyMapping); bpo_abptTextures[1].CopyTextureProperties( bpoOther.bpo_abptTextures[1], bCopyMapping); bpo_abptTextures[2].CopyTextureProperties( bpoOther.bpo_abptTextures[2], bCopyMapping); return *this; }; /* Copy polygon properties without texture */ CBrushPolygon &CBrushPolygon::CopyPropertiesWithoutTexture(CBrushPolygon &bpoOther) { bpo_ulFlags &= ~BPOF_MASK_FOR_COPYING; bpo_ulFlags |= (bpoOther.bpo_ulFlags&BPOF_MASK_FOR_COPYING); bpo_bppProperties = bpoOther.bpo_bppProperties; bpo_colShadow = bpoOther.bpo_colShadow; return *this; }; /* Copy polygon's textures */ CBrushPolygon &CBrushPolygon::CopyTextures(CBrushPolygon &bpoOther) { bpo_abptTextures[0].CopyTextureProperties( bpoOther.bpo_abptTextures[0], TRUE); bpo_abptTextures[1].CopyTextureProperties( bpoOther.bpo_abptTextures[1], TRUE); bpo_abptTextures[2].CopyTextureProperties( bpoOther.bpo_abptTextures[2], TRUE); return *this; }; /* * Calculate area of the polygon. */ DOUBLE CBrushPolygon::CalculateArea(void) { ASSERT(GetFPUPrecision()==FPT_53BIT); DOUBLE3D vArea = DOUBLE3D(0.0, 0.0, 0.0); // for each polygon edge {FOREACHINSTATICARRAY(bpo_abpePolygonEdges, CBrushPolygonEdge, itbpe) { DOUBLE3D v0, v1; itbpe->GetVertexCoordinatesPreciseRelative(v0, v1); // add the area of triangle that the edge closes with the origin vArea += v0*v1; }} return ( ((DOUBLE3D&)bpo_pbplPlane->bpl_pldPreciseRelative)%vArea ) / 2.0; } // move edges from another polygon into this one void CBrushPolygon::MovePolygonEdges(CBrushPolygon &bpoSource) { ASSERT(bpo_pbplPlane==bpoSource.bpo_pbplPlane); INDEX ctEdgesThis = bpo_abpePolygonEdges.Count(); INDEX ctEdgesSource = bpoSource.bpo_abpePolygonEdges.Count(); // create an array to hold all edges CStaticArray abpeNew; abpeNew.New(ctEdgesThis+ctEdgesSource); INDEX ibpeNew = 0; // copy edges of this polygon {for(INDEX ibpeThis=0; ibpeThisbvx_vRelative-bed_pbvxVertex1->bvx_vRelative).Length(); FLOAT fB0B1 = (bedOther.bed_pbvxVertex0->bvx_vRelative-bedOther.bed_pbvxVertex1->bvx_vRelative).Length(); FLOAT fA0B0 = (bed_pbvxVertex0->bvx_vRelative-bedOther.bed_pbvxVertex0->bvx_vRelative).Length(); FLOAT fA0B1 = (bed_pbvxVertex0->bvx_vRelative-bedOther.bed_pbvxVertex1->bvx_vRelative).Length(); FLOAT fA1B0 = (bed_pbvxVertex1->bvx_vRelative-bedOther.bed_pbvxVertex0->bvx_vRelative).Length(); FLOAT fA1B1 = (bed_pbvxVertex1->bvx_vRelative-bedOther.bed_pbvxVertex1->bvx_vRelative).Length(); FLOAT fB0A0 = (bedOther.bed_pbvxVertex0->bvx_vRelative-bed_pbvxVertex0->bvx_vRelative).Length(); FLOAT fB0A1 = (bedOther.bed_pbvxVertex0->bvx_vRelative-bed_pbvxVertex1->bvx_vRelative).Length(); FLOAT fB1A0 = (bedOther.bed_pbvxVertex1->bvx_vRelative-bed_pbvxVertex0->bvx_vRelative).Length(); FLOAT fB1A1 = (bedOther.bed_pbvxVertex1->bvx_vRelative-bed_pbvxVertex1->bvx_vRelative).Length(); return Abs(fB0B1-fA0B0-fA0B1)bvx_vRelative==bedOther.bed_pbvxVertex0->bvx_vRelative || bed_pbvxVertex0->bvx_vRelative==bedOther.bed_pbvxVertex1->bvx_vRelative || bed_pbvxVertex1->bvx_vRelative==bedOther.bed_pbvxVertex0->bvx_vRelative || bed_pbvxVertex1->bvx_vRelative==bedOther.bed_pbvxVertex1->bvx_vRelative) { return TRUE; // if they have no common vertices } else if( !wed_bIgnoreTJunctions) { // test if some vertex is on the other edge FLOAT fA0A1 = (bed_pbvxVertex0->bvx_vRelative-bed_pbvxVertex1->bvx_vRelative).Length(); FLOAT fB0B1 = (bedOther.bed_pbvxVertex0->bvx_vRelative-bedOther.bed_pbvxVertex1->bvx_vRelative).Length(); FLOAT fA0B0 = (bed_pbvxVertex0->bvx_vRelative-bedOther.bed_pbvxVertex0->bvx_vRelative).Length(); FLOAT fA0B1 = (bed_pbvxVertex0->bvx_vRelative-bedOther.bed_pbvxVertex1->bvx_vRelative).Length(); FLOAT fA1B0 = (bed_pbvxVertex1->bvx_vRelative-bedOther.bed_pbvxVertex0->bvx_vRelative).Length(); FLOAT fA1B1 = (bed_pbvxVertex1->bvx_vRelative-bedOther.bed_pbvxVertex1->bvx_vRelative).Length(); FLOAT fB0A0 = (bedOther.bed_pbvxVertex0->bvx_vRelative-bed_pbvxVertex0->bvx_vRelative).Length(); FLOAT fB0A1 = (bedOther.bed_pbvxVertex0->bvx_vRelative-bed_pbvxVertex1->bvx_vRelative).Length(); FLOAT fB1A0 = (bedOther.bed_pbvxVertex1->bvx_vRelative-bed_pbvxVertex0->bvx_vRelative).Length(); FLOAT fB1A1 = (bedOther.bed_pbvxVertex1->bvx_vRelative-bed_pbvxVertex1->bvx_vRelative).Length(); return Abs(fB0B1-fA0B0-fA0B1)bpe_pbedEdge->bed_pbvxVertex0->bvx_vAbsolute; const FLOAT3D &vB = itbpePolygonEdge->bpe_pbedEdge->bed_pbvxVertex1->bvx_vAbsolute; // compute general vectors needed FLOAT3D vAC = vC-vA; FLOAT3D vAB = vB-vA; // get parameter of the P - orthogonal projection of C onto AB FLOAT fR = (vAC%vAB)/(vAB%vAB); FLOAT fD2 = 0.0f; // if before A if (fR<0) { // get squared distance AC fD2 = vAC%vAC; // if after B } else if (fR>1) { // get squared distance BC FLOAT3D vBC = vC-vB; fD2 = vBC%vBC; // if between } else { // find PC FLOAT3D vBC = vC-vB; FLOAT3D vPC = vAC+(vBC-vAC)*fR; // get squared distance PC fD2 = vPC%vPC; } // update minimal squared distance if (fD2