light: remove hack which disable vis culling inside liquids

instead, implement the equivalent by bridging the PVS across opaque liquid faces
This commit is contained in:
Eric Wasylishen 2023-01-09 01:40:01 -07:00
parent 6b707b6d5a
commit 64d6cbad4f
2 changed files with 73 additions and 15 deletions

View File

@ -655,6 +655,35 @@ void DecompressVis(const uint8_t *in, const uint8_t *inend, uint8_t *out, uint8_
}
}
/**
* Updates the visdata so everything that:
* everything that can see into cluster a, can also see into cluster b + its pvs
*/
static void ConnectLeafVisibilityDirectional(const mbsp_t *bsp, std::unordered_map<int, std::vector<uint8_t>> &all_visdata, int a, int b)
{
Q_assert(bsp->loadversion->game->id == GAME_QUAKE_II);
if (a < 0)
return;
if (b < 0)
return;
const std::vector<uint8_t> b_pvs_copy = all_visdata.at(b);
for (auto &[cluster, pvs] : all_visdata) {
// can cluster see into `a`?
if (pvs[a >> 3] & (1 << (a & 7))) {
// update `pvs` to see into `b`
pvs[b >> 3] |= (1 << (b & 7));
// update `pvs` to see into b's pvs
for (int i = 0; i < pvs.size(); ++i) {
pvs[i] |= b_pvs_copy[i];
}
}
}
}
/**
* Decompress visdata for the entire map, and returns a map of:
*
@ -666,6 +695,8 @@ void DecompressVis(const uint8_t *in, const uint8_t *inend, uint8_t *out, uint8_
*/
std::unordered_map<int, std::vector<uint8_t>> DecompressAllVis(const mbsp_t *bsp, bool trans_water)
{
logging::funcheader();
std::unordered_map<int, std::vector<uint8_t>> result;
const size_t decompressed_size = DecompressedVisSize(bsp);
@ -710,6 +741,47 @@ std::unordered_map<int, std::vector<uint8_t>> DecompressAllVis(const mbsp_t *bsp
}
}
if (trans_water && bsp->loadversion->game->id == GAME_QUAKE_II) {
// FIXME: implement non q2 path
// we want to make all visblocking liquids transparent
std::set<std::pair<int, int>> cluster_pairs_to_unify;
const auto &world = bsp->dmodels[0];
for (int i = world.firstface; i < (world.firstface + world.numfaces); ++i) {
const mface_t &face = bsp->dfaces[i];
qvec3f centroid = Face_Centroid(bsp, &face);
auto plane = Face_Plane(bsp, &face);
auto *top = BSP_FindLeafAtPoint(bsp, &world, centroid + (plane.normal * 0.5));
auto *bottom = BSP_FindLeafAtPoint(bsp, &world, centroid - (plane.normal * 0.5));
contentflags_t top_contents{top->contents};
contentflags_t bottom_contents{bottom->contents};
// this weakens the pvs culling effectiveness, so only do it if top and bottom can't see each other
// FIXME: would be better to do a PVS check (especially for Q1)
if (Face_IsTranslucent(bsp, &face)) {
continue;
}
if (top_contents.is_empty(bsp->loadversion->game) && bottom_contents.is_liquid(bsp->loadversion->game)) {
cluster_pairs_to_unify.insert(std::make_pair(top->cluster, bottom->cluster));
}
}
if (cluster_pairs_to_unify.size()) {
logging::print("{:9} cluster pairs PVS connected to make liquid translucent\n", cluster_pairs_to_unify.size());
}
for (auto [top, bottom] : cluster_pairs_to_unify) {
ConnectLeafVisibilityDirectional(bsp, result, top, bottom);
ConnectLeafVisibilityDirectional(bsp, result, bottom, top);
}
}
return result;
}

View File

@ -513,16 +513,6 @@ static void CalcPvs(const mbsp_t *bsp, lightsurf_t *lightsurf)
/* copy the pvs for this leaf into pointpvs */
Mod_LeafPvs(bsp, leaf, pointpvs);
if (bsp->loadversion->game->contents_are_liquid({leaf->contents})) {
// hack for when the sample point might be in an opaque liquid, blocking vis,
// but we typically want light to pass through these.
// see also VisCullEntity() which handles the case when the light emitter is in liquid.
for (int j = 0; j < pvssize; j++) {
lightsurf->pvs[j] |= 0xff;
}
break;
}
/* merge the pvs for this sample point into lightsurf->pvs */
for (int j = 0; j < pvssize; j++) {
lightsurf->pvs[j] |= pointpvs[j];
@ -1090,11 +1080,7 @@ static bool VisCullEntity(const mbsp_t *bsp, const std::vector<uint8_t> &pvs, co
}
if (bsp->loadversion->game->contents_are_solid({entleaf->contents}) ||
bsp->loadversion->game->contents_are_sky({entleaf->contents}) ||
bsp->loadversion->game->contents_are_liquid({entleaf->contents})) {
// the liquid case is because entleaf->contents might be in an opaque liquid,
// which we typically want light to pass through, but visdata would report that
// there's no visibility across the opaque liquid. so, skip culling and do the raytracing.
bsp->loadversion->game->contents_are_sky({entleaf->contents})) {
return false;
}