diff --git a/common/bsputils.cc b/common/bsputils.cc index 80957b6c..92bd7466 100644 --- a/common/bsputils.cc +++ b/common/bsputils.cc @@ -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> &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 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> DecompressAllVis(const mbsp_t *bsp, bool trans_water) { + logging::funcheader(); + std::unordered_map> result; const size_t decompressed_size = DecompressedVisSize(bsp); @@ -710,6 +741,47 @@ std::unordered_map> 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> 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; } diff --git a/light/ltface.cc b/light/ltface.cc index 14bc7d5f..92a3ea8d 100644 --- a/light/ltface.cc +++ b/light/ltface.cc @@ -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 &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; }