qbsp: move FloodAreas to portals.cc for parity with qbsp3

This commit is contained in:
Eric Wasylishen 2022-06-27 01:32:34 -06:00
parent e660182c37
commit c7111cc99e
3 changed files with 236 additions and 233 deletions

View File

@ -61,4 +61,6 @@ void MakeTreePortals(tree_t *tree);
void FreeTreePortals_r(node_t *node);
void AssertNoPortals(node_t *node);
void MakeHeadnodePortals(tree_t *tree);
void EmitAreaPortals(node_t *headnode);
void FloodAreas(mapentity_t *entity, node_t *headnode);
void MarkVisibleSides(tree_t *tree, mapentity_t* entity);

View File

@ -26,6 +26,7 @@
#include <qbsp/map.hh>
#include <qbsp/brushbsp.hh>
#include <qbsp/qbsp.hh>
#include <qbsp/outside.hh>
#include <atomic>
@ -490,6 +491,239 @@ void FreeTreePortals_r(node_t *node)
node->portals = nullptr;
}
/*
=========================================================
FLOOD AREAS
=========================================================
*/
static void ApplyArea_r(node_t *node)
{
node->area = map.c_areas;
if (node->planenum != PLANENUM_LEAF) {
ApplyArea_r(node->children[0]);
ApplyArea_r(node->children[1]);
}
}
static mapentity_t *AreanodeEntityForLeaf(node_t *node)
{
// if detail cluster, search the children recursively
if (node->planenum != PLANENUM_LEAF) {
if (auto *child0result = AreanodeEntityForLeaf(node->children[0]); child0result) {
return child0result;
}
return AreanodeEntityForLeaf(node->children[1]);
}
for (auto &brush : node->original_brushes) {
if (brush->func_areaportal) {
return brush->func_areaportal;
}
}
return nullptr;
}
/*
=============
FloodAreas_r
=============
*/
static void FloodAreas_r(node_t *node)
{
if ((node->planenum == PLANENUM_LEAF || node->detail_separator) && (ClusterContents(node).native & Q2_CONTENTS_AREAPORTAL)) {
// grab the func_areanode entity
mapentity_t *entity = AreanodeEntityForLeaf(node);
if (entity == nullptr)
{
logging::print("WARNING: areaportal contents in node, but no entity found {} -> {}\n",
node->bounds.mins(),
node->bounds.maxs());
return;
}
// this node is part of an area portal;
// if the current area has allready touched this
// portal, we are done
if (entity->portalareas[0] == map.c_areas || entity->portalareas[1] == map.c_areas)
return;
// note the current area as bounding the portal
if (entity->portalareas[1]) {
logging::print("WARNING: areaportal entity {} touches > 2 areas\n Entity Bounds: {} -> {}\n",
entity - map.entities.data(), entity->bounds.mins(),
entity->bounds.maxs());
return;
}
if (entity->portalareas[0])
entity->portalareas[1] = map.c_areas;
else
entity->portalareas[0] = map.c_areas;
return;
}
if (node->area)
return; // already got it
node->area = map.c_areas;
// propagate area assignment to descendants if we're a cluster
if (!(node->planenum == PLANENUM_LEAF)) {
ApplyArea_r(node);
}
int32_t s;
for (portal_t *p = node->portals; p; p = p->next[s]) {
s = (p->nodes[1] == node);
#if 0
if (p->nodes[!s]->occupied)
continue;
#endif
if (!Portal_EntityFlood(p, s))
continue;
FloodAreas_r(p->nodes[!s]);
}
}
/*
=============
FindAreas_r
Just decend the tree, and for each node that hasn't had an
area set, flood fill out from there
=============
*/
static void FindAreas(node_t *node)
{
auto leafs = FindOccupiedClusters(node);
for (auto *leaf : leafs) {
if (leaf->area)
continue;
// area portals are always only flooded into, never
// out of
if (ClusterContents(leaf).native & Q2_CONTENTS_AREAPORTAL)
continue;
map.c_areas++;
FloodAreas_r(leaf);
}
}
/*
=============
SetAreaPortalAreas_r
Just decend the tree, and for each node that hasn't had an
area set, flood fill out from there
=============
*/
static void SetAreaPortalAreas_r(node_t *node)
{
if (node->planenum != PLANENUM_LEAF) {
SetAreaPortalAreas_r(node->children[0]);
SetAreaPortalAreas_r(node->children[1]);
return;
}
if (node->contents.native != Q2_CONTENTS_AREAPORTAL)
return;
if (node->area)
return; // already set
// grab the func_areanode entity
mapentity_t *entity = AreanodeEntityForLeaf(node);
if (!entity)
{
logging::print("WARNING: areaportal missing for node: {} -> {}\n",
node->bounds.mins(), node->bounds.maxs());
return;
}
node->area = entity->portalareas[0];
if (!entity->portalareas[1]) {
logging::print("WARNING: areaportal entity {} doesn't touch two areas\n Entity Bounds: {} -> {}\n",
entity - map.entities.data(),
entity->bounds.mins(), entity->bounds.maxs());
return;
}
}
/*
=============
EmitAreaPortals
=============
*/
void EmitAreaPortals(node_t *headnode)
{
logging::print(logging::flag::PROGRESS, "---- {} ----\n", __func__);
map.bsp.dareaportals.emplace_back();
map.bsp.dareas.emplace_back();
for (size_t i = 1; i <= map.c_areas; i++) {
darea_t &area = map.bsp.dareas.emplace_back();
area.firstareaportal = map.bsp.dareaportals.size();
for (auto &e : map.entities) {
if (!e.areaportalnum)
continue;
dareaportal_t dp = {};
if (e.portalareas[0] == i) {
dp.portalnum = e.areaportalnum;
dp.otherarea = e.portalareas[1];
} else if (e.portalareas[1] == i) {
dp.portalnum = e.areaportalnum;
dp.otherarea = e.portalareas[0];
}
size_t j = 0;
for (; j < map.bsp.dareaportals.size(); j++)
{
if (map.bsp.dareaportals[j] == dp)
break;
}
if (j == map.bsp.dareaportals.size())
map.bsp.dareaportals.push_back(dp);
}
area.numareaportals = map.bsp.dareaportals.size() - area.firstareaportal;
}
logging::print(logging::flag::STAT, "{:5} numareas\n", map.bsp.dareas.size());
logging::print(logging::flag::STAT, "{:5} numareaportals\n", map.bsp.dareaportals.size());
}
/*
=============
FloodAreas
Mark each leaf with an area, bounded by CONTENTS_AREAPORTAL
=============
*/
void FloodAreas(mapentity_t *entity, node_t *headnode)
{
logging::print(logging::flag::PROGRESS, "---- {} ----\n", __func__);
FindAreas(headnode);
SetAreaPortalAreas_r(headnode);
logging::print(logging::flag::STAT, "{:5} areas\n", map.c_areas);
}
//==============================================================
/*

View File

@ -456,239 +456,6 @@ static void ExportBrushList(mapentity_t *entity, node_t *node)
logging::print(logging::flag::STAT, " {:8} total leaf brushes\n", brush_state.total_leaf_brushes);
}
/*
=========================================================
FLOOD AREAS
=========================================================
*/
static void ApplyArea_r(node_t *node)
{
node->area = map.c_areas;
if (node->planenum != PLANENUM_LEAF) {
ApplyArea_r(node->children[0]);
ApplyArea_r(node->children[1]);
}
}
static mapentity_t *AreanodeEntityForLeaf(node_t *node)
{
// if detail cluster, search the children recursively
if (node->planenum != PLANENUM_LEAF) {
if (auto *child0result = AreanodeEntityForLeaf(node->children[0]); child0result) {
return child0result;
}
return AreanodeEntityForLeaf(node->children[1]);
}
for (auto &brush : node->original_brushes) {
if (brush->func_areaportal) {
return brush->func_areaportal;
}
}
return nullptr;
}
/*
=============
FloodAreas_r
=============
*/
static void FloodAreas_r(node_t *node)
{
if ((node->planenum == PLANENUM_LEAF || node->detail_separator) && (ClusterContents(node).native & Q2_CONTENTS_AREAPORTAL)) {
// grab the func_areanode entity
mapentity_t *entity = AreanodeEntityForLeaf(node);
if (entity == nullptr)
{
logging::print("WARNING: areaportal contents in node, but no entity found {} -> {}\n",
node->bounds.mins(),
node->bounds.maxs());
return;
}
// this node is part of an area portal;
// if the current area has allready touched this
// portal, we are done
if (entity->portalareas[0] == map.c_areas || entity->portalareas[1] == map.c_areas)
return;
// note the current area as bounding the portal
if (entity->portalareas[1]) {
logging::print("WARNING: areaportal entity {} touches > 2 areas\n Entity Bounds: {} -> {}\n",
entity - map.entities.data(), entity->bounds.mins(),
entity->bounds.maxs());
return;
}
if (entity->portalareas[0])
entity->portalareas[1] = map.c_areas;
else
entity->portalareas[0] = map.c_areas;
return;
}
if (node->area)
return; // already got it
node->area = map.c_areas;
// propagate area assignment to descendants if we're a cluster
if (!(node->planenum == PLANENUM_LEAF)) {
ApplyArea_r(node);
}
int32_t s;
for (portal_t *p = node->portals; p; p = p->next[s]) {
s = (p->nodes[1] == node);
#if 0
if (p->nodes[!s]->occupied)
continue;
#endif
if (!Portal_EntityFlood(p, s))
continue;
FloodAreas_r(p->nodes[!s]);
}
}
/*
=============
FindAreas_r
Just decend the tree, and for each node that hasn't had an
area set, flood fill out from there
=============
*/
static void FindAreas(node_t *node)
{
auto leafs = FindOccupiedClusters(node);
for (auto *leaf : leafs) {
if (leaf->area)
continue;
// area portals are always only flooded into, never
// out of
if (ClusterContents(leaf).native & Q2_CONTENTS_AREAPORTAL)
continue;
map.c_areas++;
FloodAreas_r(leaf);
}
}
/*
=============
SetAreaPortalAreas_r
Just decend the tree, and for each node that hasn't had an
area set, flood fill out from there
=============
*/
static void SetAreaPortalAreas_r(node_t *node)
{
if (node->planenum != PLANENUM_LEAF) {
SetAreaPortalAreas_r(node->children[0]);
SetAreaPortalAreas_r(node->children[1]);
return;
}
if (node->contents.native != Q2_CONTENTS_AREAPORTAL)
return;
if (node->area)
return; // already set
// grab the func_areanode entity
mapentity_t *entity = AreanodeEntityForLeaf(node);
if (!entity)
{
logging::print("WARNING: areaportal missing for node: {} -> {}\n",
node->bounds.mins(), node->bounds.maxs());
return;
}
node->area = entity->portalareas[0];
if (!entity->portalareas[1]) {
logging::print("WARNING: areaportal entity {} doesn't touch two areas\n Entity Bounds: {} -> {}\n",
entity - map.entities.data(),
entity->bounds.mins(), entity->bounds.maxs());
return;
}
}
/*
=============
FloodAreas
Mark each leaf with an area, bounded by CONTENTS_AREAPORTAL
=============
*/
static void FloodAreas(mapentity_t *entity, node_t *headnode)
{
logging::print(logging::flag::PROGRESS, "---- {} ----\n", __func__);
FindAreas(headnode);
SetAreaPortalAreas_r(headnode);
logging::print(logging::flag::STAT, "{:5} areas\n", map.c_areas);
}
/*
=============
EmitAreaPortals
=============
*/
static void EmitAreaPortals(node_t *headnode)
{
logging::print(logging::flag::PROGRESS, "---- {} ----\n", __func__);
map.bsp.dareaportals.emplace_back();
map.bsp.dareas.emplace_back();
for (size_t i = 1; i <= map.c_areas; i++) {
darea_t &area = map.bsp.dareas.emplace_back();
area.firstareaportal = map.bsp.dareaportals.size();
for (auto &e : map.entities) {
if (!e.areaportalnum)
continue;
dareaportal_t dp = {};
if (e.portalareas[0] == i) {
dp.portalnum = e.areaportalnum;
dp.otherarea = e.portalareas[1];
} else if (e.portalareas[1] == i) {
dp.portalnum = e.areaportalnum;
dp.otherarea = e.portalareas[0];
}
size_t j = 0;
for (; j < map.bsp.dareaportals.size(); j++)
{
if (map.bsp.dareaportals[j] == dp)
break;
}
if (j == map.bsp.dareaportals.size())
map.bsp.dareaportals.push_back(dp);
}
area.numareaportals = map.bsp.dareaportals.size() - area.firstareaportal;
}
logging::print(logging::flag::STAT, "{:5} numareas\n", map.bsp.dareas.size());
logging::print(logging::flag::STAT, "{:5} numareaportals\n", map.bsp.dareaportals.size());
}
winding_t BaseWindingForPlane(const qplane3d &p)
{
return winding_t::from_plane(p, options.worldextent.value());