Serious-Engine/Sources/Engine/Brushes/BrushSector.cpp

1381 lines
49 KiB
C++
Raw Normal View History

2016-03-11 14:57:17 +01:00
/* Copyright (c) 2002-2012 Croteam Ltd. All rights reserved. */
#include "stdh.h"
#include <Engine/Brushes/Brush.h>
#include <Engine/Brushes/BrushTransformed.h>
#include <Engine/Math/Geometry.inl>
#include <Engine/Base/Console.h>
#include <Engine/World/World.h>
#include <Engine/World/WorldEditingProfile.h>
#include <Engine/Math/Projection_DOUBLE.h>
#include <Engine/Templates/DynamicArray.cpp>
#include <Engine/Templates/StaticArray.cpp>
#include <Engine/Templates/DynamicContainer.cpp>
#include <Engine/Math/Float.h>
#include <Engine/Math/OBBox.h>
#include <Engine/Entities/Entity.h>
#include <Engine/Templates/BSP.h>
#include <Engine/Templates/BSP_internal.h>
//template CDynamicArray<CBrushVertex>;
CBrushSector::CBrushSector(const CBrushSector &c)
: bsc_bspBSPTree(*new DOUBLEbsptree3D)
{
ASSERT(FALSE);
};
CBrushSector &CBrushSector::operator=(const CBrushSector &c)
{
ASSERT(FALSE);
return *this;
};
extern void AssureFPT_53(void);
/* Default constructor. */
CBrushSector::CBrushSector(void)
: bsc_ulFlags(0)
, bsc_ulFlags2(0)
, bsc_ulTempFlags(0)
, bsc_ulVisFlags(0)
, bsc_strName("")
, bsc_bspBSPTree(*new DOUBLEbsptree3D)
{
};
CBrushSector::~CBrushSector(void)
{
delete &bsc_bspBSPTree;
}
/*
* Calculate bounding boxs of polygons in this sector.
*/
void CBrushSector::CalculateBoundingBoxes(CSimpleProjection3D_DOUBLE &prRelativeToAbsolute)
{
// assure that floating point precision is 53 bits
AssureFPT_53();
// discard portal-sector links to this sector
extern BOOL _bDontDiscardLinks;
if (!(bsc_ulTempFlags&BSCTF_PRELOADEDLINKS) && !_bDontDiscardLinks) {
bsc_rdOtherSidePortals.Clear();
}
// create an array of precise vertices in absolute space
CStaticArray<DOUBLE3D> avdAbsoluteVertices;
avdAbsoluteVertices.New(bsc_abvxVertices.Count());
bsc_boxRelative = FLOATaabbox3D();
// for each vertex in sector
for(INDEX ivx=0; ivx<bsc_abvxVertices.Count(); ivx++) {
// make the original vertex point to absolute vertex
bsc_abvxVertices[ivx].bvx_pvdPreciseAbsolute = &avdAbsoluteVertices[ivx];
bsc_abvxVertices[ivx].bvx_pwvxWorking = &bsc_awvxVertices[ivx];
// transform original there
prRelativeToAbsolute.ProjectCoordinate(bsc_abvxVertices[ivx].bvx_vdPreciseRelative, avdAbsoluteVertices[ivx]);
// remember the absolute and relative coordinates in lower precision
bsc_abvxVertices[ivx].bvx_vAbsolute = DOUBLEtoFLOAT(avdAbsoluteVertices[ivx]);
bsc_awvxVertices[ivx].wvx_vRelative =
bsc_abvxVertices[ivx].bvx_vRelative = DOUBLEtoFLOAT(bsc_abvxVertices[ivx].bvx_vdPreciseRelative);
// add vertex to relative box
bsc_boxRelative |= bsc_abvxVertices[ivx].bvx_vRelative;
}
// create an array of precise planes in absolute space
CStaticArray<DOUBLEplane3D> apldAbsolutePlanes;
apldAbsolutePlanes.New(bsc_abplPlanes.Count());
// for each plane in sector
for(INDEX ipl=0; ipl<bsc_abplPlanes.Count(); ipl++){
// make the original plane point to absolute plane
bsc_abplPlanes[ipl].bpl_ppldPreciseAbsolute = &apldAbsolutePlanes[ipl];
bsc_abplPlanes[ipl].bpl_pwplWorking = &bsc_awplPlanes[ipl];
// transform original there
prRelativeToAbsolute.Project(bsc_abplPlanes[ipl].bpl_pldPreciseRelative, apldAbsolutePlanes[ipl]);
// remember the absolute and relative coordinates in lower precision
bsc_abplPlanes[ipl].bpl_plAbsolute = DOUBLEtoFLOAT(apldAbsolutePlanes[ipl]);
bsc_awplPlanes[ipl].wpl_plRelative =
bsc_abplPlanes[ipl].bpl_plRelative = DOUBLEtoFLOAT(bsc_abplPlanes[ipl].bpl_pldPreciseRelative);
// make default mapping coordinates for the plane
bsc_awplPlanes[ipl].wpl_mvRelative.FromPlane(bsc_awplPlanes[ipl].wpl_plRelative);
// remember major axes of the plane in apsolute space
GetMajorAxesForPlane(bsc_abplPlanes[ipl].bpl_plAbsolute,
bsc_abplPlanes[ipl].bpl_iPlaneMajorAxis1,
bsc_abplPlanes[ipl].bpl_iPlaneMajorAxis2);
}
// clear the bounding box of the sector
bsc_boxBoundingBox = FLOATaabbox3D();
// for all polygons in this sector
{FOREACHINSTATICARRAY(bsc_abpoPolygons, CBrushPolygon, itbpo) {
// calculate bounding box for that polygon
itbpo->CalculateBoundingBox();
// add the polygon's bounding box to sector's bounding box
bsc_boxBoundingBox |= itbpo->bpo_boxBoundingBox;
}}
// if the bsp tree is not preloaded
if (!(bsc_ulTempFlags&BSCTF_PRELOADEDBSP)) {
// clear BSP tree of the sector
bsc_bspBSPTree.Destroy();
// if the brush is zoning or field
CEntity *pen = bsc_pbmBrushMip->bm_pbrBrush->br_penEntity;
if (pen!=NULL &&
((pen->en_ulFlags&ENF_ZONING) || pen->en_RenderType==CEntity::RT_FIELDBRUSH) ) {
// create an array of bsp polygons for sector polygons
INDEX ctPolygons = bsc_abpoPolygons.Count();
CDynamicArray< BSPPolygon<DOUBLE, 3> > arbpoPolygons;
arbpoPolygons.New(ctPolygons);
// for all polygons in this sector
arbpoPolygons.Lock();
{for(INDEX iPolygon=0; iPolygon<ctPolygons; iPolygon++){
// create a BSP polygon from the brush polygon
CBrushPolygon &brpo = bsc_abpoPolygons[iPolygon];
BSPPolygon<DOUBLE, 3> &bspo = arbpoPolygons[iPolygon];
brpo.CreateBSPPolygon(bspo);
}}
arbpoPolygons.Unlock();
// create the bsp tree from the bsp polygons
bsc_bspBSPTree.Create(arbpoPolygons);
}
}
// clear preloading flags
bsc_ulTempFlags&=~BSCTF_PRELOADEDBSP;
bsc_ulTempFlags&=~BSCTF_PRELOADEDLINKS;
// if in debug version
#ifndef NDEBUG
// for each vertex in sector
{for(INDEX ivx=0; ivx<bsc_abvxVertices.Count(); ivx++){
// discard absolute vertex pointer
bsc_abvxVertices[ivx].bvx_pvdPreciseAbsolute = NULL;
}}
// for each plane in sector
{for(INDEX ipl=0; ipl<bsc_abplPlanes.Count(); ipl++){
// discard absolute plane pointer
bsc_abplPlanes[ipl].bpl_ppldPreciseAbsolute = NULL;
}}
#endif
// !!!! remove this after loading all old levels
// but should save size of polygon in mex (not shadow map)
// NOTE: but this is also called in FromObject3D (?!)
// for each polygon
{for(INDEX iPolygon=0; iPolygon<bsc_abpoPolygons.Count(); iPolygon++) {
CBrushPolygon &bpo = bsc_abpoPolygons[iPolygon]; // brush polygon alias
// if the shadow map of the polygon is not initialized
if (bpo.bpo_smShadowMap.sm_mexWidth==0 || bpo.bpo_smShadowMap.sm_pixPolygonSizeU<0) {
// initialize the shadow map of the polygon
bpo.InitializeShadowMap();
}
}}
}
/* Uncache lightmaps on all shadows on the sector. */
void CBrushSector::UncacheLightMaps(void)
{
// for all polygons in this sector
{FOREACHINSTATICARRAY(bsc_abpoPolygons, CBrushPolygon, itbpo) {
// uncache shadow map on the polygon
itbpo->bpo_smShadowMap.Uncache();
}}
}
/* Find and remember all entities in this sector. */
void CBrushSector::FindEntitiesInSector(void)
{
// assure that floating point precision is 53 bits
CSetFPUPrecision sfp(FPT_53BIT);
// get the entity of this sector's brush
CEntity *penEntity = bsc_pbmBrushMip->bm_pbrBrush->br_penEntity;
// if the brush entity is not zoning
if (penEntity==NULL || !(penEntity->en_ulFlags&ENF_ZONING)) {
// do nothing
return;
}
// unset spatial clasification
bsc_rsEntities.Clear();
// remember sectors obbox
FLOATobbox3D boxSector(bsc_boxBoundingBox);
// for each entity in the world
{FOREACHINDYNAMICCONTAINER(penEntity->en_pwoWorld->wo_cenEntities, CEntity, iten) {
// if not in spatial clasification
if (iten->en_fSpatialClassificationRadius<0) {
// skip it
continue;
}
// get bounding sphere
FLOAT fSphereRadius = iten->en_fSpatialClassificationRadius;
const FLOAT3D &vSphereCenter = iten->en_plPlacement.pl_PositionVector;
// if the sector's bounding box has contact with the sphere
if(bsc_boxBoundingBox.TouchesSphere(vSphereCenter, fSphereRadius)) {
// if the sphere is inside the sector
if (bsc_bspBSPTree.TestSphere(
FLOATtoDOUBLE(vSphereCenter), FLOATtoDOUBLE(fSphereRadius))>=0) {
// make oriented bounding box of the entity
FLOATobbox3D boxEntity(iten->en_boxSpatialClassification,
iten->en_plPlacement.pl_PositionVector, iten->en_mRotation);
// if the box is inside the sector
if (boxSector.HasContactWith(boxEntity) &&
bsc_bspBSPTree.TestBox(FLOATtoDOUBLE(boxEntity))>=0) {
// relate the entity to the sector
if (iten->en_RenderType==CEntity::RT_BRUSH
||iten->en_RenderType==CEntity::RT_FIELDBRUSH
||iten->en_RenderType==CEntity::RT_TERRAIN) { // brushes first
AddRelationPairHeadHead(bsc_rsEntities, iten->en_rdSectors);
} else {
AddRelationPairTailTail(bsc_rsEntities, iten->en_rdSectors);
}
}
}
}
}}
}
/*
* Clear the object.
*/
void CBrushSector::Clear(void)
{
bsc_abvxVertices.Clear();
bsc_awvxVertices.Clear();
bsc_abedEdges.Clear();
bsc_awedEdges.Clear();
bsc_abplPlanes.Clear();
bsc_awplPlanes.Clear();
bsc_abpoPolygons.Clear();
bsc_rdOtherSidePortals.Clear();
bsc_rsEntities.Clear();
bsc_strName.Clear();
// bsc_bspBSPTree.Destroy();
}
/*
* Lock all arrays.
*/
void CBrushSector::LockAll(void)
{
/* this function does nothing, because in current implementation all
arrays in brush sector are static arrays
*/
}
/*
* Unlock all arrays.
*/
void CBrushSector::UnlockAll(void)
{
/* this function does nothing, because in current implementation all
arrays in brush sector are static arrays
*/
}
/*
* Calculate volume of the sector (all polygons must be triangularized).
*/
DOUBLE CBrushSector::CalculateVolume(void)
{
// assure that floating point precision is 53 bits
AssureFPT_53();
DOUBLE fSectorVolume = 0.0;
// for each polygon
{FOREACHINSTATICARRAY(bsc_abpoPolygons, CBrushPolygon, itbpo) {
// calculate the area of the polygon
DOUBLE fPolygonArea = itbpo->CalculateArea();
// add the volume of the pyramid that the polygon closes with origin
fSectorVolume += fPolygonArea * itbpo->bpo_pbplPlane->bpl_pldPreciseRelative.Distance() / 3.0;
}}
// if the volume is positive
if (fSectorVolume>=0.0) {
// remember that the sector is open
bsc_ulFlags |= BSCF_OPENSECTOR;
// if the sector belongs to a field brush
CBrush3D *pbr = bsc_pbmBrushMip->bm_pbrBrush;
ASSERT(pbr!=NULL);
if (pbr->br_pfsFieldSettings!=NULL) {
// report a warning
CPrintF("Warning: Open sector in a field brush!\n");
}
// if the volume is negative
} else {
// remember that the sector is closed
bsc_ulFlags &= ~BSCF_OPENSECTOR;
}
Triangulate();
return fSectorVolume;
}
void CBrushSector::Triangulate(void)
{
_pfWorldEditingProfile.StartTimer(CWorldEditingProfile::PTI_TRIANGULATE);
// for each polygon
{FOREACHINSTATICARRAY(bsc_abpoPolygons, CBrushPolygon, itbpo) {
itbpo->Triangulate();
}}
_pfWorldEditingProfile.StopTimer(CWorldEditingProfile::PTI_TRIANGULATE);
}
// update changed sector's data after dragging vertices or importing
void CBrushSector::UpdateSector(void)
{
// calculate the bounding box of the brush sector
CSimpleProjection3D_DOUBLE prBrushToAbsolute;
bsc_pbmBrushMip->bm_pbrBrush->PrepareRelativeToAbsoluteProjection(prBrushToAbsolute);
CalculateBoundingBoxes(prBrushToAbsolute);
// calculate the volume of the sector
CalculateVolume();
// for each polygon
INDEX ctPolygons = bsc_abpoPolygons.Count();
{for(INDEX iPolygon=0; iPolygon<ctPolygons; iPolygon++) {
CBrushPolygon &bpo = bsc_abpoPolygons[iPolygon]; // brush polygon alias
// initialize the shadow map of the polygon
bpo.InitializeShadowMap();
}}
// find and remember all entities in this sector
FindEntitiesInSector();
}
// recalculate planes for polygons from their vertices
// NOTE: Planes are not optimal here. You have to reoptimize the brush
// after done with dragging
//
void CBrushSector::MakePlanesFromVertices(void)
{
// clear old planes
bsc_abplPlanes.Clear();
bsc_awplPlanes.Clear();
// get the number of planes to create
INDEX ctPlanes = bsc_abpoPolygons.Count();
// create that much planes
bsc_abplPlanes.New(ctPlanes);
bsc_awplPlanes.New(ctPlanes);
// for all polygons
INDEX ctPolygons = bsc_abpoPolygons.Count();
for(INDEX iPolygon=0; iPolygon<ctPolygons; iPolygon++) {
CBrushPolygon &bpo = bsc_abpoPolygons[iPolygon]; // brush polygon alias
CBrushPlane &bpl = bsc_abplPlanes[iPolygon];
CWorkingPlane &wpl = bsc_awplPlanes[iPolygon];
// link to plane
bpo.bpo_pbplPlane = &bpl;
bpl.bpl_pwplWorking = &wpl;
// clear plane normal
DOUBLE3D vNormal = DOUBLE3D(0.0f,0.0f,0.0f);
DOUBLE3D vAnyVertex;
// for all edges in polygon
INDEX ctVertices = bpo.bpo_abpePolygonEdges.Count();
{for(INDEX iVertex=0; iVertex<ctVertices; iVertex++) {
// get its vertices in counterclockwise order
CBrushPolygonEdge &bpe = bpo.bpo_abpePolygonEdges[iVertex];
DOUBLE3D v0, v1;
bpe.GetVertexCoordinatesPreciseRelative(v0, v1);
DOUBLE3D vSum = v0+v1;
DOUBLE3D vDif = v0-v1;
// 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);
vAnyVertex = v0;
}}
// if the polygon area is too small
if (vNormal.Length()<1E-8) {
// make dummy normal
vNormal = DOUBLE3D(0,1,0);
}
// construct this plane from normal vector and one point
bpl.bpl_pldPreciseRelative = DOUBLEplane3D(vNormal, vAnyVertex);
wpl.wpl_plRelative = DOUBLEtoFLOAT(bpl.bpl_pldPreciseRelative);
// make default mapping coordinates for the plane
wpl.wpl_mvRelative.FromPlane(wpl.wpl_plRelative);
}
}
// Update sector after moving vertices
void CBrushSector::UpdateVertexChanges(void)
{
// recalculate planes for polygons from their vertices
MakePlanesFromVertices();
// update changed sector's data after dragging vertices or importing
bsc_ulTempFlags|=BSCTF_PRELOADEDBSP;
UpdateSector();
}
void CBrushSector::TriangularizePolygon( CBrushPolygon *pbpo)
{
// clear marked for use flag on all polygons in world
CWorld *pwo=bsc_pbmBrushMip->bm_pbrBrush->br_penEntity->en_pwoWorld;
pwo->ClearMarkedForUseFlag();
pbpo->bpo_ulFlags |= BPOF_MARKED_FOR_USE;
TriangularizeMarkedPolygons();
UpdateVertexChanges();
}
// Triangularize polygons that continin vertices from selection
void CBrushSector::TriangularizeForVertices( CBrushVertexSelection &selVertex)
{
// clear marked for use flag on all polygons in world
CWorld *pwo=bsc_pbmBrushMip->bm_pbrBrush->br_penEntity->en_pwoWorld;
pwo->ClearMarkedForUseFlag();
// ---------- Mark polygons in this sector that contain any of the selected vertices
// for all polygons in this sector
{FOREACHINSTATICARRAY(bsc_abpoPolygons, CBrushPolygon, itbpo)
{
// if polygon is already marked for triangularization
if( itbpo->bpo_ulFlags & BPOF_MARKED_FOR_USE)
{
// no need to test it again
continue;
}
// if this polygon is triangle
if( itbpo->bpo_aiTriangleElements.Count() == 3)
{
// skip it
continue;
}
// for all vertices in this polygon
{FOREACHINSTATICARRAY(itbpo->bpo_apbvxTriangleVertices, CBrushVertex *, itpbvx)
{
// if any of polygon's vertices is selected
if( (*itpbvx)->bvx_ulFlags&BVXF_SELECTED)
{
// mark for triangularized
itbpo->bpo_ulFlags |= BPOF_MARKED_FOR_USE;
// no need to test other vertices in this polygon
break;
}
}}
}}
// triangularize marked polygons
TriangularizeMarkedPolygons();
}
void CBrushSector::TriangularizeMarkedPolygons( void)
{
// for marked polygons: count how many there are and how many new triangles will be created
INDEX ctPolygonsToRemove = 0;
INDEX ctNewTriangles = 0;
// for all polygons in this sector
{FOREACHINSTATICARRAY(bsc_abpoPolygons, CBrushPolygon, itbpo)
{
// if this polygon is triangle
if( itbpo->bpo_aiTriangleElements.Count() == 3)
{
// skip it
continue;
}
// if polygon is already marked for triangularization
if( itbpo->bpo_ulFlags & BPOF_MARKED_FOR_USE)
{
// count polygon
ctPolygonsToRemove++;
// and its triangles
ctNewTriangles+=itbpo->bpo_aiTriangleElements.Count()/3;
}
}}
// if all marked polygons are already triangularized
if( ctPolygonsToRemove == 0)
{
// don't do anything
return;
}
// create new edge and polygon arrays
CStaticArray<CBrushEdge> abedEdgesNew;
CStaticArray<CBrushPolygon> abpoPolygonsNew;
INDEX ctOldEdges = bsc_abedEdges.Count();
INDEX ctOldPolygons = bsc_abpoPolygons.Count();
abedEdgesNew.New( ctOldEdges+ctNewTriangles*3);
abpoPolygonsNew.New( ctOldPolygons-ctPolygonsToRemove+ctNewTriangles);
// copy old edges to new edge array
INDEX iEdge;
for( iEdge=0; iEdge<ctOldEdges; iEdge++)
{
abedEdgesNew[iEdge] = bsc_abedEdges[iEdge];
}
// note that iEdge points to first new edge
// ----------- Copy old polygons, create new ones along with their edges
// for all polygons in this sector
INDEX iNewPolygons=0;
{FOREACHINSTATICARRAY(bsc_abpoPolygons, CBrushPolygon, itbpo)
{
CBrushPolygon &bpoOld = *itbpo;
// polygon shouldn't be triangularized
if( !(itbpo->bpo_ulFlags & BPOF_MARKED_FOR_USE))
{
CBrushPolygon &bpoNew = abpoPolygonsNew[ iNewPolygons];
// copy the old polygon
bpoNew.bpo_pbplPlane = itbpo->bpo_pbplPlane;
bpoNew.bpo_abptTextures[0].CopyTextureProperties( itbpo->bpo_abptTextures[0], TRUE);
bpoNew.bpo_abptTextures[1].CopyTextureProperties( itbpo->bpo_abptTextures[1], TRUE);
bpoNew.bpo_abptTextures[2].CopyTextureProperties( itbpo->bpo_abptTextures[2], TRUE);
bpoNew.bpo_colColor = itbpo->bpo_colColor;
bpoNew.bpo_ulFlags = itbpo->bpo_ulFlags & ~(BPOF_MARKED_FOR_USE|BPOF_SELECTED);
bpoNew.bpo_colShadow = itbpo->bpo_colShadow;
bpoNew.bpo_bppProperties = itbpo->bpo_bppProperties;
bpoNew.bpo_pbscSector = itbpo->bpo_pbscSector;
// remap brush polygon edges to point to edges of new array
INDEX ctEdgesToRemap = itbpo->bpo_abpePolygonEdges.Count();
// allocate new polygon edges
bpoNew.bpo_abpePolygonEdges.New(ctEdgesToRemap);
// for each edge in polygon
for( INDEX iRemapEdge=0; iRemapEdge<ctEdgesToRemap; iRemapEdge++)
{
CBrushPolygonEdge &bpeOld = itbpo->bpo_abpePolygonEdges[iRemapEdge];
CBrushPolygonEdge &bpeNew = bpoNew.bpo_abpePolygonEdges[iRemapEdge];
// get index of the edge for old edge array
INDEX iOldIndex = bsc_abedEdges.Index( bpeOld.bpe_pbedEdge);
// use same index, but point to edge in new edge array
bpeNew.bpe_pbedEdge = &abedEdgesNew[iOldIndex];
// set edge direction
bpeNew.bpe_bReverse = bpeOld.bpe_bReverse;
}
bpoNew.bpo_apbvxTriangleVertices = bpoOld.bpo_apbvxTriangleVertices;
bpoNew.bpo_aiTriangleElements = bpoOld.bpo_aiTriangleElements;
// initialize shadow map
bpoNew.InitializeShadowMap();
iNewPolygons++;
}
// if polygon is marked for triangularization
else
{
INDEX ctTriangles = itbpo->bpo_aiTriangleElements.Count()/3;
// for each triangle in old polygon
for( INDEX iTriangle=0; iTriangle<ctTriangles; iTriangle++)
{
CBrushPolygon &bpoNew = abpoPolygonsNew[ iNewPolygons];
INDEX iVtx0 = itbpo->bpo_aiTriangleElements[iTriangle*3+0];
INDEX iVtx1 = itbpo->bpo_aiTriangleElements[iTriangle*3+1];
INDEX iVtx2 = itbpo->bpo_aiTriangleElements[iTriangle*3+2];
CBrushVertex *pbvtx0 = itbpo->bpo_apbvxTriangleVertices[ iVtx0];
CBrushVertex *pbvtx1 = itbpo->bpo_apbvxTriangleVertices[ iVtx1];
CBrushVertex *pbvtx2 = itbpo->bpo_apbvxTriangleVertices[ iVtx2];
// setup edge 0
abedEdgesNew[iEdge+0].bed_pbvxVertex0 = pbvtx0;
abedEdgesNew[iEdge+0].bed_pbvxVertex1 = pbvtx1;
// setup edge 1
abedEdgesNew[iEdge+1].bed_pbvxVertex0 = pbvtx1;
abedEdgesNew[iEdge+1].bed_pbvxVertex1 = pbvtx2;
// setup edge 2
abedEdgesNew[iEdge+2].bed_pbvxVertex0 = pbvtx2;
abedEdgesNew[iEdge+2].bed_pbvxVertex1 = pbvtx0;
// allocate and set polygon edges for new triangle
bpoNew.bpo_abpePolygonEdges.New(3);
bpoNew.bpo_abpePolygonEdges[0].bpe_pbedEdge = &abedEdgesNew[iEdge+0];
bpoNew.bpo_abpePolygonEdges[0].bpe_bReverse = FALSE;
bpoNew.bpo_abpePolygonEdges[1].bpe_pbedEdge = &abedEdgesNew[iEdge+1];
bpoNew.bpo_abpePolygonEdges[1].bpe_bReverse = FALSE;
bpoNew.bpo_abpePolygonEdges[2].bpe_pbedEdge = &abedEdgesNew[iEdge+2];
bpoNew.bpo_abpePolygonEdges[2].bpe_bReverse = FALSE;
CBrushEdge &edg0 = *bpoNew.bpo_abpePolygonEdges[0].bpe_pbedEdge;
CBrushEdge &edg1 = *bpoNew.bpo_abpePolygonEdges[1].bpe_pbedEdge;
CBrushEdge &edg2 = *bpoNew.bpo_abpePolygonEdges[2].bpe_pbedEdge;
// set brush vertex ptrs
bpoNew.bpo_apbvxTriangleVertices.New(3);
bpoNew.bpo_apbvxTriangleVertices[0] = pbvtx0;
bpoNew.bpo_apbvxTriangleVertices[1] = pbvtx1;
bpoNew.bpo_apbvxTriangleVertices[2] = pbvtx2;
// setup fixed trinagle element indices
bpoNew.bpo_aiTriangleElements.New(3);
bpoNew.bpo_aiTriangleElements[0] = 0;
bpoNew.bpo_aiTriangleElements[1] = 1;
bpoNew.bpo_aiTriangleElements[2] = 2;
// copy parameters from old polygon
bpoNew.bpo_pbplPlane = itbpo->bpo_pbplPlane;
bpoNew.bpo_abptTextures[0].CopyTextureProperties( itbpo->bpo_abptTextures[0], TRUE);
bpoNew.bpo_abptTextures[1].CopyTextureProperties( itbpo->bpo_abptTextures[1], TRUE);
bpoNew.bpo_abptTextures[2].CopyTextureProperties( itbpo->bpo_abptTextures[2], TRUE);
bpoNew.bpo_colColor = itbpo->bpo_colColor;
bpoNew.bpo_ulFlags = itbpo->bpo_ulFlags & ~(BPOF_MARKED_FOR_USE|BPOF_SELECTED);
bpoNew.bpo_colShadow = itbpo->bpo_colShadow;
bpoNew.bpo_bppProperties = itbpo->bpo_bppProperties;
bpoNew.bpo_pbscSector = itbpo->bpo_pbscSector;
// initialize shadow map
bpoNew.InitializeShadowMap();
// skip created edges
iEdge+=3;
// next triangle
iNewPolygons++;
}
}
}}
// copy new arrays over old ones
bsc_abedEdges.MoveArray( abedEdgesNew);
bsc_abpoPolygons.MoveArray( abpoPolygonsNew);
// create array of working edges
INDEX cted = bsc_abedEdges.Count();
bsc_awedEdges.Clear();
bsc_awedEdges.New(cted);
// for each edge
for (INDEX ied=0; ied<cted; ied++) {
CWorkingEdge &wed = bsc_awedEdges[ied];
CBrushEdge &bed = bsc_abedEdges[ied];
// setup its working edge
bed.bed_pwedWorking = &wed;
wed.wed_iwvx0 = bsc_abvxVertices.Index(bed.bed_pbvxVertex0);
wed.wed_iwvx1 = bsc_abvxVertices.Index(bed.bed_pbvxVertex1);
}
// recalculate planes for polygons from their vertices
MakePlanesFromVertices();
}
void CBrushSector::InsertVertexIntoTriangle( CBrushPolygonSelection &selPolygon, FLOAT3D vVertex)
{
if( selPolygon.Count() != 1)
{
return;
}
SubdivideTriangles(selPolygon);
// get last vertex
INDEX ctVertices = bsc_abvxVertices.Count();
CBrushVertex *pbvtxLast = &bsc_abvxVertices[ctVertices-1];
pbvtxLast->SetAbsolutePosition( FLOATtoDOUBLE(vVertex));
}
void CBrushSector::SubdivideTriangles( CBrushPolygonSelection &selPolygon)
{
INDEX ctPolygonsToRemove = selPolygon.Count();
INDEX ctNewTriangles = ctPolygonsToRemove*3;
// clear marked for use flag on all polygons in world
CWorld *pwo=bsc_pbmBrushMip->bm_pbrBrush->br_penEntity->en_pwoWorld;
pwo->ClearMarkedForUseFlag();
// for all polygons in selection
{FOREACHINDYNAMICCONTAINER(selPolygon, CBrushPolygon, itbpo)
{
// mark them for use
itbpo->bpo_ulFlags |= BPOF_MARKED_FOR_USE;
if( itbpo->bpo_aiTriangleElements.Count() != 3)
{
return;
}
}}
// clear the selection
selPolygon.Clear();
// create new arrays
CStaticArray<CWorkingVertex> awvxVerticesNew;
CStaticArray<CBrushVertex> abvxVerticesNew;
CStaticArray<CBrushEdge> abedEdgesNew;
CStaticArray<CBrushPolygon> abpoPolygonsNew;
INDEX ctOldVertices = bsc_abvxVertices.Count();
INDEX ctOldEdges = bsc_abedEdges.Count();
INDEX ctOldPolygons = bsc_abpoPolygons.Count();
// allocate arrays
abvxVerticesNew.New( ctOldVertices+ctPolygonsToRemove);
awvxVerticesNew.New( ctOldVertices+ctPolygonsToRemove);
abedEdgesNew.New( ctOldEdges+ctPolygonsToRemove*6);
abpoPolygonsNew.New( ctOldPolygons-ctPolygonsToRemove+ctNewTriangles);
// copy old vertices to new vertex array
INDEX iVtx;
for( iVtx=0; iVtx<ctOldVertices; iVtx++)
{
abvxVerticesNew[iVtx] = bsc_abvxVertices[iVtx];
awvxVerticesNew[iVtx] = bsc_awvxVertices[iVtx];
}
// note that iVtx points to first new vertex
// initialize working vertex ptrs
for( INDEX iVtxTemp=0; iVtxTemp<abvxVerticesNew.Count(); iVtxTemp++)
{
abvxVerticesNew[iVtxTemp].bvx_pwvxWorking = &awvxVerticesNew[iVtxTemp];
}
// copy old edges to new edge array
INDEX iEdge;
for( iEdge=0; iEdge<ctOldEdges; iEdge++)
{
// remap old vertices into new vertex array using their indices
INDEX iOldVtx0 = bsc_abvxVertices.Index( bsc_abedEdges[iEdge].bed_pbvxVertex0);
INDEX iOldVtx1 = bsc_abvxVertices.Index( bsc_abedEdges[iEdge].bed_pbvxVertex1);
abedEdgesNew[iEdge].bed_pbvxVertex0 = &abvxVerticesNew[iOldVtx0];
abedEdgesNew[iEdge].bed_pbvxVertex1 = &abvxVerticesNew[iOldVtx1];
}
// note that iEdge points to first new edge
// ----------- Copy old polygons, create new ones along with their edges and vertices
// for all polygons in this sector
INDEX iNewPolygons=0;
{FOREACHINSTATICARRAY(bsc_abpoPolygons, CBrushPolygon, itbpo)
{
CBrushPolygon &bpoOld = *itbpo;
// polygon shouldn't be subdivided
if( !(itbpo->bpo_ulFlags & BPOF_MARKED_FOR_USE))
{
// copy the old polygon
CBrushPolygon &bpoNew = abpoPolygonsNew[ iNewPolygons];
bpoNew.bpo_pbplPlane = bpoOld.bpo_pbplPlane;
bpoNew.bpo_abptTextures[0].CopyTextureProperties( bpoOld.bpo_abptTextures[0], TRUE);
bpoNew.bpo_abptTextures[1].CopyTextureProperties( bpoOld.bpo_abptTextures[1], TRUE);
bpoNew.bpo_abptTextures[2].CopyTextureProperties( bpoOld.bpo_abptTextures[2], TRUE);
bpoNew.bpo_colColor = bpoOld.bpo_colColor;
bpoNew.bpo_ulFlags = bpoOld.bpo_ulFlags & ~(BPOF_MARKED_FOR_USE|BPOF_SELECTED);
bpoNew.bpo_colShadow = bpoOld.bpo_colShadow;
bpoNew.bpo_bppProperties = bpoOld.bpo_bppProperties;
bpoNew.bpo_pbscSector = bpoOld.bpo_pbscSector;
// remap brush polygon edges to point to edges of new array
INDEX ctEdgesToRemap = bpoOld.bpo_abpePolygonEdges.Count();
// allocate new polygon edges
bpoNew.bpo_abpePolygonEdges.New(ctEdgesToRemap);
// for each edge in polygon
for( INDEX iRemapEdge=0; iRemapEdge<ctEdgesToRemap; iRemapEdge++)
{
CBrushPolygonEdge &bpeOld = bpoOld.bpo_abpePolygonEdges[iRemapEdge];
CBrushPolygonEdge &bpeNew = bpoNew.bpo_abpePolygonEdges[iRemapEdge];
// get index of the edge for old edge array
INDEX iOldIndex = bsc_abedEdges.Index( bpeOld.bpe_pbedEdge);
// use same index, but point to edge in new edge array
bpeNew.bpe_pbedEdge = &abedEdgesNew[iOldIndex];
// set edge direction
bpeNew.bpe_bReverse = bpeOld.bpe_bReverse;
}
// allocate and set vertex pointers
bpoNew.bpo_apbvxTriangleVertices.New(3);
// remap brush vertex pointers
for( INDEX iTVtx=0; iTVtx<3; iTVtx++)
{
INDEX iOldVtx = bsc_abvxVertices.Index( bpoOld.bpo_apbvxTriangleVertices[iTVtx]);
bpoNew.bpo_apbvxTriangleVertices[iTVtx] = &abvxVerticesNew[iOldVtx];
}
// copy triangle and triangle elements arrays
bpoNew.bpo_aiTriangleElements = bpoOld.bpo_aiTriangleElements;
// initialize shadow map
bpoNew.InitializeShadowMap();
iNewPolygons++;
}
// if polygon is marked for subdivision
else
{
INDEX iVtx0 = bpoOld.bpo_aiTriangleElements[0];
INDEX iVtx1 = bpoOld.bpo_aiTriangleElements[1];
INDEX iVtx2 = bpoOld.bpo_aiTriangleElements[2];
INDEX iOldVtx0 = bsc_abvxVertices.Index( bpoOld.bpo_apbvxTriangleVertices[ iVtx0]);
INDEX iOldVtx1 = bsc_abvxVertices.Index( bpoOld.bpo_apbvxTriangleVertices[ iVtx1]);
INDEX iOldVtx2 = bsc_abvxVertices.Index( bpoOld.bpo_apbvxTriangleVertices[ iVtx2]);
CBrushVertex *pbvtx0 = &abvxVerticesNew[iOldVtx0];
CBrushVertex *pbvtx1 = &abvxVerticesNew[iOldVtx1];
CBrushVertex *pbvtx2 = &abvxVerticesNew[iOldVtx2];
CBrushVertex *pbvtx3 = &abvxVerticesNew[ iVtx];
pbvtx3->bvx_pbscSector = this;
// calculate and set middle point of the triangle
DOUBLE3D vCenter = FLOATtoDOUBLE(
pbvtx0->bvx_vAbsolute +
pbvtx1->bvx_vAbsolute +
pbvtx2->bvx_vAbsolute)/3.0;
pbvtx3->SetAbsolutePosition( vCenter);
// add new edges
// setup edge 0 (copy of old edge)
abedEdgesNew[iEdge+0].bed_pbvxVertex0 = pbvtx0;
abedEdgesNew[iEdge+0].bed_pbvxVertex1 = pbvtx1;
// setup edge 1 (copy of old edge)
abedEdgesNew[iEdge+1].bed_pbvxVertex0 = pbvtx1;
abedEdgesNew[iEdge+1].bed_pbvxVertex1 = pbvtx2;
// setup edge 2 (copy of old edge)
abedEdgesNew[iEdge+2].bed_pbvxVertex0 = pbvtx2;
abedEdgesNew[iEdge+2].bed_pbvxVertex1 = pbvtx0;
// setup edge 3
abedEdgesNew[iEdge+3].bed_pbvxVertex0 = pbvtx0;
abedEdgesNew[iEdge+3].bed_pbvxVertex1 = pbvtx3;
// setup edge 4
abedEdgesNew[iEdge+4].bed_pbvxVertex0 = pbvtx1;
abedEdgesNew[iEdge+4].bed_pbvxVertex1 = pbvtx3;
// setup edge 5
abedEdgesNew[iEdge+5].bed_pbvxVertex0 = pbvtx2;
abedEdgesNew[iEdge+5].bed_pbvxVertex1 = pbvtx3;
// ---------------- Create first sub-triangle
CBrushPolygon &bpoNew1 = abpoPolygonsNew[ iNewPolygons+0];
// allocate and set polygon edges
bpoNew1.bpo_abpePolygonEdges.New(3);
bpoNew1.bpo_abpePolygonEdges[0].bpe_pbedEdge = &abedEdgesNew[iEdge+0];
bpoNew1.bpo_abpePolygonEdges[0].bpe_bReverse = FALSE;
bpoNew1.bpo_abpePolygonEdges[1].bpe_pbedEdge = &abedEdgesNew[iEdge+4];
bpoNew1.bpo_abpePolygonEdges[1].bpe_bReverse = FALSE;
bpoNew1.bpo_abpePolygonEdges[2].bpe_pbedEdge = &abedEdgesNew[iEdge+3];
bpoNew1.bpo_abpePolygonEdges[2].bpe_bReverse = TRUE;
// set brush vertex ptrs
bpoNew1.bpo_apbvxTriangleVertices.New(3);
bpoNew1.bpo_apbvxTriangleVertices[0] = pbvtx0;
bpoNew1.bpo_apbvxTriangleVertices[1] = pbvtx1;
bpoNew1.bpo_apbvxTriangleVertices[2] = pbvtx3;
// setup fixed trinagle element indices
bpoNew1.bpo_aiTriangleElements.New(3);
bpoNew1.bpo_aiTriangleElements[0] = 0;
bpoNew1.bpo_aiTriangleElements[1] = 1;
bpoNew1.bpo_aiTriangleElements[2] = 2;
// copy parameters from old polygon
bpoNew1.bpo_pbplPlane = bpoOld.bpo_pbplPlane;
bpoNew1.bpo_abptTextures[0].CopyTextureProperties( bpoOld.bpo_abptTextures[0], TRUE);
bpoNew1.bpo_abptTextures[1].CopyTextureProperties( bpoOld.bpo_abptTextures[1], TRUE);
bpoNew1.bpo_abptTextures[2].CopyTextureProperties( bpoOld.bpo_abptTextures[2], TRUE);
bpoNew1.bpo_colColor = bpoOld.bpo_colColor;
bpoNew1.bpo_ulFlags = bpoOld.bpo_ulFlags & ~(BPOF_MARKED_FOR_USE|BPOF_SELECTED);
bpoNew1.bpo_colShadow = bpoOld.bpo_colShadow;
bpoNew1.bpo_bppProperties = bpoOld.bpo_bppProperties;
bpoNew1.bpo_pbscSector = bpoOld.bpo_pbscSector;
// initialize shadow map
bpoNew1.InitializeShadowMap();
// ---------------- Create second sub-triangle
CBrushPolygon &bpoNew2 = abpoPolygonsNew[ iNewPolygons+1];
// allocate and set polygon edges
bpoNew2.bpo_abpePolygonEdges.New(3);
bpoNew2.bpo_abpePolygonEdges[0].bpe_pbedEdge = &abedEdgesNew[iEdge+1];
bpoNew2.bpo_abpePolygonEdges[0].bpe_bReverse = FALSE;
bpoNew2.bpo_abpePolygonEdges[1].bpe_pbedEdge = &abedEdgesNew[iEdge+5];
bpoNew2.bpo_abpePolygonEdges[1].bpe_bReverse = FALSE;
bpoNew2.bpo_abpePolygonEdges[2].bpe_pbedEdge = &abedEdgesNew[iEdge+4];
bpoNew2.bpo_abpePolygonEdges[2].bpe_bReverse = TRUE;
// set brush vertex ptrs
bpoNew2.bpo_apbvxTriangleVertices.New(3);
bpoNew2.bpo_apbvxTriangleVertices[0] = pbvtx1;
bpoNew2.bpo_apbvxTriangleVertices[1] = pbvtx2;
bpoNew2.bpo_apbvxTriangleVertices[2] = pbvtx3;
// setup fixed trinagle element indices
bpoNew2.bpo_aiTriangleElements.New(3);
bpoNew2.bpo_aiTriangleElements[0] = 0;
bpoNew2.bpo_aiTriangleElements[1] = 1;
bpoNew2.bpo_aiTriangleElements[2] = 2;
// copy parameters from old polygon
bpoNew2.bpo_pbplPlane = bpoOld.bpo_pbplPlane;
bpoNew2.bpo_abptTextures[0].CopyTextureProperties( bpoOld.bpo_abptTextures[0], TRUE);
bpoNew2.bpo_abptTextures[1].CopyTextureProperties( bpoOld.bpo_abptTextures[1], TRUE);
bpoNew2.bpo_abptTextures[2].CopyTextureProperties( bpoOld.bpo_abptTextures[2], TRUE);
bpoNew2.bpo_colColor = bpoOld.bpo_colColor;
bpoNew2.bpo_ulFlags = bpoOld.bpo_ulFlags & ~(BPOF_MARKED_FOR_USE|BPOF_SELECTED);
bpoNew2.bpo_colShadow = bpoOld.bpo_colShadow;
bpoNew2.bpo_bppProperties = bpoOld.bpo_bppProperties;
bpoNew2.bpo_pbscSector = bpoOld.bpo_pbscSector;
// initialize shadow map
bpoNew2.InitializeShadowMap();
// ---------------- Create third sub-triangle
CBrushPolygon &bpoNew3 = abpoPolygonsNew[ iNewPolygons+2];
// allocate and set polygon edges
bpoNew3.bpo_abpePolygonEdges.New(3);
bpoNew3.bpo_abpePolygonEdges[0].bpe_pbedEdge = &abedEdgesNew[iEdge+2];
bpoNew3.bpo_abpePolygonEdges[0].bpe_bReverse = FALSE;
bpoNew3.bpo_abpePolygonEdges[1].bpe_pbedEdge = &abedEdgesNew[iEdge+3];
bpoNew3.bpo_abpePolygonEdges[1].bpe_bReverse = FALSE;
bpoNew3.bpo_abpePolygonEdges[2].bpe_pbedEdge = &abedEdgesNew[iEdge+5];
bpoNew3.bpo_abpePolygonEdges[2].bpe_bReverse = TRUE;
// set brush vertex ptrs
bpoNew3.bpo_apbvxTriangleVertices.New(3);
bpoNew3.bpo_apbvxTriangleVertices[0] = pbvtx2;
bpoNew3.bpo_apbvxTriangleVertices[1] = pbvtx0;
bpoNew3.bpo_apbvxTriangleVertices[2] = pbvtx3;
// setup fixed trinagle element indices
bpoNew3.bpo_aiTriangleElements.New(3);
bpoNew3.bpo_aiTriangleElements[0] = 0;
bpoNew3.bpo_aiTriangleElements[1] = 1;
bpoNew3.bpo_aiTriangleElements[2] = 2;
// copy parameters from old polygon
bpoNew3.bpo_pbplPlane = bpoOld.bpo_pbplPlane;
bpoNew3.bpo_abptTextures[0].CopyTextureProperties( bpoOld.bpo_abptTextures[0], TRUE);
bpoNew3.bpo_abptTextures[1].CopyTextureProperties( bpoOld.bpo_abptTextures[1], TRUE);
bpoNew3.bpo_abptTextures[2].CopyTextureProperties( bpoOld.bpo_abptTextures[2], TRUE);
bpoNew3.bpo_colColor = bpoOld.bpo_colColor;
bpoNew3.bpo_ulFlags = bpoOld.bpo_ulFlags & ~(BPOF_MARKED_FOR_USE|BPOF_SELECTED);
bpoNew3.bpo_colShadow = bpoOld.bpo_colShadow;
bpoNew3.bpo_bppProperties = bpoOld.bpo_bppProperties;
bpoNew3.bpo_pbscSector = bpoOld.bpo_pbscSector;
// initialize shadow map
bpoNew3.InitializeShadowMap();
// skip newly created vertex
iVtx++;
// skip created edges
iEdge+=6;
// next triangle
iNewPolygons += 3;
}
}}
// copy new arrays over old ones
bsc_awvxVertices.MoveArray( awvxVerticesNew);
bsc_abvxVertices.MoveArray( abvxVerticesNew);
bsc_abedEdges.MoveArray( abedEdgesNew);
bsc_abpoPolygons.MoveArray( abpoPolygonsNew);
// create array of working edges
INDEX cted = bsc_abedEdges.Count();
bsc_awedEdges.Clear();
bsc_awedEdges.New(cted);
// for each edge
for (INDEX ied=0; ied<cted; ied++)
{
CWorkingEdge &wed = bsc_awedEdges[ied];
CBrushEdge &bed = bsc_abedEdges[ied];
// setup its working edge
bed.bed_pwedWorking = &wed;
wed.wed_iwvx0 = bsc_abvxVertices.Index(bed.bed_pbvxVertex0);
wed.wed_iwvx1 = bsc_abvxVertices.Index(bed.bed_pbvxVertex1);
}
// recalculate planes for polygons from their vertices
MakePlanesFromVertices();
UpdateSector();
}
// Search for shared edge in two given triangles
void GetSharedEdge( CBrushPolygon *pbpo0, CBrushPolygon *pbpo1, CBrushEdge *&pse)
{
for( INDEX iFirst=0; iFirst<3; iFirst++)
{
for( INDEX iSecond=0; iSecond<3; iSecond++)
{
if( pbpo0->bpo_abpePolygonEdges[iFirst].bpe_pbedEdge ==
pbpo1->bpo_abpePolygonEdges[iSecond].bpe_pbedEdge )
{
pse = pbpo0->bpo_abpePolygonEdges[iFirst].bpe_pbedEdge;
return;
}
}
}
pse = NULL;
}
BOOL CBrushSector::IsReTripleAvailable( CBrushPolygonSelection &selPolygon)
{
// we must have two polygons in selection
if( selPolygon.Count() != 2) return FALSE;
// obtain polygons
CBrushPolygon *pbpoOld0 = selPolygon.sa_Array[0];
CBrushPolygon *pbpoOld1 = selPolygon.sa_Array[1];
// must be in the same sector
if( pbpoOld0->bpo_pbscSector != pbpoOld1->bpo_pbscSector) return FALSE;
// both of them must be triangles
if( pbpoOld0->bpo_aiTriangleElements.Count() != 3) return FALSE;
if( pbpoOld1->bpo_aiTriangleElements.Count() != 3) return FALSE;
// triangles must share an edge
CBrushEdge *pse = NULL;
GetSharedEdge( pbpoOld0, pbpoOld1, pse);
return(pse != NULL);
}
void GetNonSharedEdgesContainingVtx(CBrushPolygon *pbpo0, CBrushPolygon *pbpo1,
CBrushEdge *pse, CBrushVertex *pvtx,
CBrushPolygonEdge *&pbpe0, CBrushPolygonEdge *&pbpe1)
{
// set invalid ptrs
pbpe0 = NULL;
pbpe1 = NULL;
// for first triangle
for( INDEX itri0=0; itri0<3; itri0++)
{
CBrushPolygonEdge *pbpeTest = &pbpo0->bpo_abpePolygonEdges[itri0];
if( ( pbpeTest->bpe_pbedEdge != pse) && // if it is not shared edge
((pbpeTest->bpe_pbedEdge->bed_pbvxVertex0 == pvtx) || // and contains given vertex
(pbpeTest->bpe_pbedEdge->bed_pbvxVertex1 == pvtx)) )
{
pbpe0 = pbpeTest;
}
}
// for second triangle
for( INDEX itri1=0; itri1<3; itri1++)
{
CBrushPolygonEdge *pbpeTest = &pbpo1->bpo_abpePolygonEdges[itri1];
if( ( pbpeTest->bpe_pbedEdge != pse) && // if it is not shared edge
((pbpeTest->bpe_pbedEdge->bed_pbvxVertex0 == pvtx) || // and contains given vertex
(pbpeTest->bpe_pbedEdge->bed_pbvxVertex1 == pvtx)) )
{
pbpe1 = pbpeTest;
}
}
// both must be found
ASSERT( pbpe0 != NULL);
ASSERT( pbpe1 != NULL);
return;
}
void CBrushSector::ReTriple( CBrushPolygonSelection &selPolygon)
{
// we must have two polygons in selection
if( selPolygon.Count() != 2) return;
// obtain polygons
CBrushPolygon *pbpoOld0 = selPolygon.sa_Array[0];
CBrushPolygon *pbpoOld1 = selPolygon.sa_Array[1];
// both of them must be triangles
if( pbpoOld0->bpo_aiTriangleElements.Count() != 3) return;
if( pbpoOld1->bpo_aiTriangleElements.Count() != 3) return;
// clear marked for use flag on all polygons in world
CWorld *pwo=bsc_pbmBrushMip->bm_pbrBrush->br_penEntity->en_pwoWorld;
pwo->ClearMarkedForUseFlag();
// mark them for use
pbpoOld0->bpo_ulFlags |= BPOF_MARKED_FOR_USE;
pbpoOld1->bpo_ulFlags |= BPOF_MARKED_FOR_USE;
// clear the selection (polygons will be erased)
selPolygon.Clear();
// create new arrays
CStaticArray<CBrushEdge> abedEdgesNew;
CStaticArray<CBrushPolygon> abpoPolygonsNew;
INDEX ctOldEdges = bsc_abedEdges.Count();
INDEX ctOldPolygons = bsc_abpoPolygons.Count();
// allocate arrays
abedEdgesNew.New( ctOldEdges+1);
abpoPolygonsNew.New( ctOldPolygons);
// copy old edges to new edge array
INDEX iEdge;
for( iEdge=0; iEdge<ctOldEdges; iEdge++)
{
// copy vertex ptrs
abedEdgesNew[iEdge].bed_pbvxVertex0 = bsc_abedEdges[iEdge].bed_pbvxVertex0;
abedEdgesNew[iEdge].bed_pbvxVertex1 = bsc_abedEdges[iEdge].bed_pbvxVertex1;
}
// note that iEdge points to first new edge
// ----------- Copy old polygons (except two new ones)
// for all polygons in this sector
INDEX iNewPolygons=0;
{FOREACHINSTATICARRAY(bsc_abpoPolygons, CBrushPolygon, itbpo)
{
CBrushPolygon &bpoOld = *itbpo;
// polygon shouldn't be subdivided
if( !(itbpo->bpo_ulFlags & BPOF_MARKED_FOR_USE))
{
// copy the old polygon
CBrushPolygon &bpoNew = abpoPolygonsNew[ iNewPolygons];
bpoNew.bpo_pbplPlane = bpoOld.bpo_pbplPlane;
bpoNew.bpo_abptTextures[0].CopyTextureProperties( bpoOld.bpo_abptTextures[0], TRUE);
bpoNew.bpo_abptTextures[1].CopyTextureProperties( bpoOld.bpo_abptTextures[1], TRUE);
bpoNew.bpo_abptTextures[2].CopyTextureProperties( bpoOld.bpo_abptTextures[2], TRUE);
bpoNew.bpo_colColor = bpoOld.bpo_colColor;
bpoNew.bpo_ulFlags = bpoOld.bpo_ulFlags & ~(BPOF_MARKED_FOR_USE|BPOF_SELECTED);
bpoNew.bpo_colShadow = bpoOld.bpo_colShadow;
bpoNew.bpo_bppProperties = bpoOld.bpo_bppProperties;
bpoNew.bpo_pbscSector = bpoOld.bpo_pbscSector;
// remap brush polygon edges to point to edges of new array
INDEX ctEdgesToRemap = bpoOld.bpo_abpePolygonEdges.Count();
// allocate new polygon edges
bpoNew.bpo_abpePolygonEdges.New(ctEdgesToRemap);
// for each edge in polygon
for( INDEX iRemapEdge=0; iRemapEdge<ctEdgesToRemap; iRemapEdge++)
{
CBrushPolygonEdge &bpeOld = bpoOld.bpo_abpePolygonEdges[iRemapEdge];
CBrushPolygonEdge &bpeNew = bpoNew.bpo_abpePolygonEdges[iRemapEdge];
// get index of the edge for old edge array
INDEX iOldIndex = bsc_abedEdges.Index( bpeOld.bpe_pbedEdge);
// use same index, but point to edge in new edge array
bpeNew.bpe_pbedEdge = &abedEdgesNew[iOldIndex];
// set edge direction
bpeNew.bpe_bReverse = bpeOld.bpe_bReverse;
}
// allocate and set vertex pointers
INDEX ctOldTVtx = bpoOld.bpo_apbvxTriangleVertices.Count();
bpoNew.bpo_apbvxTriangleVertices.New(ctOldTVtx);
// copy old brush vertex pointers
for( INDEX iTVtx=0; iTVtx<ctOldTVtx; iTVtx++)
{
bpoNew.bpo_apbvxTriangleVertices[iTVtx] = bpoOld.bpo_apbvxTriangleVertices[iTVtx];
}
// copy triangle and triangle elements arrays
bpoNew.bpo_aiTriangleElements = bpoOld.bpo_aiTriangleElements;
// initialize shadow map
bpoNew.InitializeShadowMap();
iNewPolygons++;
}
}}
// get shared edge
CBrushEdge *pse = NULL;
GetSharedEdge( pbpoOld0, pbpoOld1, pse);
ASSERT( pse != NULL);
// obtain vertices 0 and 1 of shared edge
CBrushVertex *pv0se = pse->bed_pbvxVertex0;
CBrushVertex *pv1se = pse->bed_pbvxVertex1;
// they will form first two edges of retripled polygon (each edge will be from different polygon)
CBrushPolygonEdge *pbpev0e0;
CBrushPolygonEdge *pbpev0e1;
// from two given polygons, extract edges different from shared edge that contain given vertex of shared edge
GetNonSharedEdgesContainingVtx( pbpoOld0, pbpoOld1, pse, pv0se, pbpev0e0, pbpev0e1);
#define REMAP_EDGE( pedg) \
{INDEX iIndex = bsc_abedEdges.Index( pedg->bpe_pbedEdge);\
pedg->bpe_pbedEdge = &abedEdgesNew[iIndex];}
REMAP_EDGE( pbpev0e0);
REMAP_EDGE( pbpev0e1);
// get two other edges, for second retripled triangle
CBrushPolygonEdge *pbpev1e0;
CBrushPolygonEdge *pbpev1e1;
GetNonSharedEdgesContainingVtx( pbpoOld0, pbpoOld1, pse, pv1se, pbpev1e0, pbpev1e1);
REMAP_EDGE( pbpev1e0);
REMAP_EDGE( pbpev1e1);
// find edges that exit and enter shared edge's vertex 0
CBrushPolygonEdge *pbpeExit;
CBrushPolygonEdge *pbpeEnter;
if( ((pbpev0e0->bpe_pbedEdge->bed_pbvxVertex0 == pv0se) && !pbpev0e0->bpe_bReverse) ||
((pbpev0e0->bpe_pbedEdge->bed_pbvxVertex1 == pv0se) && pbpev0e0->bpe_bReverse) )
{
pbpeExit = pbpev0e0;
pbpeEnter = pbpev0e1;
}
else
{
pbpeExit = pbpev0e1;
pbpeEnter = pbpev0e0;
}
// find start vertex of new edge
CBrushVertex *pvNew0;
if( pbpeExit->bpe_pbedEdge->bed_pbvxVertex0 != pv0se)
{
pvNew0 = pbpeExit->bpe_pbedEdge->bed_pbvxVertex0;
}
else
{
pvNew0 = pbpeExit->bpe_pbedEdge->bed_pbvxVertex1;
}
// find end vertex of new edge
CBrushVertex *pvNew1;
if( pbpeEnter->bpe_pbedEdge->bed_pbvxVertex0 != pv0se)
{
pvNew1 = pbpeEnter->bpe_pbedEdge->bed_pbvxVertex0;
}
else
{
pvNew1 = pbpeEnter->bpe_pbedEdge->bed_pbvxVertex1;
}
// add new edge
abedEdgesNew[iEdge].bed_pbvxVertex0 = pvNew0;
abedEdgesNew[iEdge].bed_pbvxVertex1 = pvNew1;
// --------------- Create retripled triangle 1
{
// copy the old polygon
CBrushPolygon &bpoNew = abpoPolygonsNew[ iNewPolygons+0];
CBrushPolygon &bpoOld = *pbpoOld0;
bpoNew.bpo_pbplPlane = bpoOld.bpo_pbplPlane;
bpoNew.bpo_abptTextures[0].CopyTextureProperties( bpoOld.bpo_abptTextures[0], TRUE);
bpoNew.bpo_abptTextures[1].CopyTextureProperties( bpoOld.bpo_abptTextures[1], TRUE);
bpoNew.bpo_abptTextures[2].CopyTextureProperties( bpoOld.bpo_abptTextures[2], TRUE);
bpoNew.bpo_colColor = bpoOld.bpo_colColor;
bpoNew.bpo_ulFlags = bpoOld.bpo_ulFlags & ~(BPOF_MARKED_FOR_USE|BPOF_SELECTED);
bpoNew.bpo_colShadow = bpoOld.bpo_colShadow;
bpoNew.bpo_bppProperties = bpoOld.bpo_bppProperties;
bpoNew.bpo_pbscSector = bpoOld.bpo_pbscSector;
// allocate polygon edges
bpoNew.bpo_abpePolygonEdges.New(3);
// set edges data
bpoNew.bpo_abpePolygonEdges[0].bpe_pbedEdge = pbpev0e0->bpe_pbedEdge;
bpoNew.bpo_abpePolygonEdges[0].bpe_bReverse = pbpev0e0->bpe_bReverse;
bpoNew.bpo_abpePolygonEdges[1].bpe_pbedEdge = pbpev0e1->bpe_pbedEdge;
bpoNew.bpo_abpePolygonEdges[1].bpe_bReverse = pbpev0e1->bpe_bReverse;
bpoNew.bpo_abpePolygonEdges[2].bpe_pbedEdge = &abedEdgesNew[iEdge];
bpoNew.bpo_abpePolygonEdges[2].bpe_bReverse = FALSE;
// allocate and set vertex pointers
bpoNew.bpo_apbvxTriangleVertices.New(3);
bpoNew.bpo_apbvxTriangleVertices[0] = pv0se;
bpoNew.bpo_apbvxTriangleVertices[1] = pvNew0;
bpoNew.bpo_apbvxTriangleVertices[2] = pvNew1;
// copy triangle and triangle elements arrays
bpoNew.bpo_aiTriangleElements.New(3);
bpoNew.bpo_aiTriangleElements[0] = 0;
bpoNew.bpo_aiTriangleElements[1] = 1;
bpoNew.bpo_aiTriangleElements[2] = 2;
// initialize shadow map
bpoNew.InitializeShadowMap();
}
// --------------- Create retripled triangle 2
{
// copy the old polygon
CBrushPolygon &bpoNew = abpoPolygonsNew[ iNewPolygons+1];
CBrushPolygon &bpoOld = *pbpoOld1;
bpoNew.bpo_pbplPlane = bpoOld.bpo_pbplPlane;
bpoNew.bpo_abptTextures[0].CopyTextureProperties( bpoOld.bpo_abptTextures[0], TRUE);
bpoNew.bpo_abptTextures[1].CopyTextureProperties( bpoOld.bpo_abptTextures[1], TRUE);
bpoNew.bpo_abptTextures[2].CopyTextureProperties( bpoOld.bpo_abptTextures[2], TRUE);
bpoNew.bpo_colColor = bpoOld.bpo_colColor;
bpoNew.bpo_ulFlags = bpoOld.bpo_ulFlags & ~(BPOF_MARKED_FOR_USE|BPOF_SELECTED);
bpoNew.bpo_colShadow = bpoOld.bpo_colShadow;
bpoNew.bpo_bppProperties = bpoOld.bpo_bppProperties;
bpoNew.bpo_pbscSector = bpoOld.bpo_pbscSector;
// allocate polygon edges
bpoNew.bpo_abpePolygonEdges.New(3);
// set edges data
bpoNew.bpo_abpePolygonEdges[0].bpe_pbedEdge = pbpev1e0->bpe_pbedEdge;
bpoNew.bpo_abpePolygonEdges[0].bpe_bReverse = pbpev1e0->bpe_bReverse;
bpoNew.bpo_abpePolygonEdges[1].bpe_pbedEdge = pbpev1e1->bpe_pbedEdge;
bpoNew.bpo_abpePolygonEdges[1].bpe_bReverse = pbpev1e1->bpe_bReverse;
bpoNew.bpo_abpePolygonEdges[2].bpe_pbedEdge = &abedEdgesNew[iEdge];
bpoNew.bpo_abpePolygonEdges[2].bpe_bReverse = TRUE;
// allocate and set vertex pointers
bpoNew.bpo_apbvxTriangleVertices.New(3);
bpoNew.bpo_apbvxTriangleVertices[0] = pv1se;
bpoNew.bpo_apbvxTriangleVertices[1] = pvNew1;
bpoNew.bpo_apbvxTriangleVertices[2] = pvNew0;
// copy triangle and triangle elements arrays
bpoNew.bpo_aiTriangleElements.New(3);
bpoNew.bpo_aiTriangleElements[0] = 0;
bpoNew.bpo_aiTriangleElements[1] = 1;
bpoNew.bpo_aiTriangleElements[2] = 2;
// initialize shadow map
bpoNew.InitializeShadowMap();
}
// copy new arrays over old ones
bsc_abedEdges.MoveArray( abedEdgesNew);
bsc_abpoPolygons.MoveArray( abpoPolygonsNew);
// create array of working edges
INDEX cted = bsc_abedEdges.Count();
bsc_awedEdges.Clear();
bsc_awedEdges.New(cted);
// for each edge
for (INDEX ied=0; ied<cted; ied++)
{
CWorkingEdge &wed = bsc_awedEdges[ied];
CBrushEdge &bed = bsc_abedEdges[ied];
// setup its working edge
bed.bed_pwedWorking = &wed;
wed.wed_iwvx0 = bsc_abvxVertices.Index(bed.bed_pbvxVertex0);
wed.wed_iwvx1 = bsc_abvxVertices.Index(bed.bed_pbvxVertex1);
}
// recalculate planes for polygons from their vertices
MakePlanesFromVertices();
UpdateSector();
}
// get amount of memory used by this object
SLONG CBrushSector::GetUsedMemory(void)
{
// init
SLONG slUsedMemory = sizeof(CBrushSector);
// add some more
slUsedMemory += bsc_strName.Length();
slUsedMemory += bsc_rdOtherSidePortals.Count() * sizeof(CRelationLnk);
slUsedMemory += bsc_rsEntities.Count() * sizeof(CRelationLnk);
return slUsedMemory;
}