#include "common.h"
#include "General.h"
#include "Building.h"
#include "Treadable.h"
#include "Train.h"
#include "Pools.h"
#include "Timer.h"
#include "Camera.h"
#include "World.h"
#include "FileMgr.h"
#include "ZoneCull.h"
#include "Zones.h"
#include "Debug.h"
#include "Renderer.h"
int32 CCullZones::NumCullZones;
CCullZone CCullZones::aZones[NUMCULLZONES];
int32 CCullZones::NumAttributeZones;
CAttributeZone CCullZones::aAttributeZones[NUMATTRIBZONES];
uint16 CCullZones::aIndices[NUMZONEINDICES];
int16 CCullZones::aPointersToBigBuildingsForBuildings[NUMBUILDINGS];
int16 CCullZones::aPointersToBigBuildingsForTreadables[NUMTREADABLES];
int32 CCullZones::CurrentWantedLevelDrop_Player;
int32 CCullZones::CurrentFlags_Camera;
int32 CCullZones::CurrentFlags_Player;
int32 CCullZones::OldCullZone;
int32 CCullZones::EntityIndicesUsed;
bool CCullZones::bCurrentSubwayIsInvisible;
bool CCullZones::bCullZonesDisabled;
#define NUMUNCOMPRESSED (6000)
#define NUMTEMPINDICES (140000)
void
CCullZones::Init(void)
{
int i;
NumAttributeZones = 0;
CurrentWantedLevelDrop_Player = 0;
CurrentFlags_Camera = 0;
CurrentFlags_Player = 0;
bCurrentSubwayIsInvisible = false;
NumCullZones = 0;
OldCullZone = -1;
EntityIndicesUsed = 0;
for(i = 0; i < NUMBUILDINGS; i++)
aPointersToBigBuildingsForBuildings[i] = -1;
for(i = 0; i < NUMTREADABLES; i++)
aPointersToBigBuildingsForTreadables[i] = -1;
}
uint16* pTempArrayIndices;
int TempEntityIndicesUsed;
void
CCullZones::ResolveVisibilities(void)
{
int fd;
CFileMgr::SetDir("");
fd = CFileMgr::OpenFile("DATA\\cullzone.dat", "rb");
if(fd > 0){
CFileMgr::Read(fd, (char*)&NumCullZones, sizeof(NumCullZones));
CFileMgr::Read(fd, (char*)aZones, sizeof(aZones));
CFileMgr::Read(fd, (char*)&NumAttributeZones, sizeof(NumAttributeZones));
CFileMgr::Read(fd, (char*)aAttributeZones, sizeof(aAttributeZones));
CFileMgr::Read(fd, (char*)aIndices, sizeof(aIndices));
CFileMgr::Read(fd, (char*)aPointersToBigBuildingsForBuildings, sizeof(aPointersToBigBuildingsForBuildings));
CFileMgr::Read(fd, (char*)aPointersToBigBuildingsForTreadables, sizeof(aPointersToBigBuildingsForTreadables));
CFileMgr::CloseFile(fd);
}else{
#ifndef MASTER
EntityIndicesUsed = 0;
BuildListForBigBuildings();
pTempArrayIndices = new uint16[NUMTEMPINDICES];
TempEntityIndicesUsed = 0;
// if(!LoadTempFile()) // not in final game
{
for (int i = 0; i < NumCullZones; i++) {
//printf("testing zone %d (%d indices)\n", i, TempEntityIndicesUsed);
DoVisibilityTestCullZone(i, true);
}
// SaveTempFile(); // not in final game
}
CompressIndicesArray();
delete[] pTempArrayIndices;
pTempArrayIndices = nil;
fd = CFileMgr::OpenFileForWriting("data\\cullzone.dat");
if (fd != 0) {
CFileMgr::Write(fd, (char*)&NumCullZones, sizeof(NumCullZones));
CFileMgr::Write(fd, (char*)aZones, sizeof(aZones));
CFileMgr::Write(fd, (char*)&NumAttributeZones, sizeof(NumAttributeZones));
CFileMgr::Write(fd, (char*)&aAttributeZones, sizeof(aAttributeZones));
CFileMgr::Write(fd, (char*)&aIndices, sizeof(aIndices));
CFileMgr::Write(fd, (char*)&aPointersToBigBuildingsForBuildings, sizeof(aPointersToBigBuildingsForBuildings));
CFileMgr::Write(fd, (char*)&aPointersToBigBuildingsForTreadables, sizeof(aPointersToBigBuildingsForTreadables));
CFileMgr::CloseFile(fd);
}
#endif
}
}
bool
CCullZones::LoadTempFile(void)
{
int fd = CFileMgr::OpenFile("cullzone.tmp");
if (fd != 0) {
CFileMgr::Read(fd, (char*)&NumCullZones, sizeof(NumCullZones));
CFileMgr::Read(fd, (char*)aZones, sizeof(aZones));
CFileMgr::Read(fd, (char*)&NumAttributeZones, sizeof(NumAttributeZones));
CFileMgr::Read(fd, (char*)&aAttributeZones, sizeof(aAttributeZones));
CFileMgr::Read(fd, (char*)pTempArrayIndices, NUMTEMPINDICES*sizeof(uint16));
CFileMgr::Read(fd, (char*)&TempEntityIndicesUsed, sizeof(TempEntityIndicesUsed));
CFileMgr::Read(fd, (char*)&aPointersToBigBuildingsForBuildings, sizeof(aPointersToBigBuildingsForBuildings));
CFileMgr::Read(fd, (char*)&aPointersToBigBuildingsForTreadables, sizeof(aPointersToBigBuildingsForTreadables));
CFileMgr::CloseFile(fd);
return true;
}
return false;
}
void
CCullZones::SaveTempFile(void)
{
int fd = CFileMgr::OpenFileForWriting("cullzone.tmp");
if (fd != 0) {
CFileMgr::Write(fd, (char*)&NumCullZones, sizeof(NumCullZones));
CFileMgr::Write(fd, (char*)aZones, sizeof(aZones));
CFileMgr::Write(fd, (char*)&NumAttributeZones, sizeof(NumAttributeZones));
CFileMgr::Write(fd, (char*)&aAttributeZones, sizeof(aAttributeZones));
CFileMgr::Write(fd, (char*)pTempArrayIndices, NUMTEMPINDICES*sizeof(uint16));
CFileMgr::Write(fd, (char*)&TempEntityIndicesUsed, sizeof(TempEntityIndicesUsed));
CFileMgr::Write(fd, (char*)&aPointersToBigBuildingsForBuildings, sizeof(aPointersToBigBuildingsForBuildings));
CFileMgr::Write(fd, (char*)&aPointersToBigBuildingsForTreadables, sizeof(aPointersToBigBuildingsForTreadables));
CFileMgr::CloseFile(fd);
}
}
void
CCullZones::BuildListForBigBuildings()
{
for (int i = CPools::GetBuildingPool()->GetSize()-1; i >= 0; i--) {
CBuilding *building = CPools::GetBuildingPool()->GetSlot(i);
if (building == nil || !building->bIsBIGBuilding) continue;
CSimpleModelInfo *nonlod = ((CSimpleModelInfo *)CModelInfo::GetModelInfo(building->GetModelIndex()))->GetRelatedModel();
if (nonlod == nil) continue;
for (int j = CPools::GetBuildingPool()->GetSize()-1; j >= 0; j--) {
CBuilding *building2 = CPools::GetBuildingPool()->GetSlot(j);
if (building2 == nil || building2->bIsBIGBuilding) continue;
if (CModelInfo::GetModelInfo(building2->GetModelIndex()) == nonlod) {
if ((building2->GetPosition() - building->GetPosition()).Magnitude() < 5.0f) {
aPointersToBigBuildingsForBuildings[j] = i;
}
}
}
for (int j = CPools::GetTreadablePool()->GetSize()-1; j >= 0; j--) {
CTreadable *treadable = CPools::GetTreadablePool()->GetSlot(j);
if (treadable == nil || treadable->bIsBIGBuilding) continue;
if (CModelInfo::GetModelInfo(treadable->GetModelIndex()) == nonlod) {
if ((treadable->GetPosition() - building->GetPosition()).Magnitude() < 5.0f) {
aPointersToBigBuildingsForTreadables[j] = i;
}
}
}
}
}
void
CCullZones::DoVisibilityTestCullZone(int zoneId, bool findIndices)
{
aZones[zoneId].m_groupIndexCount[0] = 0;
aZones[zoneId].m_groupIndexCount[1] = 0;
aZones[zoneId].m_groupIndexCount[2] = 0;
aZones[zoneId].m_indexStart = TempEntityIndicesUsed;
aZones[zoneId].FindTestPoints();
if (!findIndices) return;
for (int i = CPools::GetBuildingPool()->GetSize() - 1; i >= 0; i--) {
CBuilding *building = CPools::GetBuildingPool()->GetSlot(i);
if (building != nil && !building->bIsBIGBuilding && aZones[zoneId].IsEntityCloseEnoughToZone(building, aPointersToBigBuildingsForBuildings[i] != -1)) {
CBuilding *LODbuilding = nil;
if (aPointersToBigBuildingsForBuildings[i] != -1)
LODbuilding = CPools::GetBuildingPool()->GetSlot(aPointersToBigBuildingsForBuildings[i]);
if (!aZones[zoneId].TestEntityVisibilityFromCullZone(building, 0.0f, LODbuilding)) {
assert(TempEntityIndicesUsed < NUMTEMPINDICES);
pTempArrayIndices[TempEntityIndicesUsed++] = i;
aZones[zoneId].m_groupIndexCount[0]++;
}
}
}
for (int i = CPools::GetTreadablePool()->GetSize() - 1; i >= 0; i--) {
CBuilding* building = CPools::GetTreadablePool()->GetSlot(i);
if (building != nil && aZones[zoneId].IsEntityCloseEnoughToZone(building, aPointersToBigBuildingsForTreadables[i] != -1)) {
CBuilding *LODbuilding = nil;
if (aPointersToBigBuildingsForTreadables[i] != -1)
LODbuilding = CPools::GetBuildingPool()->GetSlot(aPointersToBigBuildingsForTreadables[i]);
if (!aZones[zoneId].TestEntityVisibilityFromCullZone(building, 10.0f, LODbuilding)) {
assert(TempEntityIndicesUsed < NUMTEMPINDICES);
pTempArrayIndices[TempEntityIndicesUsed++] = i;
aZones[zoneId].m_groupIndexCount[1]++;
}
}
}
for (int i = CPools::GetTreadablePool()->GetSize() - 1; i >= 0; i--) {
CBuilding *building = CPools::GetTreadablePool()->GetSlot(i);
if (building != nil && aZones[zoneId].CalcDistToCullZoneSquared(building->GetPosition().x, building->GetPosition().y) < SQR(200.0f)) {
int start = aZones[zoneId].m_groupIndexCount[0] + aZones[zoneId].m_indexStart;
int end = aZones[zoneId].m_groupIndexCount[1] + start;
bool alreadyAdded = false;
for (int k = start; k < end; k++) {
#ifdef FIX_BUGS
if (pTempArrayIndices[k] == i)
#else
if (aIndices[k] == i)
#endif
alreadyAdded = true;
}
if (!alreadyAdded) {
CBuilding *LODbuilding = nil;
if (aPointersToBigBuildingsForTreadables[i] != -1)
LODbuilding = CPools::GetBuildingPool()->GetSlot(aPointersToBigBuildingsForTreadables[i]);
if (!aZones[zoneId].TestEntityVisibilityFromCullZone(building, 0.0f, LODbuilding)) {
assert(TempEntityIndicesUsed < NUMTEMPINDICES);
pTempArrayIndices[TempEntityIndicesUsed++] = i;
aZones[zoneId].m_groupIndexCount[2]++;
}
}
}
}
}
void
CCullZones::Update(void)
{
bool invisible;
if(bCullZonesDisabled)
return;
switch(CTimer::GetFrameCounter() & 7){
case 0:
case 4:
/* Update Cull zone */
ForceCullZoneCoors(TheCamera.GetGameCamPosition());
break;
case 2:
/* Update camera attributes */
CurrentFlags_Camera = FindAttributesForCoors(TheCamera.GetGameCamPosition(), nil);
invisible = (CurrentFlags_Camera & ATTRZONE_SUBWAYVISIBLE) == 0;
if(invisible != bCurrentSubwayIsInvisible){
MarkSubwayAsInvisible(!invisible);
bCurrentSubwayIsInvisible = invisible;
}
break;
case 6:
/* Update player attributes */
CurrentFlags_Player = FindAttributesForCoors(FindPlayerCoors(),
&CurrentWantedLevelDrop_Player);
break;
}
}
void
CCullZones::ForceCullZoneCoors(CVector coors)
{
int32 z;
z = FindCullZoneForCoors(coors);
if(z != OldCullZone){
if(OldCullZone >= 0)
aZones[OldCullZone].DoStuffLeavingZone();
if(z >= 0)
aZones[z].DoStuffEnteringZone();
OldCullZone = z;
}
}
int32
CCullZones::FindCullZoneForCoors(CVector coors)
{
int i;
for(i = 0; i < NumCullZones; i++)
if(coors.x >= aZones[i].minx && coors.x <= aZones[i].maxx &&
coors.y >= aZones[i].miny && coors.y <= aZones[i].maxy &&
coors.z >= aZones[i].minz && coors.z <= aZones[i].maxz)
return i;
return -1;
}
int32
CCullZones::FindAttributesForCoors(CVector coors, int32 *wantedLevel)
{
int i;
int32 attribs;
if (wantedLevel)
*wantedLevel = 0;
attribs = 0;
for(i = 0; i < NumAttributeZones; i++)
if(coors.x >= aAttributeZones[i].minx && coors.x <= aAttributeZones[i].maxx &&
coors.y >= aAttributeZones[i].miny && coors.y <= aAttributeZones[i].maxy &&
coors.z >= aAttributeZones[i].minz && coors.z <= aAttributeZones[i].maxz){
attribs |= aAttributeZones[i].attributes;
if(wantedLevel)
*wantedLevel = Max(*wantedLevel, aAttributeZones[i].wantedLevel);
}
return attribs;
}
CAttributeZone*
CCullZones::FindZoneWithStairsAttributeForPlayer(void)
{
int i;
CVector coors;
coors = FindPlayerCoors();
for(i = 0; i < NumAttributeZones; i++)
if(aAttributeZones[i].attributes & ATTRZONE_STAIRS &&
coors.x >= aAttributeZones[i].minx && coors.x <= aAttributeZones[i].maxx &&
coors.y >= aAttributeZones[i].miny && coors.y <= aAttributeZones[i].maxy &&
coors.z >= aAttributeZones[i].minz && coors.z <= aAttributeZones[i].maxz)
return &aAttributeZones[i];
return nil;
}
void
CCullZones::MarkSubwayAsInvisible(bool visible)
{
int i, n;
CEntity *e;
CVehicle *v;
n = CPools::GetBuildingPool()->GetSize()-1;
for(i = n; i >= 0; i--){
e = CPools::GetBuildingPool()->GetSlot(i);
if(e && e->bIsSubway)
e->bIsVisible = visible;
}
n = CPools::GetTreadablePool()->GetSize()-1;
for(i = n; i >= 0; i--){
e = CPools::GetTreadablePool()->GetSlot(i);
if(e && e->bIsSubway)
e->bIsVisible = visible;
}
n = CPools::GetVehiclePool()->GetSize()-1;
for(i = n; i >= 0; i--){
v = CPools::GetVehiclePool()->GetSlot(i);
if(v && v->IsTrain() && ((CTrain*)v)->m_nTrackId != TRACK_ELTRAIN)
v->bIsVisible = visible;
}
}
void
CCullZones::AddCullZone(CVector const &position,
float minx, float maxx,
float miny, float maxy,
float minz, float maxz,
uint16 flag, int16 wantedLevel)
{
CCullZone *cull;
CAttributeZone *attrib;
CVector v;
if((flag & ATTRZONE_NOTCULLZONE) == 0){
cull = &aZones[NumCullZones++];
v = position;
// reposition start point to the start/end of the
// alley next to the big building in the industrial district.
// probably isn't analyzed correctly otherwise?s
if((v-CVector(1032.14f, -624.255f, 24.93f)).Magnitude() < 1.0f)
v = CVector(1061.7f, -613.0f, 19.0f);
if((v-CVector(1029.48f, -495.757f, 21.98f)).Magnitude() < 1.0f)
v = CVector(1061.4f, -506.0f, 18.5f);
cull->position.x = clamp(v.x, minx, maxx);
cull->position.y = clamp(v.y, miny, maxy);
cull->position.z = clamp(v.z, minz, maxz);
cull->minx = minx;
cull->maxx = maxx;
cull->miny = miny;
cull->maxy = maxy;
cull->minz = minz;
cull->maxz = maxz;
cull->m_groupIndexCount[0] = 0;
cull->m_groupIndexCount[1] = 0;
cull->m_groupIndexCount[2] = 0;
cull->m_indexStart = 0;
}
if(flag & ~ATTRZONE_NOTCULLZONE){
attrib = &aAttributeZones[NumAttributeZones++];
attrib->minx = minx;
attrib->maxx = maxx;
attrib->miny = miny;
attrib->maxy = maxy;
attrib->minz = minz;
attrib->maxz = maxz;
attrib->attributes = flag;
attrib->wantedLevel = wantedLevel;
}
}
uint16 *pExtraArrayIndices;
void
CCullZones::CompressIndicesArray()
{
uint16 set[3];
// These are used to hold the compressed groups in sets of 3
int numExtraIndices = 0;
pExtraArrayIndices = new uint16[NUMTEMPINDICES];
for(int numOccurrences = 6; numOccurrences > 1; numOccurrences--){
if(NumCullZones == 0)
break;
//printf("checking occurrences %d\n", numOccurrences);
int attempt = 0;
while(attempt < 10000){
for(;;){
attempt++;
int zone = CGeneral::GetRandomNumber() % NumCullZones;
int group = CGeneral::GetRandomNumber() % 3;
if(!PickRandomSetForGroup(zone, group, set))
break;
if(!DoWeHaveMoreThanXOccurencesOfSet(numOccurrences, set))
break;
// add this set
attempt = 1;
int setId = numExtraIndices + NUMUNCOMPRESSED;
pExtraArrayIndices[numExtraIndices++] = set[0];
pExtraArrayIndices[numExtraIndices++] = set[1];
pExtraArrayIndices[numExtraIndices++] = set[2];
ReplaceSetForAllGroups(set, setId);
}
}
}
TidyUpAndMergeLists(pExtraArrayIndices, numExtraIndices);
delete[] pExtraArrayIndices;
}
// Get three random indices for this group of a zone
bool
CCullZones::PickRandomSetForGroup(int32 zone, int32 group, uint16 *set)
{
int32 start;
int32 size;
aZones[zone].GetGroupStartAndSize(group, start, size);
if(size <= 0)
return false;
int numIndices = 0;
for(int i = 0; i < size; i++)
if(pTempArrayIndices[start + i] != 0xFFFF)
numIndices++;
if(numIndices < 3)
return false;
int first = CGeneral::GetRandomNumber() % (numIndices-2);
numIndices = 0;
int n = 0;
for(int i = 0; i < size; i++)
if(pTempArrayIndices[start + i] != 0xFFFF){
if(n++ < first) continue;
set[numIndices++] = pTempArrayIndices[start + i];
if(numIndices == 3)
break;
}
return true;
}
bool
CCullZones::DoWeHaveMoreThanXOccurencesOfSet(int32 count, uint16 *set)
{
int32 curCount;
int32 start;
int32 size;
curCount = 0;
for (int i = 0; i < NumCullZones; i++) {
for (int group = 0; group < 3; group++) {
aZones[i].GetGroupStartAndSize(group, start, size);
if(size <= 0) continue;
// check if the set is a subset of the group
int n = 0;
for (int j = 0; j < size; j++) {
for (int k = 0; k < 3; k++) {
if (pTempArrayIndices[start+j] == set[k])
n++;
}
}
// yes it is
if(n == 3){
curCount++;
// check if we have seen this set often enough
if(curCount >= count)
return true;
}
}
}
return false;
}
void
CCullZones::ReplaceSetForAllGroups(uint16 *set, uint16 setid)
{
int32 start;
int32 size;
for(int i = 0; i < NumCullZones; i++)
for(int group = 0; group < 3; group++){
aZones[i].GetGroupStartAndSize(group, start, size);
if(size <= 0) continue;
// check if the set is a subset of the group
int n = 0;
for(int j = 0; j < size; j++){
for(int k = 0; k < 3; k++){
if(pTempArrayIndices[start+j] == set[k])
n++;
}
}
// yes it is, so replace it
if(n == 3){
bool insertedSet = false;
for(int j = 0; j < size; j++){
for(int k = 0; k < 3; k++){
// replace first element by set, invalidate others
if(pTempArrayIndices[start+j] == set[k]){
if(!insertedSet)
pTempArrayIndices[start+j] = setid;
else
pTempArrayIndices[start+j] = 0xFFFF;
insertedSet = true;
}
}
}
}
}
}
void
CCullZones::TidyUpAndMergeLists(uint16 *extraIndices, int32 numExtraIndices)
{
int numTempIndices = 0;
for(int i = 0; i < TempEntityIndicesUsed; i++)
if(pTempArrayIndices[i] != 0xFFFF)
numTempIndices++;
// Fix up zone ranges such that there are no holes
for(int i = 0; i < NumCullZones; i++){
int j;
int start = 0;
for(j = 0; j < aZones[i].m_indexStart; j++)
if(pTempArrayIndices[j] != 0xFFFF)
start++;
aZones[i].m_indexStart = start;
aZones[i].m_numBuildings = 0;
aZones[i].m_numTreadablesPlus10m = 0;
aZones[i].m_numTreadables = 0;
for(int k = 0; k < aZones[i].m_groupIndexCount[0]; k++)
if(pTempArrayIndices[j++] != 0xFFFF)
aZones[i].m_numBuildings++;
for(int k = 0; k < aZones[i].m_groupIndexCount[1]; k++)
if(pTempArrayIndices[j++] != 0xFFFF)
aZones[i].m_numTreadablesPlus10m++;
for(int k = 0; k < aZones[i].m_groupIndexCount[2]; k++)
if(pTempArrayIndices[j++] != 0xFFFF)
aZones[i].m_numTreadables++;
}
// Now copy the actually used indices
EntityIndicesUsed = 0;
for(int i = 0; i < TempEntityIndicesUsed; i++)
if(pTempArrayIndices[i] != 0xFFFF){
assert(EntityIndicesUsed < NUMZONEINDICES);
if(pTempArrayIndices[i] < NUMUNCOMPRESSED)
aIndices[EntityIndicesUsed++] = pTempArrayIndices[i];
else
aIndices[EntityIndicesUsed++] = pTempArrayIndices[i] + numTempIndices;
}
for(int i = 0; i < numExtraIndices; i++)
if(extraIndices[i] != 0xFFFF){
assert(EntityIndicesUsed < NUMZONEINDICES);
if(extraIndices[i] < NUMUNCOMPRESSED)
aIndices[EntityIndicesUsed++] = extraIndices[i];
else
aIndices[EntityIndicesUsed++] = extraIndices[i] + numTempIndices;
}
}
void
CCullZone::DoStuffLeavingZone(void)
{
int i;
for(i = 0; i < m_numBuildings; i++)
DoStuffLeavingZone_OneBuilding(CCullZones::aIndices[m_indexStart + i]);
for(; i < m_numBuildings + m_numTreadablesPlus10m + m_numTreadables ; i++)
DoStuffLeavingZone_OneTreadableBoth(CCullZones::aIndices[m_indexStart + i]);
}
void
CCullZone::DoStuffLeavingZone_OneBuilding(uint16 i)
{
int16 bb;
int j;
if(i < NUMUNCOMPRESSED){
CPools::GetBuildingPool()->GetSlot(i)->bZoneCulled = false;
bb = CCullZones::aPointersToBigBuildingsForBuildings[i];
if(bb != -1)
CPools::GetBuildingPool()->GetSlot(bb)->bZoneCulled = false;
}else{
i -= NUMUNCOMPRESSED;
for(j = 0; j < 3; j++)
DoStuffLeavingZone_OneBuilding(CCullZones::aIndices[i+j]);
}
}
void
CCullZone::DoStuffLeavingZone_OneTreadableBoth(uint16 i)
{
int16 bb;
int j;
if(i < NUMUNCOMPRESSED){
CPools::GetTreadablePool()->GetSlot(i)->bZoneCulled = false;
CPools::GetTreadablePool()->GetSlot(i)->bZoneCulled2 = false;
bb = CCullZones::aPointersToBigBuildingsForTreadables[i];
if(bb != -1)
CPools::GetBuildingPool()->GetSlot(bb)->bZoneCulled = false;
}else{
i -= NUMUNCOMPRESSED;
for(j = 0; j < 3; j++)
DoStuffLeavingZone_OneTreadableBoth(CCullZones::aIndices[i+j]);
}
}
void
CCullZone::DoStuffEnteringZone(void)
{
int i;
for(i = 0; i < m_numBuildings; i++)
DoStuffEnteringZone_OneBuilding(CCullZones::aIndices[m_indexStart + i]);
for(; i < m_numBuildings + m_numTreadablesPlus10m; i++)
DoStuffEnteringZone_OneTreadablePlus10m(CCullZones::aIndices[m_indexStart + i]);
for(; i < m_numBuildings + m_numTreadablesPlus10m + m_numTreadables; i++)
DoStuffEnteringZone_OneTreadable(CCullZones::aIndices[m_indexStart + i]);
}
void
CCullZone::DoStuffEnteringZone_OneBuilding(uint16 i)
{
int16 bb;
int j;
if(i < NUMUNCOMPRESSED){
CPools::GetBuildingPool()->GetSlot(i)->bZoneCulled = true;
bb = CCullZones::aPointersToBigBuildingsForBuildings[i];
if(bb != -1)
CPools::GetBuildingPool()->GetSlot(bb)->bZoneCulled = true;
}else{
i -= NUMUNCOMPRESSED;
for(j = 0; j < 3; j++)
DoStuffEnteringZone_OneBuilding(CCullZones::aIndices[i+j]);
}
}
void
CCullZone::DoStuffEnteringZone_OneTreadablePlus10m(uint16 i)
{
int16 bb;
int j;
if(i < NUMUNCOMPRESSED){
CPools::GetTreadablePool()->GetSlot(i)->bZoneCulled = true;
CPools::GetTreadablePool()->GetSlot(i)->bZoneCulled2 = true;
bb = CCullZones::aPointersToBigBuildingsForTreadables[i];
if(bb != -1)
CPools::GetBuildingPool()->GetSlot(bb)->bZoneCulled = true;
}else{
i -= NUMUNCOMPRESSED;
for(j = 0; j < 3; j++)
DoStuffEnteringZone_OneTreadablePlus10m(CCullZones::aIndices[i+j]);
}
}
void
CCullZone::DoStuffEnteringZone_OneTreadable(uint16 i)
{
int16 bb;
int j;
if(i < NUMUNCOMPRESSED){
CPools::GetTreadablePool()->GetSlot(i)->bZoneCulled = true;
bb = CCullZones::aPointersToBigBuildingsForTreadables[i];
if(bb != -1)
CPools::GetBuildingPool()->GetSlot(bb)->bZoneCulled = true;
}else{
i -= NUMUNCOMPRESSED;
for(j = 0; j < 3; j++)
DoStuffEnteringZone_OneTreadable(CCullZones::aIndices[i+j]);
}
}
float
CCullZone::CalcDistToCullZoneSquared(float x, float y)
{
float rx, ry;
if (x < minx) rx = sq(x - minx);
else if (x > maxx) rx = sq(x - maxx);
else rx = 0.0f;
if (y < miny) ry = sq(y - miny);
else if (y > maxy) ry = sq(y - maxy);
else ry = 0.0f;
return rx + ry;
}
bool
CCullZone::TestLine(CVector vec1, CVector vec2)
{
CColPoint colPoint;
CEntity *entity;
if (CWorld::ProcessLineOfSight(vec1, vec2, colPoint, entity, true, false, false, false, false, true, false))
return true;
if (CWorld::ProcessLineOfSight(CVector(vec1.x + 0.05f, vec1.y, vec1.z), CVector(vec2.x + 0.05f, vec2.y, vec2.z), colPoint, entity, true, false, false, false, false, true, false))
return true;
if (CWorld::ProcessLineOfSight(CVector(vec1.x - 0.05f, vec1.y, vec1.z), CVector(vec2.x - 0.05f, vec2.y, vec2.z), colPoint, entity, true, false, false, false, false, true, false))
return true;
if (CWorld::ProcessLineOfSight(CVector(vec1.x, vec1.y + 0.05f, vec1.z), CVector(vec2.x, vec2.y + 0.05f, vec2.z), colPoint, entity, true, false, false, false, false, true, false))
return true;
if (CWorld::ProcessLineOfSight(CVector(vec1.x, vec1.y - 0.05f, vec1.z), CVector(vec2.x, vec2.y - 0.05f, vec2.z), colPoint, entity, true, false, false, false, false, true, false))
return true;
if (CWorld::ProcessLineOfSight(CVector(vec1.x, vec1.y, vec1.z + 0.05f), CVector(vec2.x, vec2.y, vec2.z + 0.05f), colPoint, entity, true, false, false, false, false, true, false))
return true;
return CWorld::ProcessLineOfSight(CVector(vec1.x, vec1.y, vec1.z - 0.05f), CVector(vec2.x, vec2.y, vec2.z - 0.05f), colPoint, entity, true, false, false, false, false, true, false);
}
bool
CCullZone::DoThoroughLineTest(CVector start, CVector end, CEntity *testEntity)
{
CColPoint colPoint;
CEntity *entity;
if(CWorld::ProcessLineOfSight(start, end, colPoint, entity, true, false, false, false, false, true, false) &&
testEntity != entity)
return false;
CVector side;
#ifdef FIX_BUGS
if(start.x != end.x || start.y != end.y)
#else
if(start.x != end.x && start.y != end.y)
#endif
side = CVector(0.0f, 0.0f, 1.0f);
else
side = CVector(1.0f, 0.0f, 0.0f);
CVector up = CrossProduct(side, end - start);
side = CrossProduct(up, end - start);
side.Normalise();
up.Normalise();
side *= 0.1f;
up *= 0.1f;
if(CWorld::ProcessLineOfSight(start+side, end+side, colPoint, entity, true, false, false, false, false, true, false) &&
testEntity != entity)
return false;
if(CWorld::ProcessLineOfSight(start-side, end-side, colPoint, entity, true, false, false, false, false, true, false) &&
testEntity != entity)
return false;
if(CWorld::ProcessLineOfSight(start+up, end+up, colPoint, entity, true, false, false, false, false, true, false) &&
testEntity != entity)
return false;
if(CWorld::ProcessLineOfSight(start-up, end-up, colPoint, entity, true, false, false, false, false, true, false) &&
testEntity != entity)
return false;
return true;
}
bool
CCullZone::IsEntityCloseEnoughToZone(CEntity *entity, bool checkLevel)
{
const CVector &pos = entity->GetPosition();
CSimpleModelInfo *minfo = (CSimpleModelInfo*)CModelInfo::GetModelInfo(entity->GetModelIndex());
float distToZone = CalcDistToCullZone(pos.x, pos.y);
float lodDist;
if (minfo->m_noFade)
lodDist = minfo->GetLargestLodDistance() + STREAM_DISTANCE;
else
lodDist = minfo->GetLargestLodDistance() + STREAM_DISTANCE + FADE_DISTANCE;
if (lodDist > distToZone) return true;
if (!checkLevel) return false;
CVector tempPos(minx, miny, minz);
return CTheZones::GetLevelFromPosition(&pos) == CTheZones::GetLevelFromPosition(&tempPos);
}
bool
CCullZone::PointFallsWithinZone(CVector pos, float radius)
{
if(minx - radius > pos.x ||
maxx + radius < pos.x ||
miny - radius > pos.y ||
maxy + radius < pos.y ||
minz - radius > pos.z ||
maxz + radius < pos.z)
return false;
return true;
}
CVector ExtraFudgePointsCoors[] = {
CVector(978.0, -394.0, 18.0),
CVector(1189.7, -414.6, 27.0),
CVector(978.8, -391.0, 19.0),
CVector(1199.0, -502.3, 28.0),
CVector(1037.0, -391.9, 18.4),
CVector(1140.0, -608.7, 16.0),
CVector(1051.0, -26.0, 11.0),
CVector(951.5, -345.1, 12.0),
CVector(958.2, -394.6, 16.0),
CVector(1036.5, -390.0, 15.2),
CVector(960.6, -390.5, 20.9),
CVector(1061.0, -640.6, 16.3),
CVector(1034.5, -388.96, 14.78),
CVector(1038.4, -13.98, 12.2),
CVector(1047.2, -16.7, 10.6),
CVector(1257.9, -333.3, 40.0),
CVector(885.6, -424.9, 17.0),
CVector(1127.5, -795.8, 17.7),
CVector(1133.0, -716.0, 19.0),
CVector(1125.0, -694.0, 18.5),
CVector(1125.0, -670.0, 16.3),
CVector(1051.6, 36.3, 17.9),
CVector(1054.6, -11.4, 15.0),
CVector(1058.9, -278.0, 15.0),
CVector(1059.4, -261.0, 10.9),
CVector(1051.5, -638.5, 16.5),
CVector(1058.2, -643.4, 15.5),
CVector(1058.2, -643.4, 18.0),
CVector(826.0, -260.0, 7.0),
CVector(826.0, -260.0, 11.0),
CVector(833.0, -603.6, 16.4),
CVector(833.0, -603.6, 20.0),
CVector(1002.0, -318.5, 10.5),
CVector(998.0, -318.0, 9.8),
CVector(1127.0, -183.0, 18.1),
CVector(1123.0, -331.5, 23.8),
CVector(1123.8, -429.0, 24.0),
CVector(1197.0, -30.0, 13.7),
CVector(1117.5, -230.0, 17.3),
CVector(1117.5, -230.0, 20.0),
CVector(1120.0, -281.6, 21.5),
CVector(1120.0, -281.6, 24.0),
CVector(1084.5, -1022.7, 17.0),
CVector(1071.5, 5.4, 4.6),
CVector(1177.2, -215.7, 27.6),
CVector(841.6, -460.0, 19.7),
CVector(874.8, -456.6, 16.6),
CVector(918.3, -451.8, 17.8),
CVector(844.0, -495.7, 16.7),
CVector(842.0, -493.4, 21.0),
CVector(1433.5, -774.4, 16.9),
CVector(1051.0, -205.0, 7.5),
CVector(885.5, -425.6, 15.6),
CVector(182.6, -470.4, 27.8),
CVector(132.5, -930.2, 29.0),
CVector(124.7, -904.0, 28.0),
CVector(-50.0, -686.0, 22.0),
CVector(-49.1, -694.5, 22.5),
CVector(1063.8, -404.45, 16.2),
CVector(1062.2, -405.5, 17.0)
};
int32 NumTestPoints;
int32 aTestPointsX[100];
int32 aTestPointsY[100];
int32 aTestPointsZ[100];
CVector aTestPoints[100];
int32 ElementsX, ElementsY, ElementsZ;
float StepX, StepY, StepZ;
int32 Memsize;
uint8 *pMem;
#define MEM(x, y, z) pMem[((x)*ElementsY + (y))*ElementsZ + (z)]
#define FLAG_FREE 1
#define FLAG_PROCESSED 2
int32 MinValX, MaxValX;
int32 MinValY, MaxValY;
int32 MinValZ, MaxValZ;
int32 Point1, Point2;
int32 NewPointX, NewPointY, NewPointZ;
void
CCullZone::FindTestPoints()
{
static int CZNumber;
NumTestPoints = 0;
ElementsX = (maxx-minx) < 1.0f ? 2 : (maxx-minx)+1.0f;
ElementsY = (maxy-miny) < 1.0f ? 2 : (maxy-miny)+1.0f;
ElementsZ = (maxz-minz) < 1.0f ? 2 : (maxz-minz)+1.0f;
if(ElementsX > 32) ElementsX = 32;
if(ElementsY > 32) ElementsY = 32;
if(ElementsZ > 32) ElementsZ = 32;
Memsize = ElementsX * ElementsY * ElementsZ;
StepX = (maxx-minx)/(ElementsX-1);
StepY = (maxy-miny)/(ElementsY-1);
StepZ = (maxz-minz)/(ElementsZ-1);
pMem = new uint8[Memsize];
memset(pMem, 0, Memsize);
// indices of center
int x = ElementsX * (position.x-minx)/(maxx-minx);
x = clamp(x, 0, ElementsX-1);
int y = ElementsY * (position.y-miny)/(maxy-miny);
y = clamp(y, 0, ElementsY-1);
int z = ElementsZ * (position.z-minz)/(maxz-minz);
z = clamp(z, 0, ElementsZ-1);
// Mark which test points inside the zone are not occupied by buildings.
// To do this, mark the start point as free and do a food fill.
// NB: we just assume the start position is free here!
MEM(x, y, z) |= FLAG_FREE;
aTestPointsX[NumTestPoints] = x;
aTestPointsY[NumTestPoints] = y;
aTestPointsZ[NumTestPoints] = z;
NumTestPoints++;
bool notDoneYet;
do{
notDoneYet = false;
for(x = 0; x < ElementsX; x++){
for(y = 0; y < ElementsY; y++){
for(z = 0; z < ElementsZ; z++){
if(!(MEM(x, y, z) & FLAG_FREE) || MEM(x, y, z) & FLAG_PROCESSED)
continue;
float pX = x*StepX + minx;
float pY = y*StepY + miny;
float pZ = z*StepZ + minz;
if(x > 0 && !(MEM(x-1, y, z) & (FLAG_FREE | FLAG_PROCESSED)) &&
!TestLine(CVector(pX, pY, pZ), CVector(pX-StepX, pY, pZ)))
MEM(x-1, y, z) |= FLAG_FREE;
if(x < ElementsX-1 && !(MEM(x+1, y, z) & (FLAG_FREE | FLAG_PROCESSED)) &&
!TestLine(CVector(pX, pY, pZ), CVector(pX+StepX, pY, pZ)))
MEM(x+1, y, z) |= FLAG_FREE;
if(y > 0 && !(MEM(x, y-1, z) & (FLAG_FREE | FLAG_PROCESSED)) &&
!TestLine(CVector(pX, pY, pZ), CVector(pX, pY-StepY, pZ)))
MEM(x, y-1, z) |= FLAG_FREE;
if(y < ElementsY-1 && !(MEM(x, y+1, z) & (FLAG_FREE | FLAG_PROCESSED)) &&
!TestLine(CVector(pX, pY, pZ), CVector(pX, pY+StepY, pZ)))
MEM(x, y+1, z) |= FLAG_FREE;
if(z > 0 && !(MEM(x, y, z-1) & (FLAG_FREE | FLAG_PROCESSED)) &&
!TestLine(CVector(pX, pY, pZ), CVector(pX, pY, pZ-StepZ)))
MEM(x, y, z-1) |= FLAG_FREE;
if(z < ElementsZ-1 && !(MEM(x, y, z+1) & (FLAG_FREE | FLAG_PROCESSED)) &&
!TestLine(CVector(pX, pY, pZ), CVector(pX, pY, pZ+StepZ)))
MEM(x, y, z+1) |= FLAG_FREE;
notDoneYet = true;
MEM(x, y, z) |= FLAG_PROCESSED;
}
}
}
}while(notDoneYet);
bool done;
// Find bound planes of free space
// increase x, bounds in y and z
x = 0;
do{
done = false;
int minA = 10000;
int minB = 10000;
int maxA = -10000;
int maxB = -10000;
for(y = 0; y < ElementsY; y++)
for(z = 0; z < ElementsZ; z++)
if(MEM(x, y, z) & FLAG_FREE){
if(y + z < minA){
minA = y + z;
aTestPointsX[NumTestPoints] = x;
aTestPointsY[NumTestPoints] = y;
aTestPointsZ[NumTestPoints] = z;
}
if(y + z > maxA){
maxA = y + z;
aTestPointsX[NumTestPoints+1] = x;
aTestPointsY[NumTestPoints+1] = y;
aTestPointsZ[NumTestPoints+1] = z;
}
if(y - z < minB){
minB = y - z;
aTestPointsX[NumTestPoints+2] = x;
aTestPointsY[NumTestPoints+2] = y;
aTestPointsZ[NumTestPoints+2] = z;
}
if(y - z > maxB){
maxB = y - z;
aTestPointsX[NumTestPoints+3] = x;
aTestPointsY[NumTestPoints+3] = y;
aTestPointsZ[NumTestPoints+3] = z;
}
done = true;
}
x++;
}while(!done);
NumTestPoints += 4;
// decrease x, bounds in y and z
x = ElementsX-1;
do{
done = false;
int minA = 10000;
int minB = 10000;
int maxA = -10000;
int maxB = -10000;
for(y = 0; y < ElementsY; y++)
for(z = 0; z < ElementsZ; z++)
if(MEM(x, y, z) & FLAG_FREE){
if(y + z < minA){
minA = y + z;
aTestPointsX[NumTestPoints] = x;
aTestPointsY[NumTestPoints] = y;
aTestPointsZ[NumTestPoints] = z;
}
if(y + z > maxA){
maxA = y + z;
aTestPointsX[NumTestPoints+1] = x;
aTestPointsY[NumTestPoints+1] = y;
aTestPointsZ[NumTestPoints+1] = z;
}
if(y - z < minB){
minB = y - z;
aTestPointsX[NumTestPoints+2] = x;
aTestPointsY[NumTestPoints+2] = y;
aTestPointsZ[NumTestPoints+2] = z;
}
if(y - z > maxB){
maxB = y - z;
aTestPointsX[NumTestPoints+3] = x;
aTestPointsY[NumTestPoints+3] = y;
aTestPointsZ[NumTestPoints+3] = z;
}
done = true;
}
x--;
}while(!done);
NumTestPoints += 4;
// increase y, bounds in x and z
y = 0;
do{
done = false;
int minA = 10000;
int minB = 10000;
int maxA = -10000;
int maxB = -10000;
for(x = 0; x < ElementsX; x++)
for(z = 0; z < ElementsZ; z++)
if(MEM(x, y, z) & FLAG_FREE){
if(x + z < minA){
minA = x + z;
aTestPointsX[NumTestPoints] = x;
aTestPointsY[NumTestPoints] = y;
aTestPointsZ[NumTestPoints] = z;
}
if(x + z > maxA){
maxA = x + z;
aTestPointsX[NumTestPoints+1] = x;
aTestPointsY[NumTestPoints+1] = y;
aTestPointsZ[NumTestPoints+1] = z;
}
if(x - z < minB){
minB = x - z;
aTestPointsX[NumTestPoints+2] = x;
aTestPointsY[NumTestPoints+2] = y;
aTestPointsZ[NumTestPoints+2] = z;
}
if(x - z > maxB){
maxB = x - z;
aTestPointsX[NumTestPoints+3] = x;
aTestPointsY[NumTestPoints+3] = y;
aTestPointsZ[NumTestPoints+3] = z;
}
done = true;
}
y++;
}while(!done);
NumTestPoints += 4;
// decrease y, bounds in x and z
y = ElementsY-1;
do{
done = false;
int minA = 10000;
int minB = 10000;
int maxA = -10000;
int maxB = -10000;
for(x = 0; x < ElementsX; x++)
for(z = 0; z < ElementsZ; z++)
if(MEM(x, y, z) & FLAG_FREE){
if(x + z < minA){
minA = x + z;
aTestPointsX[NumTestPoints] = x;
aTestPointsY[NumTestPoints] = y;
aTestPointsZ[NumTestPoints] = z;
}
if(x + z > maxA){
maxA = x + z;
aTestPointsX[NumTestPoints+1] = x;
aTestPointsY[NumTestPoints+1] = y;
aTestPointsZ[NumTestPoints+1] = z;
}
if(x - z < minB){
minB = x - z;
aTestPointsX[NumTestPoints+2] = x;
aTestPointsY[NumTestPoints+2] = y;
aTestPointsZ[NumTestPoints+2] = z;
}
if(x - z > maxB){
maxB = x - z;
aTestPointsX[NumTestPoints+3] = x;
aTestPointsY[NumTestPoints+3] = y;
aTestPointsZ[NumTestPoints+3] = z;
}
done = true;
}
y--;
}while(!done);
NumTestPoints += 4;
// increase z, bounds in x and y
z = 0;
do{
done = false;
int minA = 10000;
int minB = 10000;
int maxA = -10000;
int maxB = -10000;
for(x = 0; x < ElementsX; x++)
for(y = 0; y < ElementsY; y++)
if(MEM(x, y, z) & FLAG_FREE){
if(x + y < minA){
minA = x + y;
aTestPointsX[NumTestPoints] = x;
aTestPointsY[NumTestPoints] = y;
aTestPointsZ[NumTestPoints] = z;
}
if(x + y > maxA){
maxA = x + y;
aTestPointsX[NumTestPoints+1] = x;
aTestPointsY[NumTestPoints+1] = y;
aTestPointsZ[NumTestPoints+1] = z;
}
if(x - y < minB){
minB = x - y;
aTestPointsX[NumTestPoints+2] = x;
aTestPointsY[NumTestPoints+2] = y;
aTestPointsZ[NumTestPoints+2] = z;
}
if(x - y > maxB){
maxB = x - y;
aTestPointsX[NumTestPoints+3] = x;
aTestPointsY[NumTestPoints+3] = y;
aTestPointsZ[NumTestPoints+3] = z;
}
done = true;
}
z++;
}while(!done);
NumTestPoints += 4;
// decrease z, bounds in x and y
z = ElementsZ-1;
do{
done = false;
int minA = 10000;
int minB = 10000;
int maxA = -10000;
int maxB = -10000;
for(x = 0; x < ElementsX; x++)
for(y = 0; y < ElementsY; y++)
if(MEM(x, y, z) & FLAG_FREE){
if(x + y < minA){
minA = x + y;
aTestPointsX[NumTestPoints] = x;
aTestPointsY[NumTestPoints] = y;
aTestPointsZ[NumTestPoints] = z;
}
if(x + y > maxA){
maxA = x + y;
aTestPointsX[NumTestPoints+1] = x;
aTestPointsY[NumTestPoints+1] = y;
aTestPointsZ[NumTestPoints+1] = z;
}
if(x - y < minB){
minB = x - y;
aTestPointsX[NumTestPoints+2] = x;
aTestPointsY[NumTestPoints+2] = y;
aTestPointsZ[NumTestPoints+2] = z;
}
if(x - y > maxB){
maxB = x - y;
aTestPointsX[NumTestPoints+3] = x;
aTestPointsY[NumTestPoints+3] = y;
aTestPointsZ[NumTestPoints+3] = z;
}
done = true;
}
z--;
}while(!done);
NumTestPoints += 4;
// divide the axis aligned bounding planes into 4 and place some test points
// x = 0 plane
MinValY = 999999;
MinValZ = 999999;
MaxValY = 0;
MaxValZ = 0;
for(y = 0; y < ElementsY; y++)
for(z = 0; z < ElementsZ; z++)
if(MEM(0, y, z) & FLAG_FREE){
if(y < MinValY) MinValY = y;
if(z < MinValZ) MinValZ = z;
if(y > MaxValY) MaxValY = y;
if(z > MaxValZ) MaxValZ = z;
}
// pick 4 points in the found bounds and add new test points
if(MaxValY != 0 && MaxValZ != 0)
for(Point1 = 0; Point1 < 2; Point1++)
for(Point2 = 0; Point2 < 2; Point2++){
NewPointY = (Point1 + 0.5f)*(MaxValY - MinValY)*0.5f + MinValY;
NewPointZ = (Point2 + 0.5f)*(MaxValZ - MinValZ)*0.5f + MinValZ;
if(MEM(0, NewPointY, NewPointZ) & FLAG_FREE){
aTestPointsX[NumTestPoints] = 0;
aTestPointsY[NumTestPoints] = NewPointY;
aTestPointsZ[NumTestPoints] = NewPointZ;
NumTestPoints++;
}
}
// x = ElementsX-1 plane
MinValY = 999999;
MinValZ = 999999;
MaxValY = 0;
MaxValZ = 0;
for(y = 0; y < ElementsY; y++)
for(z = 0; z < ElementsZ; z++)
if(MEM(ElementsX-1, y, z) & FLAG_FREE){
if(y < MinValY) MinValY = y;
if(z < MinValZ) MinValZ = z;
if(y > MaxValY) MaxValY = y;
if(z > MaxValZ) MaxValZ = z;
}
// pick 4 points in the found bounds and add new test points
if(MaxValY != 0 && MaxValZ != 0)
for(Point1 = 0; Point1 < 2; Point1++)
for(Point2 = 0; Point2 < 2; Point2++){
NewPointY = (Point1 + 0.5f)*(MaxValY - MinValY)*0.5f + MinValY;
NewPointZ = (Point2 + 0.5f)*(MaxValZ - MinValZ)*0.5f + MinValZ;
if(MEM(ElementsX-1, NewPointY, NewPointZ) & FLAG_FREE){
aTestPointsX[NumTestPoints] = ElementsX-1;
aTestPointsY[NumTestPoints] = NewPointY;
aTestPointsZ[NumTestPoints] = NewPointZ;
NumTestPoints++;
}
}
// y = 0 plane
MinValX = 999999;
MinValZ = 999999;
MaxValX = 0;
MaxValZ = 0;
for(x = 0; x < ElementsX; x++)
for(z = 0; z < ElementsZ; z++)
if(MEM(x, 0, z) & FLAG_FREE){
if(x < MinValX) MinValX = x;
if(z < MinValZ) MinValZ = z;
if(x > MaxValX) MaxValX = x;
if(z > MaxValZ) MaxValZ = z;
}
// pick 4 points in the found bounds and add new test points
if(MaxValX != 0 && MaxValZ != 0)
for(Point1 = 0; Point1 < 2; Point1++)
for(Point2 = 0; Point2 < 2; Point2++){
NewPointX = (Point1 + 0.5f)*(MaxValX - MinValX)*0.5f + MinValX;
NewPointZ = (Point2 + 0.5f)*(MaxValZ - MinValZ)*0.5f + MinValZ;
if(MEM(NewPointX, 0, NewPointZ) & FLAG_FREE){
aTestPointsX[NumTestPoints] = NewPointX;
aTestPointsY[NumTestPoints] = 0;
aTestPointsZ[NumTestPoints] = NewPointZ;
NumTestPoints++;
}
}
// y = ElementsY-1 plane
MinValX = 999999;
MinValZ = 999999;
MaxValX = 0;
MaxValZ = 0;
for(x = 0; x < ElementsX; x++)
for(z = 0; z < ElementsZ; z++)
if(MEM(x, ElementsY-1, z) & FLAG_FREE){
if(x < MinValX) MinValX = x;
if(z < MinValZ) MinValZ = z;
if(x > MaxValX) MaxValX = x;
if(z > MaxValZ) MaxValZ = z;
}
// pick 4 points in the found bounds and add new test points
if(MaxValX != 0 && MaxValZ != 0)
for(Point1 = 0; Point1 < 2; Point1++)
for(Point2 = 0; Point2 < 2; Point2++){
NewPointX = (Point1 + 0.5f)*(MaxValX - MinValX)*0.5f + MinValX;
NewPointZ = (Point2 + 0.5f)*(MaxValZ - MinValZ)*0.5f + MinValZ;
if(MEM(NewPointX, ElementsY-1, NewPointZ) & FLAG_FREE){
aTestPointsX[NumTestPoints] = NewPointX;
aTestPointsY[NumTestPoints] = ElementsY-1;
aTestPointsZ[NumTestPoints] = NewPointZ;
NumTestPoints++;
}
}
// z = 0 plane
MinValX = 999999;
MinValY = 999999;
MaxValX = 0;
MaxValY = 0;
for(x = 0; x < ElementsX; x++)
for(y = 0; y < ElementsY; y++)
if(MEM(x, y, 0) & FLAG_FREE){
if(x < MinValX) MinValX = x;
if(y < MinValY) MinValY = y;
if(x > MaxValX) MaxValX = x;
if(y > MaxValY) MaxValY = y;
}
// pick 4 points in the found bounds and add new test points
if(MaxValX != 0 && MaxValY != 0)
for(Point1 = 0; Point1 < 2; Point1++)
for(Point2 = 0; Point2 < 2; Point2++){
NewPointX = (Point1 + 0.5f)*(MaxValX - MinValX)*0.5f + MinValX;
NewPointY = (Point2 + 0.5f)*(MaxValY - MinValY)*0.5f + MinValY;
if(MEM(NewPointX, NewPointY, 0) & FLAG_FREE){
aTestPointsX[NumTestPoints] = NewPointX;
aTestPointsY[NumTestPoints] = NewPointY;
aTestPointsZ[NumTestPoints] = 0;
NumTestPoints++;
}
}
// z = ElementsZ-1 plane
MinValX = 999999;
MinValY = 999999;
MaxValX = 0;
MaxValY = 0;
for(x = 0; x < ElementsX; x++)
for(y = 0; y < ElementsY; y++)
if(MEM(x, y, ElementsZ-1) & FLAG_FREE){
if(x < MinValX) MinValX = x;
if(y < MinValY) MinValY = y;
if(x > MaxValX) MaxValX = x;
if(y > MaxValY) MaxValY = y;
}
// pick 4 points in the found bounds and add new test points
if(MaxValX != 0 && MaxValY != 0)
for(Point1 = 0; Point1 < 2; Point1++)
for(Point2 = 0; Point2 < 2; Point2++){
NewPointX = (Point1 + 0.5f)*(MaxValX - MinValX)*0.5f + MinValX;
NewPointY = (Point2 + 0.5f)*(MaxValY - MinValY)*0.5f + MinValY;
if(MEM(NewPointX, NewPointY, ElementsZ-1) & FLAG_FREE){
aTestPointsX[NumTestPoints] = NewPointX;
aTestPointsY[NumTestPoints] = NewPointY;
aTestPointsZ[NumTestPoints] = ElementsZ-1;
NumTestPoints++;
}
}
// add some hardcoded test points
for(int i = 0; i < ARRAY_SIZE(ExtraFudgePointsCoors); i++)
if(PointFallsWithinZone(ExtraFudgePointsCoors[i], 0.0f)){
x = ElementsX * (ExtraFudgePointsCoors[i].x-minx)/(maxx-minx);
y = ElementsY * (ExtraFudgePointsCoors[i].y-miny)/(maxy-miny);
z = ElementsZ * (ExtraFudgePointsCoors[i].z-minz)/(maxz-minz);
if(MEM(x, y, z) & FLAG_FREE){
aTestPointsX[NumTestPoints] = x;
aTestPointsY[NumTestPoints] = y;
aTestPointsZ[NumTestPoints] = z;
NumTestPoints++;
}
}
// remove duplicate points
for(int i = 0; i < NumTestPoints; i++)
for(int j = i+1; j < NumTestPoints; j++)
if(aTestPointsX[j] == aTestPointsX[i] &&
aTestPointsY[j] == aTestPointsY[i] &&
aTestPointsZ[j] == aTestPointsZ[i]){
// get rid of [j]
for(int k = j; k < NumTestPoints-1; k++){
aTestPointsX[k] = aTestPointsX[k+1];
aTestPointsY[k] = aTestPointsY[k+1];
aTestPointsZ[k] = aTestPointsZ[k+1];
}
NumTestPoints--;
}
// convert points to floating point
for(int i = 0; i < NumTestPoints; i++){
aTestPoints[i].x = aTestPointsX[i]*StepX + minx;
aTestPoints[i].y = aTestPointsY[i]*StepY + miny;
aTestPoints[i].z = aTestPointsZ[i]*StepZ + minz;
}
CZNumber++;
delete[] pMem;
pMem = nil;
}
bool
CCullZone::TestEntityVisibilityFromCullZone(CEntity *entity, float extraDist, CEntity *LODentity)
{
CColModel *colmodel = entity->GetColModel();
float boundMaxX = colmodel->boundingBox.max.x;
float boundMaxY = colmodel->boundingBox.max.y;
float boundMaxZ = colmodel->boundingBox.max.z;
float boundMinX = colmodel->boundingBox.min.x;
float boundMinY = colmodel->boundingBox.min.y;
float boundMinZ = colmodel->boundingBox.min.z;
if(LODentity){
colmodel = LODentity->GetColModel();
boundMaxX = Max(boundMaxX, colmodel->boundingBox.max.x);
boundMaxY = Max(boundMaxY, colmodel->boundingBox.max.y);
boundMaxZ = Max(boundMaxZ, colmodel->boundingBox.max.z);
boundMinX = Min(boundMinX, colmodel->boundingBox.min.x);
boundMinY = Min(boundMinY, colmodel->boundingBox.min.y);
boundMinZ = Min(boundMinZ, colmodel->boundingBox.min.z);
}
if(boundMaxZ-boundMinZ + extraDist < 0.5f)
boundMaxZ = boundMinZ + 0.5f;
else
boundMaxZ += extraDist;
CVector vecMin = entity->GetMatrix() * CVector(boundMinX, boundMinY, boundMinZ);
CVector vecMaxX = entity->GetMatrix() * CVector(boundMaxX, boundMinY, boundMinZ);
CVector vecMaxY = entity->GetMatrix() * CVector(boundMinX, boundMaxY, boundMinZ);
CVector vecMaxZ = entity->GetMatrix() * CVector(boundMinX, boundMinY, boundMaxZ);
CVector dirx = vecMaxX - vecMin;
CVector diry = vecMaxY - vecMin;
CVector dirz = vecMaxZ - vecMin;
// If building intersects zone at all, it's visible
int x, y, z;
for(x = 0; x < 9; x++){
CVector posX = vecMin + x/8.0f*dirx;
for(y = 0; y < 9; y++){
CVector posY = posX + y/8.0f*diry;
for(z = 0; z < 9; z++){
CVector posZ = posY + z/8.0f*dirz;
if(PointFallsWithinZone(posZ, 2.0f))
return true;
}
}
}
float distToZone = CalcDistToCullZone(entity->GetPosition().x, entity->GetPosition().y)/15.0f;
distToZone = Max(distToZone, 7.0f);
int numX = (boundMaxX - boundMinX)/distToZone + 2.0f;
int numY = (boundMaxY - boundMinY)/distToZone + 2.0f;
int numZ = (boundMaxZ - boundMinZ)/distToZone + 2.0f;
float stepX = 1.0f/(numX-1);
float stepY = 1.0f/(numY-1);
float stepZ = 1.0f/(numZ-1);
float midX = (boundMaxX + boundMinX)/2.0f;
float midY = (boundMaxY + boundMinY)/2.0f;
float midZ = (boundMaxZ + boundMinZ)/2.0f;
// check both xy planes
for(int i = 0; i < NumTestPoints; i++){
CVector testPoint = aTestPoints[i];
CVector mid = entity->GetMatrix() * CVector(midX, midY, midZ);
mid.z += 0.1f;
if(DoThoroughLineTest(testPoint, mid, entity))
return true;
CVector ray = entity->GetPosition() - testPoint;
float dotX = DotProduct(ray, dirx);
float dotY = DotProduct(ray, diry);
float dotZ = DotProduct(ray, dirz);
for(x = 0; x < numX; x++){
CVector pMinZ = vecMin + x*stepX*dirx;
CVector pMaxZ = vecMin + x*stepX*dirx + dirz;
for(y = 0; y < numY; y++)
if(dotZ > 0.0f){
if(DoThoroughLineTest(testPoint, pMinZ + y*stepY*diry, entity))
return true;
}else{
if(DoThoroughLineTest(testPoint, pMaxZ + y*stepY*diry, entity))
return true;
}
}
for(x = 0; x < numX; x++){
CVector pMinY = vecMin + x*stepX*dirx;
CVector pMaxY = vecMin + x*stepX*dirx + diry;
for(z = 1; z < numZ-1; z++) // edge cases already handled
if(dotY > 0.0f){
if(DoThoroughLineTest(testPoint, pMinY + z*stepZ*dirz, entity))
return true;
}else{
if(DoThoroughLineTest(testPoint, pMaxY + z*stepZ*dirz, entity))
return true;
}
}
for(y = 1; y < numY-1; y++){ // edge cases already handled
CVector pMinX = vecMin + y*stepY*diry;
CVector pMaxX = vecMin + y*stepY*diry + dirx;
for(z = 1; z < numZ-1; z++) // edge cases already handled
if(dotX > 0.0f){
if(DoThoroughLineTest(testPoint, pMinX + z*stepZ*dirz, entity))
return true;
}else{
if(DoThoroughLineTest(testPoint, pMaxX + z*stepZ*dirz, entity))
return true;
}
}
}
return false;
}