Serious-Engine/Sources/Engine/World/World.cpp
Emil Laine 5badefaf90 Change remaining "if (this!=NULL)" to "ASSERT(this!=NULL)"
Also changed all "if (this==NULL) return;"s.

Fixes some -Wtautological-undefined-compare warnings.

Quoting Clang:
"'this' pointer cannot be null in well-defined C++ code; comparison may
be assumed to always evaluate to false"
2016-05-30 00:48:47 +02:00

1163 lines
34 KiB
C++

/* 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 <Engine/Base/Console.h>
#include <Engine/Math/Float.h>
#include <Engine/World/World.h>
#include <Engine/World/WorldEditingProfile.h>
#include <Engine/Graphics/RenderScene.h>
#include <Engine/World/WorldSettings.h>
#include <Engine/Entities/EntityClass.h>
#include <Engine/Entities/Precaching.h>
#include <Engine/Entities/EntityProperties.h>
#include <Engine/Base/ListIterator.inl>
#include <Engine/Templates/DynamicContainer.cpp>
#include <Engine/Graphics/Color.h>
#include <Engine/Brushes/BrushArchive.h>
#include <Engine/Terrain/TerrainArchive.h>
#include <Engine/Entities/InternalClasses.h>
#include <Engine/Network/Network.h>
#include <Engine/Network/SessionState.h>
#include <Engine/Templates/DynamicArray.cpp>
#include <Engine/Brushes/Brush.h>
#include <Engine/Light/LightSource.h>
#include <Engine/Base/ProgressHook.h>
#include <Engine/Templates/StaticArray.cpp>
#include <Engine/Templates/Selection.cpp>
#include <Engine/Terrain/Terrain.h>
#include <Engine/Templates/Stock_CEntityClass.h>
template class CDynamicContainer<CEntity>;
template class CSelection<CBrushPolygon, BPOF_SELECTED>;
template class CSelection<CBrushSector, BSCF_SELECTED>;
template class CSelection<CEntity, ENF_SELECTED>;
extern BOOL _bPortalSectorLinksPreLoaded;
extern BOOL _bEntitySectorLinksPreLoaded;
extern INDEX _ctPredictorEntities;
#if 0 // DG: unused.
// calculate ray placement from origin and target positions (obsolete?)
static inline CPlacement3D CalculateRayPlacement(
const FLOAT3D &vOrigin, const FLOAT3D &vTarget)
{
CPlacement3D plRay;
// ray position is at origin
plRay.pl_PositionVector = vOrigin;
// calculate ray direction vector
FLOAT3D vDirection = vTarget-vOrigin;
// calculate ray orientation from the direction vector
vDirection.Normalize();
DirectionVectorToAngles(vDirection, plRay.pl_OrientationAngle);
return plRay;
}
#endif // 0
/* Constructor. */
CTextureTransformation::CTextureTransformation(void)
{
tt_strName = "";
}
/* Constructor. */
CTextureBlending::CTextureBlending(void)
{
tb_strName = "";
tb_ubBlendingType = STXF_BLEND_OPAQUE;
tb_colMultiply = C_WHITE|0xFF;
}
/*
* Constructor.
*/
CWorld::CWorld(void)
: wo_pecWorldBaseClass(NULL) // worldbase class must be obtained before using the world
, wo_baBrushes(*new CBrushArchive)
, wo_taTerrains(*new CTerrainArchive)
, wo_colBackground(C_lGRAY) // clear background color
, wo_ulSpawnFlags(0)
, wo_bPortalLinksUpToDate(FALSE) // portal-sector links must be updated
{
wo_baBrushes.ba_pwoWorld = this;
wo_taTerrains.ta_pwoWorld = this;
// create empty texture movements
wo_attTextureTransformations.New(256);
wo_atbTextureBlendings.New(256);
wo_astSurfaceTypes.New(256);
wo_actContentTypes.New(256);
wo_aetEnvironmentTypes.New(256);
wo_aitIlluminationTypes.New(256);
// initialize collision grid
InitCollisionGrid();
wo_slStateDictionaryOffset = 0;
wo_strBackdropUp = "";
wo_strBackdropFt = "";
wo_strBackdropRt = "";
wo_strBackdropObject = "";
wo_fUpW = wo_fUpL = 1.0f; wo_fUpCX = wo_fUpCZ = 0.0f;
wo_fFtW = wo_fFtH = 1.0f; wo_fFtCX = wo_fFtCY = 0.0f;
wo_fRtL = wo_fRtH = 1.0f; wo_fRtCZ = wo_fRtCY = 0.0f;
wo_ulNextEntityID = 1;
// set default placement
wo_plFocus = CPlacement3D( FLOAT3D(3.0f, 4.0f, 10.0f),
ANGLE3D(AngleDeg( 20.0f), AngleDeg( -20.0f), 0));
wo_fTargetDistance = 10.0f;
// set default thumbnail placement
wo_plThumbnailFocus = CPlacement3D( FLOAT3D(3.0f, 4.0f, 10.0f),
ANGLE3D(AngleDeg( 20.0f), AngleDeg( -20.0f), 0));
wo_fThumbnailTargetDistance = 10.0f;
}
/*
* Destructor.
*/
CWorld::~CWorld()
{
// clear all arrays
Clear();
// destroy collision grid
DestroyCollisionGrid();
delete &wo_baBrushes;
delete &wo_taTerrains;
}
/*
* Clear all arrays.
*/
void CWorld::Clear(void)
{
// detach worldbase class
if (wo_pecWorldBaseClass!=NULL) {
if ( wo_pecWorldBaseClass->ec_pdecDLLClass!=NULL
&&wo_pecWorldBaseClass->ec_pdecDLLClass->dec_OnWorldEnd!=NULL) {
wo_pecWorldBaseClass->ec_pdecDLLClass->dec_OnWorldEnd(this);
}
wo_pecWorldBaseClass=NULL;
}
{
// must be in 24bit mode when managing entities
CSetFPUPrecision FPUPrecision(FPT_24BIT);
// clear background viewer
SetBackgroundViewer(NULL);
// make a new container of entities
CDynamicContainer<CEntity> cenToDestroy = wo_cenEntities;
// for each of the entities
{FOREACHINDYNAMICCONTAINER(cenToDestroy, CEntity, iten) {
// destroy it
iten->Destroy();
}}
// the original container must be empty
ASSERT(wo_cenEntities.Count()==0);
ASSERT(wo_cenAllEntities.Count()==0);
wo_cenEntities.Clear();
wo_cenAllEntities.Clear();
cenToDestroy.Clear();
wo_ulNextEntityID = 1;
}
// clear brushes
wo_baBrushes.ba_abrBrushes.Clear();
// clear terrains
wo_taTerrains.ta_atrTerrains.Clear();
extern void ClearMovableEntityCaches(void);
ClearMovableEntityCaches();
// clear collision grid
ClearCollisionGrid();
}
/*
* Create a new entity of given class.
*/
CEntity *CWorld::CreateEntity(const CPlacement3D &plPlacement, CEntityClass *pecClass)
{
// must be in 24bit mode when managing entities
CSetFPUPrecision FPUPrecision(FPT_24BIT);
// if the world base class is not yet remembered and this class is world base
if (wo_pecWorldBaseClass==NULL
&& stricmp(pecClass->ec_pdecDLLClass->dec_strName, "WorldBase")==0) {
// remember it
wo_pecWorldBaseClass = pecClass;
// execute the class attach function
if (pecClass->ec_pdecDLLClass->dec_OnWorldInit!=NULL) {
pecClass->ec_pdecDLLClass->dec_OnWorldInit(this);
}
}
// gather CRCs of that class
pecClass->AddToCRCTable();
// ask the class to instance a new member
CEntity *penEntity = pecClass->New();
// add the reference made by the entity itself
penEntity->AddReference();
// set the entity's world pointer to this world
penEntity->en_pwoWorld = this;
// add the new member to this world's entity container
wo_cenEntities.Add(penEntity);
wo_cenAllEntities.Add(penEntity);
// set a new identifier
penEntity->en_ulID = wo_ulNextEntityID++;
// set up the placement
penEntity->en_plPlacement = plPlacement;
// calculate rotation matrix
MakeRotationMatrixFast(penEntity->en_mRotation, penEntity->en_plPlacement.pl_OrientationAngle);
// if now predicting
if (_pNetwork->IsPredicting()) {
// mark entity as a temporary predictor
penEntity->en_ulFlags |= ENF_PREDICTOR|ENF_TEMPPREDICTOR;
wo_cenPredictor.Add(penEntity);
_ctPredictorEntities++;
}
// return it
return penEntity;
}
/*
* Create a new entity of given class.
*/
CEntity *CWorld::CreateEntity_t(const CPlacement3D &plPlacement,
const CTFileName &fnmClass) // throw char *
{
// obtain a new entity class from global stock
CEntityClass *pecClass = _pEntityClassStock->Obtain_t(fnmClass);
// create entity with that class (obtains it once more)
CEntity *penNew = CreateEntity(plPlacement, pecClass);
// release the class
_pEntityClassStock->Release(pecClass);
// return the entity
return penNew;
}
/*
* Destroy one entities.
*/
void CWorld::DestroyOneEntity( CEntity *penToDestroy)
{
// if the entity is targetable
if (penToDestroy->IsTargetable()) {
// remove all eventual pointers to it
UntargetEntity( penToDestroy);
}
// destroy it
penToDestroy->Destroy();
}
/*
* Destroy a selection of entities.
*/
void CWorld::DestroyEntities(CEntitySelection &senToDestroy)
{
// must be in 24bit mode when managing entities
CSetFPUPrecision FPUPrecision(FPT_24BIT);
// for each entity in selection
FOREACHINDYNAMICCONTAINER(senToDestroy, CEntity, iten) {
// if the entity is targetable
if (iten->IsTargetable()) {
// remove all eventual pointers to it
UntargetEntity(iten);
}
// destroy it
iten->Destroy();
}
// clear the selection on the container level
/* NOTE: we must not clear the selection directly, since the entity objects
contained there are already freed and deselecting them would make an access
violation.
*/
senToDestroy.CDynamicContainer<CEntity>::Clear();
}
/*
* Clear all entity pointers that point to this entity.
*/
void CWorld::UntargetEntity(CEntity *penToUntarget)
{
// for all entities in this world
FOREACHINDYNAMICCONTAINER(wo_cenEntities, CEntity, itenInWorld){
// get the DLL class of this entity
CDLLEntityClass *pdecDLLClass = itenInWorld->en_pecClass->ec_pdecDLLClass;
// for all classes in hierarchy of this entity
for(;
pdecDLLClass!=NULL;
pdecDLLClass = pdecDLLClass->dec_pdecBase) {
// for all properties
for(INDEX iProperty=0; iProperty<pdecDLLClass->dec_ctProperties; iProperty++) {
CEntityProperty &epProperty = pdecDLLClass->dec_aepProperties[iProperty];
// if the property type is entity pointer
if (epProperty.ep_eptType == CEntityProperty::EPT_ENTITYPTR) {
// get the pointer
CEntityPointer &penPointed = ENTITYPROPERTY(&*itenInWorld, epProperty.ep_slOffset, CEntityPointer);
// if it points to the entity to be untargeted
if (penPointed == penToUntarget) {
itenInWorld->End();
// clear the pointer
penPointed = NULL;
itenInWorld->Initialize();
}
}
}
}
}
// if the entity is background viewer
if (wo_penBackgroundViewer==penToUntarget) {
// reset background viewer
SetBackgroundViewer(NULL);
}
}
/*
* Find an entity with given character.
*/
CPlayerEntity *CWorld::FindEntityWithCharacter(CPlayerCharacter &pcCharacter)
{
ASSERT(pcCharacter.pc_strName != "");
// for each entity
FOREACHINDYNAMICCONTAINER(wo_cenEntities, CEntity, iten) {
CEntity *pen = &*iten;
// if it is player entity
if (IsDerivedFromClass(pen, "PlayerEntity")) {
CPlayerEntity *penPlayer = (CPlayerEntity *)pen;
// if it has got that character
if (penPlayer->en_pcCharacter == pcCharacter) {
// return its pointer
return penPlayer;
}
}
}
// otherwise, none exists
return NULL;
}
/*
* Add an entity to list of thinkers.
*/
void CWorld::AddTimer(CRationalEntity *penThinker)
{
ASSERT(penThinker->en_timeTimer>=_pTimer->CurrentTick());
ASSERT(GetFPUPrecision()==FPT_24BIT);
// if the entity is already in the list
if (penThinker->en_lnInTimers.IsLinked()) {
// remove it
penThinker->en_lnInTimers.Remove();
}
// for each entity in the thinker list
FOREACHINLISTKEEP(CRationalEntity, en_lnInTimers, wo_lhTimers, iten) {
// if the entity in list has greater or same think time than the one to add
if (iten->en_timeTimer>=penThinker->en_timeTimer) {
// stop searching
break;
}
}
// add the new entity before current one
iten.InsertBeforeCurrent(penThinker->en_lnInTimers);
}
// set overdue timers to be due in current time
void CWorld::AdjustLateTimers(TIME tmCurrentTime)
{
// must be in 24bit mode when managing entities
CSetFPUPrecision FPUPrecision(FPT_24BIT);
// for each entity in the thinker list
FOREACHINLIST(CRationalEntity, en_lnInTimers, wo_lhTimers, iten) {
CRationalEntity &en = *iten;
// if the entity in list is overdue
if (en.en_timeTimer<tmCurrentTime) {
// set it to current time
en.en_timeTimer = tmCurrentTime;
}
}
}
/*
* Lock all arrays.
*/
void CWorld::LockAll(void)
{
wo_cenEntities.Lock();
wo_cenAllEntities.Lock();
// lock the brush archive
wo_baBrushes.ba_abrBrushes.Lock();
// lock the terrain archive
wo_taTerrains.ta_atrTerrains.Lock();
}
/*
* Unlock all arrays.
*/
void CWorld::UnlockAll(void)
{
wo_cenEntities.Unlock();
wo_cenAllEntities.Unlock();
// unlock the brush archive
wo_baBrushes.ba_abrBrushes.Unlock();
// unlock the brush archive
wo_taTerrains.ta_atrTerrains.Unlock();
}
/* Get background color for this world. */
COLOR CWorld::GetBackgroundColor(void)
{
return wo_colBackground;
}
/* Set background color for this world. */
void CWorld::SetBackgroundColor(COLOR colBackground)
{
wo_colBackground = colBackground;
}
/* Set background viewer entity for this world. */
void CWorld::SetBackgroundViewer(CEntity *penEntity)
{
wo_penBackgroundViewer = penEntity;
}
/* Get background viewer entity for this world. */
CEntity *CWorld::GetBackgroundViewer(void)
{
// if the background viewer entity is deleted
if (wo_penBackgroundViewer!=NULL && wo_penBackgroundViewer->en_ulFlags&ENF_DELETED) {
// clear the pointer
wo_penBackgroundViewer = NULL;
}
return wo_penBackgroundViewer;
}
/* Set description for this world. */
void CWorld::SetDescription(const CTString &strDescription)
{
wo_strDescription = strDescription;
}
/* Get description for this world. */
const CTString &CWorld::GetDescription(void)
{
return wo_strDescription;
}
// get/set name of the world
void CWorld::SetName(const CTString &strName)
{
wo_strName = strName;
}
const CTString &CWorld::GetName(void)
{
return wo_strName;
}
// get/set spawn flags for the world
void CWorld::SetSpawnFlags(ULONG ulFlags)
{
wo_ulSpawnFlags = ulFlags;
}
ULONG CWorld::GetSpawnFlags(void)
{
return wo_ulSpawnFlags;
}
/////////////////////////////////////////////////////////////////////
// Shadow manipulation functions
/*
* Recalculate all shadow maps that are not valid or of smaller precision.
*/
void CWorld::CalculateDirectionalShadows(void)
{
extern INDEX _ctShadowLayers;
extern INDEX _ctShadowClusters;
CTimerValue tvStart;
// clear shadow rendering stats
tvStart = _pTimer->GetHighPrecisionTimer();
_ctShadowLayers=0;
_ctShadowClusters=0;
// for each shadow map that is queued for calculation
FORDELETELIST(CBrushShadowMap, bsm_lnInUncalculatedShadowMaps,
wo_baBrushes.ba_lhUncalculatedShadowMaps, itbsm) {
// calculate shadows on it
itbsm->GetBrushPolygon()->MakeShadowMap(this, TRUE);
}
// report shadow rendering stats
CTimerValue tvStop = _pTimer->GetHighPrecisionTimer();
CPrintF("Shadow calculation: total %d clusters in %d layers, %fs\n",
_ctShadowClusters,
_ctShadowLayers,
(tvStop-tvStart).GetSeconds());
}
void CWorld::CalculateNonDirectionalShadows(void)
{
// for each shadow map that is queued for calculation
FORDELETELIST(CBrushShadowMap, bsm_lnInUncalculatedShadowMaps,
wo_baBrushes.ba_lhUncalculatedShadowMaps, itbsm) {
// calculate shadows on it
itbsm->GetBrushPolygon()->MakeShadowMap(this, FALSE);
}
}
/* Find all shadow layers near a certain position. */
void CWorld::FindShadowLayers(
const FLOATaabbox3D &boxNear,
BOOL bSelectedOnly /*=FALSE*/,
BOOL bDirectional /*= TRUE*/)
{
_pfWorldEditingProfile.StartTimer(CWorldEditingProfile::PTI_FINDSHADOWLAYERS);
// for each entity in the world
FOREACHINDYNAMICCONTAINER(wo_cenEntities, CEntity, iten) {
// if it is light entity and it influences the given range
CLightSource *pls = iten->GetLightSource();
if (pls!=NULL) {
FLOATaabbox3D boxLight(iten->en_plPlacement.pl_PositionVector, pls->ls_rFallOff);
if ( (bDirectional && (pls->ls_ulFlags & LSF_DIRECTIONAL))
||boxLight.HasContactWith(boxNear)) {
// find layers for that light source
pls->FindShadowLayers(bSelectedOnly);
}
}
}
_pfWorldEditingProfile.StopTimer(CWorldEditingProfile::PTI_FINDSHADOWLAYERS);
}
/* Discard shadows on all brush polygons in the world. */
void CWorld::DiscardAllShadows(void)
{
FLOATaabbox3D box;
// for each entity in the world
FOREACHINDYNAMICCONTAINER(wo_cenEntities, CEntity, iten) {
// if it is brush entity
if (iten->en_RenderType == CEntity::RT_BRUSH) {
// for each mip in its brush
FOREACHINLIST(CBrushMip, bm_lnInBrush, iten->en_pbrBrush->br_lhBrushMips, itbm) {
box|=itbm->bm_boxBoundingBox;
// for all sectors in this mip
FOREACHINDYNAMICARRAY(itbm->bm_abscSectors, CBrushSector, itbsc) {
// for each polygon in the sector
FOREACHINSTATICARRAY(itbsc->bsc_abpoPolygons, CBrushPolygon, itbpo) {
// discard its shadow map
itbpo->DiscardShadows();
}
}
}
}
}
// find all shadow layers in the world
FindShadowLayers(box);
}
/////////////////////////////////////////////////////////////////////
// Hide/Show functions
/*
* Hide entities contained in given selection.
*/
void CWorld::HideSelectedEntities(CEntitySelection &selenEntitiesToHide)
{
// for all entities in the selection
FOREACHINDYNAMICCONTAINER(selenEntitiesToHide, CEntity, iten) {
if( iten->IsSelected(ENF_SELECTED) &&
!((iten->en_RenderType==CEntity::RT_BRUSH) && (iten->en_ulFlags&ENF_ZONING)) )
{
// hide the entity
iten->en_ulFlags |= ENF_HIDDEN;
}
}
}
/*
* Hide all unselected entities.
*/
void CWorld::HideUnselectedEntities(void)
{
// for each entity in the world
FOREACHINDYNAMICCONTAINER(wo_cenEntities, CEntity, iten)
{
if( !iten->IsSelected(ENF_SELECTED) &&
!((iten->en_RenderType==CEntity::RT_BRUSH)&&(iten->en_ulFlags&ENF_ZONING)) )
{
// hide it
iten->en_ulFlags |= ENF_HIDDEN;
}
}
}
/*
* Show all entities.
*/
void CWorld::ShowAllEntities(void)
{
// for each entity in the world
FOREACHINDYNAMICCONTAINER(wo_cenEntities, CEntity, iten)
{
iten->en_ulFlags &= ~ENF_HIDDEN;
}
}
/*
* Hide sectors contained in given selection.
*/
void CWorld::HideSelectedSectors(CBrushSectorSelection &selbscSectorsToHide)
{
// for all sectors in the selection
FOREACHINDYNAMICCONTAINER(selbscSectorsToHide, CBrushSector, itbsc) {
// hide the sector
itbsc->bsc_ulFlags |= BSCF_HIDDEN;
}
}
/*
* Hide all unselected sectors.
*/
void CWorld::HideUnselectedSectors(void)
{
// for each entity in the world
FOREACHINDYNAMICCONTAINER(wo_cenEntities, CEntity, iten) {
// if it is brush entity
if (iten->en_RenderType == CEntity::RT_BRUSH) {
// for each mip in its brush
FOREACHINLIST(CBrushMip, bm_lnInBrush, iten->en_pbrBrush->br_lhBrushMips, itbm) {
// for all sectors in this mip
FOREACHINDYNAMICARRAY(itbm->bm_abscSectors, CBrushSector, itbsc) {
// if the sector is not selected
if (!itbsc->IsSelected(BSCF_SELECTED)) {
// hide it
itbsc->bsc_ulFlags |= BSCF_HIDDEN;
}
}
}
}
}
}
/*
* Show all sectors.
*/
void CWorld::ShowAllSectors(void)
{
// for each entity in the world
FOREACHINDYNAMICCONTAINER(wo_cenEntities, CEntity, iten) {
// if it is brush entity
if (iten->en_RenderType == CEntity::RT_BRUSH) {
// for each mip in its brush
FOREACHINLIST(CBrushMip, bm_lnInBrush, iten->en_pbrBrush->br_lhBrushMips, itbm) {
// for all sectors in this mip
FOREACHINDYNAMICARRAY(itbm->bm_abscSectors, CBrushSector, itbsc) {
// show the sector
itbsc->bsc_ulFlags &= ~BSCF_HIDDEN;
}
}
}
}
}
/*
* Select all polygons in selected sectors with same texture.
*/
void CWorld::SelectByTextureInSelectedSectors(
CTFileName fnTexture, CBrushPolygonSelection &selbpoSimilar, INDEX iTexture)
{
// for each entity in the world
FOREACHINDYNAMICCONTAINER(wo_cenEntities, CEntity, iten) {
// if it is brush entity
if (iten->en_RenderType == CEntity::RT_BRUSH) {
// for each mip in its brush
FOREACHINLIST(CBrushMip, bm_lnInBrush, iten->en_pbrBrush->br_lhBrushMips, itbm) {
// for all sectors in this mip
FOREACHINDYNAMICARRAY(itbm->bm_abscSectors, CBrushSector, itbsc) {
// if sector is selected
if (itbsc->IsSelected(BSCF_SELECTED)) {
// for all polygons in sector
FOREACHINSTATICARRAY(itbsc->bsc_abpoPolygons, CBrushPolygon, itbpo)
{
// if it is not portal and is not selected and has same texture
if ( (!(itbpo->bpo_ulFlags&BPOF_PORTAL) || (itbpo->bpo_ulFlags&(BPOF_TRANSLUCENT|BPOF_TRANSPARENT))) &&
!itbpo->IsSelected(BPOF_SELECTED) &&
(itbpo->bpo_abptTextures[iTexture].bpt_toTexture.GetData() != NULL) &&
(itbpo->bpo_abptTextures[iTexture].bpt_toTexture.GetData()->GetName()
== fnTexture) )
// select this polygon
selbpoSimilar.Select(*itbpo);
}
}
}
}
}
}
}
/*
* Select all polygons in world with same texture.
*/
void CWorld::SelectByTextureInWorld(
CTFileName fnTexture, CBrushPolygonSelection &selbpoSimilar, INDEX iTexture)
{
// for each entity in the world
FOREACHINDYNAMICCONTAINER(wo_cenEntities, CEntity, iten) {
// if it is brush entity
if (iten->en_RenderType == CEntity::RT_BRUSH) {
// for each mip in its brush
FOREACHINLIST(CBrushMip, bm_lnInBrush, iten->en_pbrBrush->br_lhBrushMips, itbm) {
// for all sectors in this mip
FOREACHINDYNAMICARRAY(itbm->bm_abscSectors, CBrushSector, itbsc) {
// for all polygons in sector
FOREACHINSTATICARRAY(itbsc->bsc_abpoPolygons, CBrushPolygon, itbpo)
{
// if it is not non translucent portal and is not selected and has same texture
if ( (!(itbpo->bpo_ulFlags&BPOF_PORTAL) || (itbpo->bpo_ulFlags&(BPOF_TRANSLUCENT|BPOF_TRANSPARENT))) &&
!itbpo->IsSelected(BPOF_SELECTED) &&
(itbpo->bpo_abptTextures[iTexture].bpt_toTexture.GetData() != NULL) &&
(itbpo->bpo_abptTextures[iTexture].bpt_toTexture.GetData()->GetName()
== fnTexture) )
// select this polygon
selbpoSimilar.Select(*itbpo);
}
}
}
}
}
}
/*
* Reinitialize entities from their properties. (use only in WEd!)
*/
void CWorld::ReinitializeEntities(void)
{
_pfWorldEditingProfile.StartTimer(CWorldEditingProfile::PTI_REINITIALIZEENTITIES);
// must be in 24bit mode when managing entities
CSetFPUPrecision FPUPrecision(FPT_24BIT);
CTmpPrecachingNow tpn;
// for each entity in the world
FOREACHINDYNAMICCONTAINER(wo_cenEntities, CEntity, iten) {
// reinitialize it
iten->Reinitialize();
}
_pfWorldEditingProfile.StopTimer(CWorldEditingProfile::PTI_REINITIALIZEENTITIES);
}
/* Precache data needed by entities. */
void CWorld::PrecacheEntities_t(void)
{
// for each entity in the world
INDEX ctEntities = wo_cenEntities.Count();
INDEX iEntity = 0;
FOREACHINDYNAMICCONTAINER(wo_cenEntities, CEntity, iten) {
// precache
CallProgressHook_t(FLOAT(iEntity)/ctEntities);
iten->Precache();
iEntity++;
}
}
// delete all entities that don't fit given spawn flags
void CWorld::FilterEntitiesBySpawnFlags(ULONG ulFlags)
{
// must be in 24bit mode when managing entities
CSetFPUPrecision FPUPrecision(FPT_24BIT);
BOOL bOldAllowRandom = _pNetwork->ga_sesSessionState.ses_bAllowRandom;
_pNetwork->ga_sesSessionState.ses_bAllowRandom = TRUE;
// create an empty selection of entities
CEntitySelection senToDestroy;
// for each entity in the world
{FOREACHINDYNAMICCONTAINER(wo_cenEntities, CEntity, iten) {
// if brush
if (iten->en_RenderType==CEntity::RT_BRUSH
||iten->en_RenderType==CEntity::RT_FIELDBRUSH) {
// skip it (brushes must not be deleted on the fly)
continue;
}
// if it shouldn't exist
ULONG ulEntityFlags = iten->GetSpawnFlags();
if (!(ulEntityFlags&ulFlags&SPF_MASK_DIFFICULTY)
||!(ulEntityFlags&ulFlags&SPF_MASK_GAMEMODE)) {
// add it to the selection
senToDestroy.Select(*iten);
}
}}
// destroy all selected entities
DestroyEntities(senToDestroy);
_pNetwork->ga_sesSessionState.ses_bAllowRandom = bOldAllowRandom;
}
// create links between zoning-brush sectors and non-zoning entities in sectors
void CWorld::LinkEntitiesToSectors(void)
{
_pfWorldEditingProfile.StartTimer(CWorldEditingProfile::PTI_LINKENTITIESTOSECTORS);
// must be in 24bit mode when managing entities
CSetFPUPrecision FPUPrecision(FPT_24BIT);
// for each entity in the world
FOREACHINDYNAMICCONTAINER(wo_cenEntities, CEntity, iten) {
CEntity &en = *iten;
// cache eventual collision info
en.FindCollisionInfo();
en.UpdateSpatialRange();
// link it
if (!_bEntitySectorLinksPreLoaded) {
en.FindSectorsAroundEntity();
}
}
// NOTE: this is here to force relinking for all moving zoning brushes after loading!
// for each entity in the world
{FOREACHINDYNAMICCONTAINER(wo_cenEntities, CEntity, iten) {
CEntity &en = *iten;
if (en.en_RenderType==CEntity::RT_BRUSH &&
(en.en_ulFlags&ENF_ZONING) && (en.en_ulPhysicsFlags&EPF_MOVABLE)){
// recalculate all bounding boxes relative to new position
extern BOOL _bDontDiscardLinks;
_bDontDiscardLinks = TRUE;
en.en_pbrBrush->CalculateBoundingBoxes();
_bDontDiscardLinks = FALSE;
// FPU must be in 53-bit mode
CSetFPUPrecision FPUPrecision(FPT_53BIT);
// for all brush mips
FOREACHINLIST(CBrushMip, bm_lnInBrush, en.en_pbrBrush->br_lhBrushMips, itbm) {
// for all sectors in the mip
{FOREACHINDYNAMICARRAY(itbm->bm_abscSectors, CBrushSector, itbsc) {
// find entities in sector
itbsc->FindEntitiesInSector();
}}
}
}
}}
_pfWorldEditingProfile.StopTimer(CWorldEditingProfile::PTI_LINKENTITIESTOSECTORS);
}
// rebuild all links in world
void CWorld::RebuildLinks(void)
{
wo_baBrushes.LinkPortalsAndSectors();
_bEntitySectorLinksPreLoaded = FALSE;
LinkEntitiesToSectors();
}
/* Update sectors during brush vertex moving */
void CWorld::UpdateSectorsDuringVertexChange( CBrushVertexSelection &selVertex)
{
// create container of sectors that will need to be updated
CDynamicContainer<CBrushSector> cbscToUpdate;
{FOREACHINDYNAMICCONTAINER( selVertex, CBrushVertex, itbvx)
{
// add the sector of that vertex to list for updating
if (!cbscToUpdate.IsMember(itbvx->bvx_pbscSector)) {
cbscToUpdate.Add(itbvx->bvx_pbscSector);
}
}}
// for each sector to be updated
{FOREACHINDYNAMICCONTAINER( cbscToUpdate, CBrushSector, itbsc){
// recalculate planes for polygons from their vertices
itbsc->MakePlanesFromVertices();
}}
}
/* Update sectors after brush vertex moving */
void CWorld::UpdateSectorsAfterVertexChange( CBrushVertexSelection &selVertex)
{
// create container of sectors that will need to be updated
CDynamicContainer<CBrushSector> cbscToUpdate;
{FOREACHINDYNAMICCONTAINER( selVertex, CBrushVertex, itbvx)
{
// add the sector of that vertex to list for updating
if (!cbscToUpdate.IsMember(itbvx->bvx_pbscSector)) {
cbscToUpdate.Add(itbvx->bvx_pbscSector);
}
}}
// for each sector to be updated
{FOREACHINDYNAMICCONTAINER( cbscToUpdate, CBrushSector, itbsc){
// update it
itbsc->UpdateVertexChanges();
}}
}
/* Triangularize polygons that contain vertices from given selection */
void CWorld::TriangularizeForVertices( CBrushVertexSelection &selVertex)
{
// create container of sectors that contain polygons that need to be triangularized
CDynamicContainer<CBrushSector> cbscToTriangularize;
{FOREACHINDYNAMICCONTAINER( selVertex, CBrushVertex, itbvx)
{
// add the sector of that vertex to list for triangularizing
if (!cbscToTriangularize.IsMember(itbvx->bvx_pbscSector)) {
cbscToTriangularize.Add(itbvx->bvx_pbscSector);
}
}}
// for each sector to be updated
{FOREACHINDYNAMICCONTAINER( cbscToTriangularize, CBrushSector, itbsc){
// update it
itbsc->TriangularizeForVertices(selVertex);
}}
}
// add this entity to prediction
void CEntity::AddToPrediction(void)
{
// this function may be called even for NULLs - TODO: fix those cases
// (The compiler is free to assume that "this" is never NULL and optimize
// based on that assumption. For example, an "if (this==NULL) {...}" could
// be optimized away completely.)
ASSERT(this!=NULL);
// if already added
if (en_ulFlags&ENF_WILLBEPREDICTED) {
// do nothing
return;
}
// mark as added
en_ulFlags|=ENF_WILLBEPREDICTED;
en_pwoWorld->wo_cenWillBePredicted.Add(this);
// add your dependents
AddDependentsToPrediction();
}
// mark all predictable entities that will be predicted using user-set criterions
void CWorld::MarkForPrediction(void)
{
extern INDEX cli_bPredictIfServer;
extern INDEX cli_bPredictLocalPlayers;
extern INDEX cli_bPredictRemotePlayers;
extern FLOAT cli_fPredictEntitiesRange;
static CStaticStackArray<FLOAT3D> avLocalPlayers;
avLocalPlayers.PopAll();
// for each player
for (INDEX iPlayer=0; iPlayer<CEntity::GetMaxPlayers(); iPlayer++) {
CEntity *pen = CEntity::GetPlayerEntity(iPlayer);
// if it exists
if (pen!=NULL) {
// find whether it is local
BOOL bLocal = _pNetwork->IsPlayerLocal(pen);
// if allowed for prediction
if ( (bLocal && cli_bPredictLocalPlayers)
|| (!bLocal && cli_bPredictRemotePlayers)) {
// add it
pen->AddToPrediction();
}
// if local
if (bLocal) {
// remember coordinates of the original entity, and eventual predictor coords
avLocalPlayers.Push() = pen->GetPlacement().pl_PositionVector;
avLocalPlayers.Push() = _pNetwork->ga_sesSessionState.GetPlayerPredictorPosition(iPlayer);
}
}
}
TIME tmNow = _pNetwork->ga_sesSessionState.ses_tmPredictionHeadTick;
// for each predictable entity
{FOREACHINDYNAMICCONTAINER(wo_cenPredictable, CEntity, iten){
CEntity &en = *iten;
// it must not be void (so that its coordinates are relevant)
ASSERT(en.GetRenderType()!=CEntity::RT_VOID);
// get its upper time limit for prediction
TIME tmLimit = en.GetPredictionTime();
// if now inside time prediction interval
if (tmNow<tmLimit) {
// add it to prediction
iten->AddToPrediction();
continue;
}
// if predicting entities by range
if (cli_fPredictEntitiesRange>0) {
FLOAT fRange = en.GetPredictionRange();
if (fRange<=0) {
continue;
}
fRange = Min(fRange, cli_fPredictEntitiesRange);
// get its coordinates and maximal prediction range
const FLOAT3D &v = en.GetPlacement().pl_PositionVector;
// check if it is within range of any local player
BOOL bInRange = FALSE;
for(INDEX i=0; i<avLocalPlayers.Count(); i++) {
if ((avLocalPlayers[i]-v).Length()<fRange) {
bInRange = TRUE;
break;
}
}
// if it is within the range
if (bInRange) {
// add it
iten->AddToPrediction();
}
}
}}
}
// unmark all predictable entities marked for prediction
void CWorld::UnmarkForPrediction(void)
{
// for each entity marked
{FOREACHINDYNAMICCONTAINER(wo_cenWillBePredicted, CEntity, iten){
// unmark for prediction
iten->en_ulFlags&=~ENF_WILLBEPREDICTED;
}}
wo_cenWillBePredicted.Clear();
}
// create predictors for predictable entities that are marked for prediction
void CWorld::CreatePredictors(void)
{
CDynamicContainer<CEntity> cenForPrediction;
// for each entity marked
{FOREACHINDYNAMICCONTAINER(wo_cenWillBePredicted, CEntity, iten){
// if not deleted
if (!(iten->en_ulFlags&ENF_DELETED)) {
// add to container
cenForPrediction.Add(iten);
}
// unmark
iten->en_ulFlags&=~ENF_WILLBEPREDICTED;
}}
wo_cenWillBePredicted.Clear();
// CPrintF("for prediction: %d\n", cenForPrediction.Count());
// create copies of those entities as predictors
CopyEntitiesToPredictors(cenForPrediction);
}
// delete all predictor entities
void CWorld::DeletePredictors(void)
{
// must be in 24bit mode when managing entities
CSetFPUPrecision FPUPrecision(FPT_24BIT);
// first remember eventual predicted player positions
_pNetwork->ga_sesSessionState.RememberPlayerPredictorPositions();
// make a copy of predictor container (for safe iteration)
CDynamicContainer<CEntity> cenPredictor = wo_cenPredictor;
// for each predictor
{FOREACHINDYNAMICCONTAINER( cenPredictor, CEntity, iten){
CEntity &en = *iten;
ASSERT(en.IsPredictor());
// destroy it
en.Destroy();
}}
// for each predicted
{FOREACHINDYNAMICCONTAINER( wo_cenPredicted, CEntity, iten){
CEntity &en = *iten;
ASSERT(en.IsPredicted());
// kill its pointer to predictor
en.SetPredictionPair(NULL);
// mark as not predicted
en.en_ulFlags&=~ENF_PREDICTED;
}}
ASSERT(_ctPredictorEntities==0);
wo_cenPredictor.Clear();
wo_cenPredicted.Clear();
// for each entity in the world
FOREACHINDYNAMICCONTAINER(wo_cenEntities, CEntity, iten) {
CEntity &en = *iten;
ASSERT(!en.IsPredictor());
}
}
// get entity by its ID
CEntity *CWorld::EntityFromID(ULONG ulID)
{
FOREACHINDYNAMICCONTAINER(wo_cenAllEntities, CEntity, iten) {
if (iten->en_ulID==ulID) {
return iten;
}
}
ASSERT(FALSE);
return NULL;
}
/* Triangularize selected polygons. */
void CWorld::TriangularizePolygons(CDynamicContainer<CBrushPolygon> &dcPolygons)
{
ClearMarkedForUseFlag();
CDynamicContainer<CBrushSector> cbscToProcess;
// for each polyon in selection
FOREACHINDYNAMICCONTAINER(dcPolygons, CBrushPolygon, itbpo)
{
CBrushPolygon &bp=*itbpo;
bp.bpo_ulFlags |= BPOF_MARKED_FOR_USE;
CBrushSector *pbsc=bp.bpo_pbscSector;
if( !cbscToProcess.IsMember( pbsc))
{
cbscToProcess.Add( pbsc);
}
}
FOREACHINDYNAMICCONTAINER(cbscToProcess, CBrushSector, itbsc)
{
itbsc->TriangularizeMarkedPolygons();
itbsc->UpdateVertexChanges();
}
}
// Clear marked for use flag on all polygons in world
void CWorld::ClearMarkedForUseFlag(void)
{
// for each entity in the world
FOREACHINDYNAMICCONTAINER(wo_cenEntities, CEntity, iten) {
// if it is brush entity
if (iten->en_RenderType == CEntity::RT_BRUSH) {
// for each mip in its brush
FOREACHINLIST(CBrushMip, bm_lnInBrush, iten->en_pbrBrush->br_lhBrushMips, itbm) {
// for all sectors in this mip
FOREACHINDYNAMICARRAY(itbm->bm_abscSectors, CBrushSector, itbsc) {
// for each polygon in the sector
FOREACHINSTATICARRAY(itbsc->bsc_abpoPolygons, CBrushPolygon, itbpo) {
// discard marked for use flag
itbpo->bpo_ulFlags &= ~BPOF_MARKED_FOR_USE;
}
}
}
}
}
}