Files

222 lines
8.3 KiB
C++

#include "gamelib/aas2/aas2_local.h"
#include "gamelib/physics/clipmodel.h"
#include <algorithm>
#include <cstring>
#include <vector>
namespace {
constexpr int MAX_OBSTACLE_VERTICES = 0x300;
constexpr int MAX_OBSTACLE_EDGES = 0x200;
constexpr int MAX_OBSTACLE_MODELS = 128;
bool PVSIncludes(const std::uint8_t* const pvs, const int areaNum) {
return pvs != nullptr && areaNum >= 0
&& (pvs[areaNum >> 3] & (1u << (areaNum & 7))) != 0;
}
} // namespace
void idAAS2Local::ShutdownObstaclePVS() {
FreeClipLinks();
delete[] obstaclePVS;
delete[] areaClipModels;
numObstaclePVSBytes = 0;
obstaclePVS = nullptr;
areaClipModels = nullptr;
obstaclePVSAreaNum = 0;
}
int idAAS2Local::GetObstaclePVSWallEdges(const int areaNum,
const int edgeFlags, int* const edges, const int maxEdges) const {
return file != nullptr ? file->GetObstaclePVSWallEdges(areaNum,
edgeFlags, edges, maxEdges) : 0;
}
void idAAS2Local::SetupObstaclePVS() {
ShutdownObstaclePVS();
if (file == nullptr || file->areas.IsEmpty()) return;
numObstaclePVSBytes = (file->areas.Num() + 7) / 8;
obstaclePVS = new std::uint8_t[numObstaclePVSBytes];
std::memset(obstaclePVS, 0, numObstaclePVSBytes);
areaClipModels = new idAASClipLink*[file->areas.Num()]();
}
void idAAS2Local::DecompressObstaclePVS(const int areaNum,
std::uint8_t* const pvs, const int pvsSize) const {
if (pvs == nullptr || pvsSize <= 0) return;
std::memset(pvs, 0, pvsSize);
if (file == nullptr || areaNum <= 0 || areaNum >= file->areas.Num())
return;
std::uint32_t offset = file->areas[areaNum].obstaclePVSOffset;
int visibleArea = 0;
while (offset < static_cast<std::uint32_t>(file->obstaclePVS.Num())
&& visibleArea < file->areas.Num()) {
const std::uint8_t encoded = file->obstaclePVS[offset++];
if ((encoded & 0x80u) != 0) {
int skip = encoded & 0x3Fu;
if ((encoded & 0x40u) != 0
&& offset < static_cast<std::uint32_t>(
file->obstaclePVS.Num())) {
skip |= static_cast<int>(file->obstaclePVS[offset++]) << 6;
}
visibleArea += skip + 1;
continue;
}
for (int bit = 0; bit < 7 && visibleArea < file->areas.Num();
++bit, ++visibleArea) {
if ((encoded & (1u << bit)) != 0
&& (visibleArea >> 3) < pvsSize) {
pvs[visibleArea >> 3] |= static_cast<std::uint8_t>(
1u << (visibleArea & 7));
}
}
}
}
const std::uint8_t* idAAS2Local::GetObstaclePVS(const int areaNum) {
if (obstaclePVS == nullptr) return nullptr;
if (areaNum != obstaclePVSAreaNum) {
DecompressObstaclePVS(areaNum, obstaclePVS, numObstaclePVSBytes);
obstaclePVSAreaNum = areaNum;
}
return obstaclePVS;
}
void idAAS2Local::FreeClipLinks() {
if (areaClipModels != nullptr && file != nullptr) {
for (int areaNum = 0; areaNum < file->areas.Num(); ++areaNum)
areaClipModels[areaNum] = nullptr;
}
clipModelLinkAllocator.Shutdown();
}
idAASClipLink* idAAS2Local::UnLinkClipModel(idAASClipLink* link) {
while (link != nullptr) {
idAASClipLink* const nextArea = link->nextArea;
if (link->prevClip != nullptr)
link->prevClip->nextClip = link->nextClip;
else if (areaClipModels != nullptr && link->areaNum >= 0)
areaClipModels[link->areaNum] = link->nextClip;
if (link->nextClip != nullptr)
link->nextClip->prevClip = link->prevClip;
clipModelLinkAllocator.Free(link);
link = nextArea;
}
return nullptr;
}
idAASClipLink* idAAS2Local::LinkClipModel(idClipModel* const model) {
if (model == nullptr || file == nullptr || areaClipModels == nullptr)
return nullptr;
idBounds expanded = model->GetAbsBounds();
expanded[0].x -= file->settings.boundingBox[1].x;
expanded[0].y -= file->settings.boundingBox[1].y;
expanded[0].z -= file->settings.boundingBox[1].z;
expanded[1].x -= file->settings.boundingBox[0].x;
expanded[1].y -= file->settings.boundingBox[0].y;
expanded[1].z -= file->settings.boundingBox[0].z;
std::vector<int> areas;
int areaBuffer[128];
for (int tree = 0; tree < file->trees.Num(); ++tree) {
const int count = file->BoundsAreaNums(tree, expanded,
areaBuffer, 128);
for (int index = 0; index < count; ++index) {
const int areaNum = areaBuffer[index];
if (areaNum > 0 && areaNum < file->areas.Num()
&& std::find(areas.begin(), areas.end(), areaNum)
== areas.end()) areas.push_back(areaNum);
}
}
std::sort(areas.begin(), areas.end(), std::greater<int>());
idAASClipLink* first = nullptr;
for (const int areaNum : areas) {
idAASClipLink* const link = clipModelLinkAllocator.Alloc();
if (link == nullptr) break;
link->clipModel = model;
link->areaNum = areaNum;
link->prevArea = nullptr;
link->nextArea = first;
if (first != nullptr) first->prevArea = link;
first = link;
link->prevClip = nullptr;
link->nextClip = areaClipModels[areaNum];
if (link->nextClip != nullptr) link->nextClip->prevClip = link;
areaClipModels[areaNum] = link;
}
return first;
}
int idAAS2Local::GetObstaclePVSClipModels(const int areaNum,
idClipModel** const models, const int maxModels) {
if (models == nullptr || maxModels <= 0 || areaClipModels == nullptr)
return 0;
const std::uint8_t* const pvs = GetObstaclePVS(areaNum);
if (pvs == nullptr) return 0;
int count = 0;
for (int visibleArea = 1; visibleArea < file->areas.Num()
&& count < maxModels; ++visibleArea) {
if (!PVSIncludes(pvs, visibleArea)) continue;
for (idAASClipLink* link = areaClipModels[visibleArea];
link != nullptr && count < maxModels; link = link->nextClip) {
bool duplicate = false;
for (int index = 0; index < count; ++index)
if (models[index] == link->clipModel) duplicate = true;
if (!duplicate) models[count++] = link->clipModel;
}
}
return count;
}
void idAAS2Local::GetObstaclePVSObstaclesInternal(const int areaNum,
const int edgeFlags, obstacleVertex_t* const vertices,
int& numVertices, obstacleEdge_t* const edges, int& numEdges,
idClipModel** const models, int& numModels,
obstacleRoute_t* const route) const {
numVertices = 0;
numEdges = 0;
numModels = 0;
if (file == nullptr) return;
int wallEdges[MAX_OBSTACLE_EDGES];
const int wallCount = file->GetObstaclePVSWallEdges(areaNum,
edgeFlags, wallEdges, MAX_OBSTACLE_EDGES);
for (int index = 0; index < wallCount
&& numVertices + 2 <= MAX_OBSTACLE_VERTICES
&& numEdges < MAX_OBSTACLE_EDGES; ++index) {
idVec3 start;
idVec3 end;
GetEdge(wallEdges[index], start, end);
vertices[numVertices].position = start;
vertices[numVertices + 1].position = end;
obstacleEdge_t& edge = edges[numEdges++];
edge.flags = GetEdgeFlags(wallEdges[index]);
edge.verts[0] = static_cast<std::int16_t>(numVertices);
edge.verts[1] = static_cast<std::int16_t>(numVertices + 1);
edge.vertNums[0] = numVertices;
edge.vertNums[1] = numVertices + 1;
numVertices += 2;
}
numModels = const_cast<idAAS2Local*>(this)->GetObstaclePVSClipModels(
areaNum, models, MAX_OBSTACLE_MODELS);
if (route != nullptr && route->numAreas == 0) {
route->numAreas = 1;
route->areas[0].areaNum = areaNum;
route->areas[0].start = AreaCenter(areaNum);
route->areas[0].end = route->areas[0].start;
}
}
void idAAS2Local::GetObstaclePVSObstacles(const int startAreaNum,
const int edgeFlags, obstacleVertex_t* const vertices,
int* const numVertices, obstacleEdge_t* const edges,
int* const numEdges, idClipModel** const models,
int* const numModels, obstacleRoute_t* const route) {
if (numVertices == nullptr || numEdges == nullptr
|| numModels == nullptr) return;
GetObstaclePVSObstaclesInternal(startAreaNum, edgeFlags, vertices,
*numVertices, edges, *numEdges, models, *numModels, route);
}