Serious-Engine/Sources/Engine/Entities/EntityCopying.cpp
2016-03-11 18:20:51 -06:00

748 lines
23 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 "stdh.h"
#include <Engine/Entities/Entity.h>
#include <Engine/Entities/LastPositions.h>
#include <Engine/Entities/EntityProperties.h>
#include <Engine/Entities/EntityClass.h>
#include <Engine/Base/Translation.h>
#include <Engine/Base/Timer.h>
#include <Engine/Base/Console.h>
#include <Engine/Base/ListIterator.inl>
#include <Engine/Base/ErrorReporting.h>
#include <Engine/World/World.h>
#include <Engine/Math/Float.h>
#include <Engine/Math/Quaternion.h>
#include <Engine/Brushes/BrushArchive.h>
#include <Engine/Light/LightSource.h>
#include <Engine/Entities/ShadingInfo.h>
#include <Engine/Models/ModelObject.h>
#include <Engine/Sound/SoundObject.h>
#include <Engine/Templates/DynamicContainer.cpp>
#include <Engine/Templates/StaticArray.cpp>
#include <Engine/Templates/Selection.cpp>
class CPointerRemapping {
public:
CEntity *pr_penOriginal;
CEntity *pr_penCopy;
inline void Clear(void) {};
};
static CStaticArray<CPointerRemapping> _aprRemaps;
static BOOL _bRemapPointersToNULLs = TRUE;
extern BOOL _bReinitEntitiesWhileCopying = TRUE;
static BOOL _bMirrorAndStretch = FALSE;
static FLOAT _fStretch = 1.0f;
static enum WorldMirrorType _wmtMirror = WMT_NONE;
extern INDEX _ctPredictorEntities;
// mirror a placement of one entity
static void MirrorAndStretchPlacement(CPlacement3D &pl)
{
ASSERT(_wmtMirror==WMT_X||_wmtMirror==WMT_Y||_wmtMirror==WMT_Z||_wmtMirror==WMT_NONE);
// if there should be mirror
if (_wmtMirror!=WMT_NONE) {
// make rotation matrix for the placement
FLOATmatrix3D m;
MakeRotationMatrix(m, pl.pl_OrientationAngle);
// get row vectors, with object x flipped
FLOAT3D vX(-m(1,1),m(1,2),m(1,3));
FLOAT3D vY(-m(2,1),m(2,2),m(2,3));
FLOAT3D vZ(-m(3,1),m(3,2),m(3,3));
// flip needed axis
switch(_wmtMirror) {
case WMT_X:
pl.pl_PositionVector(1) = -pl.pl_PositionVector(1);
vX = -vX;
break;
case WMT_Y:
pl.pl_PositionVector(2) = -pl.pl_PositionVector(2);
vY = -vY;
break;
case WMT_Z:
pl.pl_PositionVector(3) = -pl.pl_PositionVector(3);
vZ = -vZ;
break;
default: ASSERT(FALSE);
}
// compose matrix back from the vectors
m(1,1) = vX(1); m(2,1) = vY(1); m(3,1) = vZ(1);
m(1,2) = vX(2); m(2,2) = vY(2); m(3,2) = vZ(2);
m(1,3) = vX(3); m(2,3) = vY(3); m(3,3) = vZ(3);
// decompose matrix into angles
DecomposeRotationMatrix(pl.pl_OrientationAngle, m);
}
pl.pl_PositionVector*=_fStretch;
}
CEntity *CEntity::FindRemappedEntityPointer(CEntity *penOriginal)
{
// if original is null
if (penOriginal==NULL) {
// copy is null
return NULL;
}
// try to find valid remap
{FOREACHINSTATICARRAY(_aprRemaps, CPointerRemapping, itpr) {
if (itpr->pr_penOriginal==penOriginal) {
return itpr->pr_penCopy;
}
}}
// if none found, copy is either null or original
return _bRemapPointersToNULLs?NULL:penOriginal;
}
/*
* Copy entity from another entity of same class.
* NOTES:
* - Doesn't copy placement, it must be done on creation.
* - Entity must be initialized afterwards.
*/
void CEntity::Copy(CEntity &enOther, ULONG ulFlags)
{
BOOL bRemapPointers = ulFlags & COPY_REMAP;
BOOL bMakePredictor = ulFlags & COPY_PREDICTOR;
// copy base class data
en_RenderType = enOther.en_RenderType;
en_ulPhysicsFlags = enOther.en_ulPhysicsFlags;
en_ulCollisionFlags = enOther.en_ulCollisionFlags;
en_ulFlags = enOther.en_ulFlags &
~(ENF_SELECTED|ENF_FOUNDINGRIDSEARCH|ENF_VALIDSHADINGINFO|ENF_INRENDERING);
en_ulSpawnFlags = enOther.en_ulSpawnFlags;
// if prediction
if (bMakePredictor) {
// set flags
en_ulFlags = (en_ulFlags&~(ENF_PREDICTED|ENF_PREDICTABLE))|ENF_PREDICTOR;
enOther.en_ulFlags = (enOther.en_ulFlags&~ENF_PREDICTOR)|ENF_PREDICTED;
} else {
en_ulFlags = (en_ulFlags&~(ENF_PREDICTED|ENF_PREDICTABLE|ENF_PREDICTOR));
}
// if this is a brush
if ( enOther.en_RenderType == RT_BRUSH || en_RenderType == RT_FIELDBRUSH) {
// there must be no existing brush
ASSERT(en_pbrBrush == NULL);
// create a new empty brush in the brush archive of current world
en_pbrBrush = en_pwoWorld->wo_baBrushes.ba_abrBrushes.New();
en_pbrBrush->br_penEntity = this;
// copy the brush
if (_bMirrorAndStretch) {
en_pbrBrush->Copy(*enOther.en_pbrBrush, _fStretch, _wmtMirror!=WMT_NONE);
} else {
en_pbrBrush->Copy(*enOther.en_pbrBrush, 1.0f, FALSE);
}
// if this is a terrain
} else if( enOther.en_RenderType == RT_TERRAIN) {
#pragma message(">> CEntity::Copy")
ASSERT(FALSE);
// if this is a model
} if ( enOther.en_RenderType == RT_MODEL || en_RenderType == RT_EDITORMODEL) {
// if will not initialize
if (!(ulFlags&COPY_REINIT)) {
// create a new model object
en_pmoModelObject = new CModelObject;
en_psiShadingInfo = new CShadingInfo;
en_ulFlags &= ~ENF_VALIDSHADINGINFO;
// copy it
en_pmoModelObject->Copy(*enOther.en_pmoModelObject);
}
// if this is ska model
} else if ( enOther.en_RenderType == RT_SKAMODEL || en_RenderType == RT_SKAEDITORMODEL) {
en_psiShadingInfo = new CShadingInfo;
en_ulFlags &= ~ENF_VALIDSHADINGINFO;
en_pmiModelInstance = CreateModelInstance("Temp");
// copy it
GetModelInstance()->Copy(*enOther.GetModelInstance());
}
// copy the parent pointer
if (bRemapPointers) {
en_penParent = FindRemappedEntityPointer(enOther.en_penParent);
} else {
en_penParent = enOther.en_penParent;
}
// if the entity has a parent
if (en_penParent!=NULL) {
// create relative offset
en_plRelativeToParent = en_plPlacement;
en_plRelativeToParent.AbsoluteToRelativeSmooth(en_penParent->en_plPlacement);
// link to parent
en_penParent->en_lhChildren.AddTail(en_lnInParent);
}
// copy the derived class properties
CopyEntityProperties(enOther, ulFlags);
// if prediction
if (bMakePredictor) {
// create cross-links and add to containers
SetPredictionPair(&enOther);
enOther.SetPredictionPair(this);
enOther.en_pwoWorld->wo_cenPredicted.Add(&enOther);
enOther.en_pwoWorld->wo_cenPredictor.Add(this);
// copy last positions
if (enOther.en_plpLastPositions!=NULL) {
en_plpLastPositions = new CLastPositions(*enOther.en_plpLastPositions);
}
}
}
/*
* Copy one entity property from property of another entity.
*/
void CEntity::CopyOneProperty( CEntityProperty &epPropertySrc, CEntityProperty &epPropertyDest,
CEntity &enOther, ULONG ulFlags)
{
// a helper macro
#define COPYPROPERTY(type) \
(type &)ENTITYPROPERTY(this, epPropertyDest.ep_slOffset, type) \
= (type &)ENTITYPROPERTY(&enOther, epPropertySrc.ep_slOffset, type)
// depending on the property type
switch (epPropertySrc.ep_eptType) {
// if it is BOOL
case CEntityProperty::EPT_BOOL:
// copy BOOL
COPYPROPERTY(INDEX);
break;
// if it is INDEX
case CEntityProperty::EPT_INDEX:
case CEntityProperty::EPT_ENUM:
case CEntityProperty::EPT_FLAGS:
case CEntityProperty::EPT_ANIMATION:
case CEntityProperty::EPT_ILLUMINATIONTYPE:
case CEntityProperty::EPT_COLOR:
case CEntityProperty::EPT_ANGLE:
// copy INDEX
COPYPROPERTY(INDEX);
break;
// if it is FLOAT
case CEntityProperty::EPT_FLOAT:
case CEntityProperty::EPT_RANGE:
// copy FLOAT
COPYPROPERTY(FLOAT);
break;
// if it is STRING
case CEntityProperty::EPT_STRING:
case CEntityProperty::EPT_STRINGTRANS:
// copy STRING
COPYPROPERTY(CTString);
break;
// if it is FILENAME
case CEntityProperty::EPT_FILENAME:
// copy FILENAME
COPYPROPERTY(CTFileName);
break;
// if it is FILENAMENODEP
case CEntityProperty::EPT_FILENAMENODEP:
// copy FILENAMENODEP
COPYPROPERTY(CTFileNameNoDep);
break;
// if it is FLOATAABBOX3D
case CEntityProperty::EPT_FLOATAABBOX3D:
// copy FLOATAABBOX3D
COPYPROPERTY(FLOATaabbox3D);
break;
// if it is FLOATMATRIX3D
case CEntityProperty::EPT_FLOATMATRIX3D:
// copy FLOATMATRIX3D
COPYPROPERTY(FLOATmatrix3D);
break;
// if it is FLOAT3D
case CEntityProperty::EPT_FLOAT3D:
// copy FLOAT3D
COPYPROPERTY(FLOAT3D);
break;
// if it is ANGLE3D
case CEntityProperty::EPT_ANGLE3D:
// copy ANGLE3D
COPYPROPERTY(ANGLE3D);
break;
// if it is QUATERNION3D
case CEntityProperty::EPT_FLOATQUAT3D:
// copy ANGLE3D
COPYPROPERTY(FLOATquat3D);
break;
// if it is FLOATplane3D
case CEntityProperty::EPT_FLOATplane3D:
// copy FLOATplane3D
COPYPROPERTY(FLOATplane3D);
break;
// if it is ENTITYPTR
case CEntityProperty::EPT_ENTITYPTR:
// remap and copy the pointer
if (ulFlags & COPY_REMAP) {
ENTITYPROPERTY(this, epPropertyDest.ep_slOffset, CEntityPointer) =
FindRemappedEntityPointer(ENTITYPROPERTY(&enOther, epPropertySrc.ep_slOffset, CEntityPointer));
// copy CEntityPointer
} else {
COPYPROPERTY(CEntityPointer);
}
break;
// if it is MODELOBJECT
case CEntityProperty::EPT_MODELOBJECT:
// copy CModelObject
ENTITYPROPERTY(this, epPropertyDest.ep_slOffset, CModelObject).
Copy(ENTITYPROPERTY(&enOther, epPropertySrc.ep_slOffset, CModelObject));
// model objects are not copied, but should be initialized in Main()
break;
// if it is MODELINSTANCE
case CEntityProperty::EPT_MODELINSTANCE:
// copy CModelInstance
ENTITYPROPERTY(this, epPropertyDest.ep_slOffset, CModelInstance).
Copy(ENTITYPROPERTY(&enOther, epPropertySrc.ep_slOffset, CModelInstance));
// model objects are not copied, but should be initialized in Main()
break;
// if it is ANIMOBJECT
case CEntityProperty::EPT_ANIMOBJECT:
// copy CAnimObject
ENTITYPROPERTY(this, epPropertyDest.ep_slOffset, CAnimObject).
Copy(ENTITYPROPERTY(&enOther, epPropertySrc.ep_slOffset, CAnimObject));
break;
// if it is SOUNDOBJECT
case CEntityProperty::EPT_SOUNDOBJECT:
{
if (!(ulFlags & COPY_PREDICTOR)) {
// copy CSoundObject
CSoundObject &so = ENTITYPROPERTY(this, epPropertyDest.ep_slOffset, CSoundObject);
so.Copy(ENTITYPROPERTY(&enOther, epPropertySrc.ep_slOffset, CSoundObject));
so.so_penEntity = this;
}
}
break;
// if it is CPlacement3D
case CEntityProperty::EPT_PLACEMENT3D:
// copy CPlacement3D
COPYPROPERTY(CPlacement3D);
break;
default:
ASSERTALWAYS("Unknown property type");
}
}
/*
* Copy entity properties from another entity of same class.
*/
void CEntity::CopyEntityProperties(CEntity &enOther, ULONG ulFlags)
{
// other entity must have same class
ASSERT(enOther.en_pecClass == en_pecClass);
// for all classes in hierarchy of this entity
for(CDLLEntityClass *pdecDLLClass = en_pecClass->ec_pdecDLLClass;
pdecDLLClass!=NULL;
pdecDLLClass = pdecDLLClass->dec_pdecBase) {
// for all properties
for(INDEX iProperty=0; iProperty<pdecDLLClass->dec_ctProperties; iProperty++) {
CEntityProperty &epProperty = pdecDLLClass->dec_aepProperties[iProperty];
CopyOneProperty( epProperty, epProperty, enOther, ulFlags);
}
}
}
/* Copy container of entities from another world to this one and select them. */
void CWorld::CopyEntities(CWorld &woOther, CDynamicContainer<CEntity> &cenToCopy,
CEntitySelection &senCopied, const CPlacement3D &plOtherSystem)
{
INDEX ctEntities = cenToCopy.Count();
if (ctEntities<=0) {
return;
}
CSetFPUPrecision FPUPrecision(FPT_24BIT);
ULONG ulCopyFlags = COPY_REMAP;
if(_bReinitEntitiesWhileCopying) {
ulCopyFlags|=COPY_REINIT;
};
// create array of pointer remaps
_aprRemaps.Clear();
_aprRemaps.New(ctEntities);
// PASS 1: create entities
// for each entity to copy
INDEX iRemap = 0;
{FOREACHINDYNAMICCONTAINER(cenToCopy, CEntity, itenToCopy) {
CEntity &enToCopy = *itenToCopy;
CEntity *penNew;
CPlacement3D plEntity;
// thansform the entity placement from the system of other world
plEntity = enToCopy.en_plPlacement;
plEntity.RelativeToAbsolute(plOtherSystem);
// mirror and stretch placement if needed
if (_bMirrorAndStretch) {
MirrorAndStretchPlacement(plEntity);
}
/*
* NOTE: We must use CreateEntity_t() overload with class name instead with class pointer
* because the entity class must be obtained by the target world too!
*/
// try to
try {
// create an entity of same class as the one to copy
penNew = CreateEntity_t(plEntity, enToCopy.en_pecClass->GetName());
// if not successfull
} catch (char *strError) {
(void)strError;
ASSERT(FALSE); // this should not happen
FatalError(TRANS("Cannot CopyEntity():\n%s"), strError);
}
// remember its remap pointer
_aprRemaps[iRemap].pr_penOriginal = &enToCopy;
_aprRemaps[iRemap].pr_penCopy = penNew;
iRemap++;
}}
// PASS 2: copy properties
// for each of the created entities
{FOREACHINSTATICARRAY(_aprRemaps, CPointerRemapping, itpr) {
CEntity *penOriginal = itpr->pr_penOriginal;
CEntity *penCopy = itpr->pr_penCopy;
// copy the entity from its original
penCopy->Copy(*penOriginal, ulCopyFlags);
// if this is a brush
if ( penOriginal->en_RenderType == CEntity::RT_BRUSH ||
penOriginal->en_RenderType == CEntity::RT_FIELDBRUSH) {
// update the bounding boxes of the brush
penCopy->en_pbrBrush->CalculateBoundingBoxes();
}
if (_bMirrorAndStretch) {
penCopy->MirrorAndStretch(_fStretch, _wmtMirror!=WMT_NONE);
}
}}
// PASS 3: initialize
// for each of the created entities
{FOREACHINSTATICARRAY(_aprRemaps, CPointerRemapping, itpr) {
CEntity *penOriginal = itpr->pr_penOriginal;
CEntity *penCopy = itpr->pr_penCopy;
if (_bReinitEntitiesWhileCopying) {
// init the new copy
penCopy->Initialize();
} else {
penCopy->UpdateSpatialRange();
penCopy->FindCollisionInfo();
// set spatial clasification
penCopy->FindSectorsAroundEntity();
}
}}
// PASS 4: find shadows
// for each of the created entities
{FOREACHINSTATICARRAY(_aprRemaps, CPointerRemapping, itpr) {
CEntity *penOriginal = itpr->pr_penOriginal;
CEntity *penCopy = itpr->pr_penCopy;
// if this is a brush
if ( penCopy->en_RenderType == CEntity::RT_BRUSH ||
penCopy->en_RenderType == CEntity::RT_FIELDBRUSH) {
// find possible shadow layers near affected area
FindShadowLayers(penCopy->en_pbrBrush->GetFirstMip()->bm_boxBoundingBox);
}
// if this is a light source
{CLightSource *pls = penCopy->GetLightSource();
if (pls!=NULL) {
// find all shadow maps that should have layers from this light source
pls->FindShadowLayers(FALSE);
// update shadow map on terrains
pls->UpdateTerrains();
}}
// select it
senCopied.Select(*penCopy);
}}
// make sure someone doesn't reuse the remap array accidentially
_aprRemaps.Clear();
}
/* Copy one entity from another world into this one. */
CEntity *CWorld::CopyOneEntity(CEntity &enToCopy, const CPlacement3D &plOtherSystem)
{
// prepare container for copying
CDynamicContainer<CEntity> cenToCopy;
cenToCopy.Add(&enToCopy);
// copy the entities in container
CEntitySelection senCopied;
CopyEntities(*enToCopy.en_pwoWorld, cenToCopy, senCopied, plOtherSystem);
{FOREACHINDYNAMICCONTAINER(senCopied, CEntity, itenCopied) {
return itenCopied;
}}
ASSERT(FALSE);
return NULL;
}
/*
* Copy all entities except one from another world to this one.
*/
void CWorld::CopyAllEntitiesExceptOne(CWorld &woOther, CEntity &enExcepted,
const CPlacement3D &plOtherSystem)
{
// prepare container for copying, without excepted entity
CDynamicContainer<CEntity> cenToCopy;
cenToCopy = woOther.wo_cenEntities;
cenToCopy.Remove(&enExcepted);
// copy the entities in container and ignore the selection (we don't need it)
CEntitySelection senCopied;
CopyEntities(woOther, cenToCopy, senCopied, plOtherSystem);
senCopied.Clear();
}
/* Copy entity in world. */
CEntity *CWorld::CopyEntityInWorld(CEntity &enOriginal, const CPlacement3D &plOtherEntity,
BOOL bWithDescendants /*= TRUE*/)
{
// new entity
CEntity *penNew;
/*
* NOTE: We must use CreateEntity_t() overload with class name instead with class pointer
* because the entity class must be obtained by the target world too!
*/
// try to
try {
// create an entity of same class as the one to copy
penNew = CreateEntity_t(plOtherEntity, enOriginal.en_pecClass->GetName());
// if not successfull
} catch (char *strError) {
(void)strError;
ASSERT(FALSE); // this should not happen
FatalError(TRANS("Cannot CopyEntity():\n%s"), strError);
}
// copy the entity from its original
penNew->Copy(enOriginal, COPY_REINIT);
// if this is a brush
if ( enOriginal.en_RenderType == CEntity::RT_BRUSH ||
enOriginal.en_RenderType == CEntity::RT_FIELDBRUSH) {
// update the bounding boxes of the brush
penNew->en_pbrBrush->CalculateBoundingBoxes();
}
// init the new copy
penNew->Initialize();
// if this is a brush
if ( penNew->en_RenderType == CEntity::RT_BRUSH ||
penNew->en_RenderType == CEntity::RT_FIELDBRUSH) {
// find possible shadow layers near affected area
FindShadowLayers(penNew->en_pbrBrush->GetFirstMip()->bm_boxBoundingBox);
}
// if this is a light source
{CLightSource *pls = penNew->GetLightSource();
if (pls!=NULL) {
// find all shadow maps that should have layers from this light source
pls->FindShadowLayers(FALSE);
}}
// if descendants should be copied too
if (bWithDescendants) {
// for each child of this entity
{FOREACHINLIST(CEntity, en_lnInParent, enOriginal.en_lhChildren, itenChild) {
// copy it relatively to the new entity
CPlacement3D plChild = itenChild->en_plRelativeToParent;
plChild.RelativeToAbsoluteSmooth(penNew->en_plPlacement);
CEntity *penNewChild = CopyEntityInWorld(*itenChild, plChild, TRUE);
// add new child to its new parent
penNewChild->SetParent(penNew);
}}
}
return penNew;
}
// mirror and stretch another world into this one
void CWorld::MirrorAndStretch(CWorld &woOriginal, FLOAT fStretch, enum WorldMirrorType wmt)
{
_bMirrorAndStretch = TRUE;
_fStretch = fStretch;
_wmtMirror = wmt;
// clear this world
Clear();
// make container for copying
CDynamicContainer<CEntity> cenToCopy;
cenToCopy = woOriginal.wo_cenEntities;
// dummy selection for copied entities, we don't need that info
CEntitySelection senCopied;
// make mirroring placement
CPlacement3D plOtherSystem;
plOtherSystem = CPlacement3D(FLOAT3D(0,0,0), ANGLE3D(0,0,0));
// copy entities with mirror and stretch
CopyEntities(woOriginal, cenToCopy, senCopied, plOtherSystem);
// update all links
{
CSetFPUPrecision FPUPrecision(FPT_53BIT);
wo_baBrushes.LinkPortalsAndSectors();
}
_bMirrorAndStretch = FALSE;
}
/* Copy entities for prediction. */
void CWorld::CopyEntitiesToPredictors(CDynamicContainer<CEntity> &cenToCopy)
{
INDEX ctEntities = cenToCopy.Count();
if (ctEntities<=0) {
return;
}
extern INDEX cli_bReportPredicted;
if (cli_bReportPredicted) {
CPrintF( TRANS("Predicting %d entities:\n"), ctEntities);
{FOREACHINDYNAMICCONTAINER(cenToCopy, CEntity, itenToCopy) {
CEntity &enToCopy = *itenToCopy;
CPrintF(" %s:%s\n", enToCopy.GetClass()->ec_pdecDLLClass->dec_strName, (const char*)enToCopy.GetName());
}}
}
// clear current tick to prevent timer setting from assertions
TIME tmCurrentTickOld = _pTimer->CurrentTick();
_pTimer->SetCurrentTick(0.0f);
ULONG ulCopyFlags = COPY_REMAP|COPY_PREDICTOR;
// create array of pointer remaps
_aprRemaps.Clear();
_aprRemaps.New(ctEntities);
// PASS 1: create entities
// for each entity to copy
INDEX iRemap = 0;
{FOREACHINDYNAMICCONTAINER(cenToCopy, CEntity, itenToCopy) {
CEntity &enToCopy = *itenToCopy;
CEntity *penNew;
// create an entity of same class as the one to copy
penNew = CreateEntity(enToCopy.en_plPlacement, enToCopy.en_pecClass);
// remember its remap pointer
_aprRemaps[iRemap].pr_penOriginal = &enToCopy;
_aprRemaps[iRemap].pr_penCopy = penNew;
iRemap++;
_ctPredictorEntities++;
}}
// unfound pointers must be kept unremapped
_bRemapPointersToNULLs = FALSE;
// PASS 2: copy properties
// for each of the created entities
{FOREACHINSTATICARRAY(_aprRemaps, CPointerRemapping, itpr) {
CEntity *penOriginal = itpr->pr_penOriginal;
CEntity *penCopy = itpr->pr_penCopy;
// copy the entity from its original
penCopy->Copy(*penOriginal, ulCopyFlags);
// if this is a brush
if ( penOriginal->en_RenderType == CEntity::RT_BRUSH ||
penOriginal->en_RenderType == CEntity::RT_FIELDBRUSH) {
ASSERT(FALSE); // should we allow prediction of brushes?
// update the bounding boxes of the brush
//penCopy->en_pbrBrush->CalculateBoundingBoxes();
}
}}
// PASS 3: initialize
// for each of the created entities
{FOREACHINSTATICARRAY(_aprRemaps, CPointerRemapping, itpr) {
CEntity *penOriginal = itpr->pr_penOriginal;
CEntity *penCopy = itpr->pr_penCopy;
// copy spatial classification
penCopy->en_fSpatialClassificationRadius = penOriginal->en_fSpatialClassificationRadius;
penCopy->en_boxSpatialClassification = penOriginal->en_boxSpatialClassification;
// copy collision info
penCopy->CopyCollisionInfo(*penOriginal);
// for each sector around the original
{FOREACHSRCOFDST(penOriginal->en_rdSectors, CBrushSector, bsc_rsEntities, pbsc)
// copy the link
if (penOriginal->en_RenderType==CEntity::RT_BRUSH
||penOriginal->en_RenderType==CEntity::RT_FIELDBRUSH
||penOriginal->en_RenderType==CEntity::RT_TERRAIN) { // brushes first
AddRelationPairHeadHead(pbsc->bsc_rsEntities, penCopy->en_rdSectors);
} else {
AddRelationPairTailTail(pbsc->bsc_rsEntities, penCopy->en_rdSectors);
}
ENDFOR}
}}
// PASS 4: find shadows
// for each of the created entities
{FOREACHINSTATICARRAY(_aprRemaps, CPointerRemapping, itpr) {
CEntity *penOriginal = itpr->pr_penOriginal;
CEntity *penCopy = itpr->pr_penCopy;
// if this is a brush
if ( penCopy->en_RenderType == CEntity::RT_BRUSH ||
penCopy->en_RenderType == CEntity::RT_FIELDBRUSH) {
ASSERT(FALSE); // should we allow prediction of brushes?
// find possible shadow layers near affected area
//FindShadowLayers(penCopy->en_pbrBrush->GetFirstMip()->bm_boxBoundingBox);
}
// if this is a light source
{CLightSource *pls = penCopy->GetLightSource();
if (pls!=NULL) {
// find all shadow maps that should have layers from this light source
pls->FindShadowLayers(FALSE);
}}
}}
// make sure someone doesn't reuse the remap array accidentially
_aprRemaps.Clear();
_bRemapPointersToNULLs = TRUE;
// return current tick
_pTimer->SetCurrentTick(tmCurrentTickOld);
}