Files
scummvm-cursorfix/engines/hpl1/engine/scene/SectorVisibility.cpp
2026-02-02 04:50:13 +01:00

420 lines
12 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Copyright (C) 2006-2010 - Frictional Games
*
* This file is part of HPL1 Engine.
*/
#include "hpl1/engine/scene/SectorVisibility.h"
#include "hpl1/engine/math/Math.h"
#include "hpl1/engine/scene/PortalContainer.h"
#include "hpl1/engine/system/low_level_system.h"
namespace hpl {
//////////////////////////////////////////////////////////////////////////
// PORTAL VISIBILTY SET
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
cPortalVisibility::cPortalVisibility() {
}
//-----------------------------------------------------------------------
cPortalVisibility::~cPortalVisibility() {
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// PORTAL VISIBILTY SET
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
cPortalVisibilitySet::cPortalVisibilitySet(cSectorVisibilityContainer *apContainer,
cPortalVisibilitySet *apParent) {
mpParent = apParent;
mpContainer = apContainer;
}
cPortalVisibilitySet::~cPortalVisibilitySet() {
STLDeleteAll(mvVisibility);
}
//-----------------------------------------------------------------------
int cPortalVisibilitySet::AddPortalVisibility(cPortal *apPortal) {
mvVisibility.push_back(hplNew(cPortalVisibility, ()));
size_t lIdx = mvVisibility.size() - 1;
mvVisibility[lIdx]->mpPortal = apPortal;
// Calculate the shadow volume, range is not really need. Just set a high value.
cShadowVolumeBV *pShadow = apPortal->GetBV()->GetShadowVolume(mpContainer->GetOrigin(), 9999.0f, true);
if (pShadow) {
mvVisibility[lIdx]->mShadow = *pShadow;
mvVisibility[lIdx]->mbNullShadow = false;
} else {
mvVisibility[lIdx]->mbNullShadow = true;
}
return (int)lIdx;
}
//-----------------------------------------------------------------------
bool cPortalVisibilitySet::PortalExists(cPortal *apPortal) {
for (size_t i = 0; i < mvVisibility.size(); ++i) {
if (mvVisibility[i]->mpPortal == apPortal)
return true;
}
return false;
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// SECTOR VISIBILTY
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
cSectorVisibility::cSectorVisibility(cSectorVisibilityContainer *apContainer) {
mpSector = NULL;
bStart = false;
mpContainer = apContainer;
}
cSectorVisibility::~cSectorVisibility() {
}
//-----------------------------------------------------------------------
cPortalVisibilitySet *cSectorVisibility::GetSetConnectingFromSector(cSector *apSector) {
for (size_t i = 0; i < mvVisibiltySets.size(); ++i) {
if (mvVisibiltySets[i]->GetVisibility(0)->mpPortal->GetSector() == apSector) {
return mvVisibiltySets[i];
}
}
return NULL;
}
//-----------------------------------------------------------------------
void cSectorVisibility::AddVisibilitySet(cPortalVisibilitySet *apSet) {
mvVisibiltySets.push_back(apSet);
}
//-----------------------------------------------------------------------
bool cSectorVisibility::PortalExists(cPortal *apPortal) {
for (size_t i = 0; i < mvVisibiltySets.size(); ++i) {
if (mvVisibiltySets[i]->PortalExists(apPortal))
return true;
}
return false;
}
//-----------------------------------------------------------------------
bool cSectorVisibility::IntersectionBV(cBoundingVolume *apBV) {
if (mvVisibiltySets.empty()) {
// Log("Checking start sector %s\n",mpSector->GetId().c_str());
return mpContainer->IntersectionBV(apBV, NULL);
} else {
// Log("Checking sector %s with %d sets\n", mpSector->GetId().c_str(), mvVisibiltySets.size());
for (size_t i = 0; i < mvVisibiltySets.size(); ++i) {
if (mpContainer->IntersectionBV(apBV, mvVisibiltySets[i])) {
return true;
}
}
return false;
}
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// SECTOR VISIBILTY CONTAINER
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
cSectorVisibilityContainer::cSectorVisibilityContainer(eSectorVisibilityType aType) {
mType = aType;
mbLog = false;
mlTabs = 0;
}
cSectorVisibilityContainer::~cSectorVisibilityContainer() {
STLMapDeleteAll(m_mapSectors);
STLDeleteAll(mlstPortalVisibilty);
}
//-----------------------------------------------------------------------
cPortalVisibilitySet *cSectorVisibilityContainer::CreatePortalVisibiltySet(cPortalVisibilitySet *apParent) {
cPortalVisibilitySet *pSet = hplNew(cPortalVisibilitySet, (this, apParent));
// Add to visibility list.
mlstPortalVisibilty.push_back(pSet);
return pSet;
}
//-----------------------------------------------------------------------
cSectorVisibility *cSectorVisibilityContainer::GetSectorVisibilty(cSector *apSector) {
// Check if the sector has already been checked.
tSectorVisibilityMapIt it = m_mapSectors.find(apSector);
// The sector has not been added, create visibility and add.
if (it == m_mapSectors.end()) {
if (mbLog)
Log("%sCreating Visibility sector for '%s'!\n", GetTabs().c_str(), apSector->GetId().c_str());
cSectorVisibility *pVisSector = hplNew(cSectorVisibility, (this));
pVisSector->mpSector = apSector;
m_mapSectors.insert(tSectorVisibilityMap::value_type(apSector, pVisSector));
return pVisSector;
}
// The sector exists, return it.
else {
if (mbLog)
Log("%sVisibility sector for '%s' already exist!\n", GetTabs().c_str(), apSector->GetId().c_str());
return it->second;
}
}
//-----------------------------------------------------------------------
void cSectorVisibilityContainer::Compute(cPortalContainer *apContainer) {
/////////////////////////////////////
// Check what sectors the type starts in
// Check the portals in these
tSectorMap *pSectorMap = apContainer->GetSectorMap();
if (mbLog)
Log("Checking for start sectors\n");
// Clear start sectors
m_setStartSectors.clear();
// Get the origin.
if (mType == eSectorVisibilityType_BV)
mvOrigin = mBoundingVolume.GetPosition();
else if (mType == eSectorVisibilityType_Frustum)
mvOrigin = mFrustum.GetOrigin();
///////////////////////////////////
// Check what start start sectors are
tSectorMapIt it = pSectorMap->begin();
for (; it != pSectorMap->end(); ++it) {
cSector *pSector = it->second;
if (cMath::PointBVCollision(mvOrigin, *pSector->GetBV())) {
if (mbLog)
Log("Sector '%s' is a start!\n", pSector->GetId().c_str());
m_setStartSectors.insert(pSector);
}
}
/////////////////////////////////
// Iterate the start sectors
tSectorSetIt startIt = m_setStartSectors.begin();
for (; startIt != m_setStartSectors.end(); ++startIt) {
cSector *pSector = *startIt;
SearchSector(pSector, NULL, 0);
}
if (mbLog)
Log("Done checking start sectors!\n");
}
tSectorVisibilityIterator cSectorVisibilityContainer::GetSectorIterator() {
return tSectorVisibilityIterator(&m_mapSectors);
}
//-----------------------------------------------------------------------
void cSectorVisibilityContainer::SearchSector(cSector *apSector, cPortalVisibilitySet *apParentSet,
int alPortalIndex) {
if (mbLog) {
Log("%s--- Searching sector %s\n%s---------------------------------------\n",
GetTabs().c_str(),
apSector->GetId().c_str(),
GetTabs().c_str());
mlTabs++;
}
cSectorVisibility *pVisSector = GetSectorVisibilty(apSector);
// Save all portals encountered here.
tPortalList lstNewPortals;
//////////////////////////////////
// Go through all portals and see which
tPortalList *pPortalList = NULL;
// Get the portals to search
if (apParentSet) {
if (mbLog)
Log("%sSearching portals from parent portal %d with index %d\n", GetTabs().c_str(), apParentSet->GetVisibility(alPortalIndex)->mpPortal->GetId(), alPortalIndex);
pPortalList = apParentSet->GetVisibility(alPortalIndex)->mpPortal->GetPortalList();
} else {
if (mbLog)
Log("%sNo parent set, searching all portals.\n", GetTabs().c_str());
pPortalList = apSector->GetPortalList();
}
// Iterate the portals
tPortalListIt it = pPortalList->begin();
for (; it != pPortalList->end(); ++it) {
cPortal *pPortal = *it;
cSector *pTargetSector = pPortal->GetTargetSector();
// Check if it is a start sector
if (m_setStartSectors.find(pTargetSector) != m_setStartSectors.end()) {
continue;
}
/////////////////////////////////////////
// Check that the portal that it is intersected and does not already exist
if (pVisSector->PortalExists(pPortal) == false &&
IntersectionBV(pPortal->GetBV(), apParentSet) &&
pPortal->GetActive()) {
if (mbLog)
Log("%sFound new valid portal %d\n", GetTabs().c_str(), pPortal->GetId());
///////////////////////////////////////////////////////
// Check if the portal is facing the right direction
if (cMath::PlaneToPointDist(pPortal->GetPlane(), mvOrigin) < 0.0f) {
continue;
}
cSectorVisibility *pTargetVisSector = GetSectorVisibilty(pTargetSector);
////////////////////////////////////////
// Check if there is another visibility set connecting to the same room
cPortalVisibilitySet *pSet = pTargetVisSector->GetSetConnectingFromSector(pTargetSector);
// If none exist, create new set.
if (pSet == NULL) {
// Create portal visibility and add it to the sector container
pSet = CreatePortalVisibiltySet(apParentSet);
// Add to the specific container
pTargetVisSector->AddVisibilitySet(pSet);
if (mbLog)
Log("%sNo portal connecting from %s to %s, creating new visibility set\n",
GetTabs().c_str(),
apSector->GetId().c_str(),
pTargetSector->GetId().c_str());
}
int lIdx = pSet->AddPortalVisibility(pPortal);
SearchSector(pTargetSector, pSet, lIdx);
} else {
if (mbLog)
Log("%sSkipped unvalid portal %d\n", GetTabs().c_str(), pPortal->GetId());
}
}
if (mbLog) {
mlTabs--;
Log("%s------------------------------------\n%s--- Done searching sector %s!\n",
GetTabs().c_str(),
GetTabs().c_str(),
apSector->GetId().c_str());
}
}
//-----------------------------------------------------------------------
bool cSectorVisibilityContainer::IntersectionBV(cBoundingVolume *apBV, cPortalVisibilitySet *pVisibilitySet) {
///////////////////////////////////////////
// First check with the portal visibility
if (pVisibilitySet) {
cPortalVisibilitySet *pSet = pVisibilitySet;
while (pSet) {
bool bIntersection = false;
size_t lVisibilityNum = pSet->GetVisibilityNum();
for (size_t i = 0; i < lVisibilityNum; ++i) {
if (pSet->GetVisibility(i)->mbNullShadow ||
pSet->GetVisibility(i)->mShadow.CollideBoundingVolume(apBV)) {
bIntersection = true;
break;
}
}
if (bIntersection == false)
return false;
pSet = pSet->GetParent();
}
}
//////////////////////////////////////////
// Check with the type
// BV:
if (mType == eSectorVisibilityType_BV) {
return cMath::CheckCollisionBV(mBoundingVolume, *apBV);
}
// Frustum:
else {
return mFrustum.CollideBoundingVolume(apBV) != eFrustumCollision_Outside;
}
}
//-----------------------------------------------------------------------
tString cSectorVisibilityContainer::GetTabs() {
tString sTabs = "";
for (int i = 0; i < mlTabs; ++i)
sTabs += " ";
return sTabs;
}
//-----------------------------------------------------------------------
} // namespace hpl