diff --git a/qbsp/portals.cc b/qbsp/portals.cc index f7f7ae87..002f6f9d 100644 --- a/qbsp/portals.cc +++ b/qbsp/portals.cc @@ -252,6 +252,16 @@ static void WritePortalfile(node_t *headnode, portal_state_t *state) if (!portalFile) FError("Failed to open {}: {}", options.szBSPName, strerror(errno)); + // q2 uses a PRT1 file, but with clusters. + // (Since q2bsp natively supports clusters, we don't need PRT2.) + if (options.target_game->id == GAME_QUAKE_II) { + fmt::print(portalFile, "PRT1\n"); + fmt::print(portalFile, "{}\n", state->num_visclusters); + fmt::print(portalFile, "{}\n", state->num_visportals); + WritePortals_r(headnode, portalFile, true); + return; + } + /* If no detail clusters, just use a normal PRT1 format */ if (!state->uses_detail) { fmt::print(portalFile, "PRT1\n"); diff --git a/vis/vis.cc b/vis/vis.cc index e8a14592..994a3e3e 100644 --- a/vis/vis.cc +++ b/vis/vis.cc @@ -16,8 +16,8 @@ */ int numportals; int portalleafs; /* leafs (PRT1) or clusters (PRT2) */ -int portalleafs_real; /* real no. of leafs after expanding PRT2 clusters */ -int *clustermap; /* mapping from real leaf to cluster number */ +int portalleafs_real; /* real no. of leafs after expanding PRT2 clusters. Not used for Q2. */ +int *clustermap; /* mapping from real leaf to cluster number. Not used for Q2. */ portal_t *portals; leaf_t *leafs; @@ -39,7 +39,7 @@ uint8_t *uncompressed_q2; // [leafbytes*portalleafs] int leafbytes; // (portalleafs+63)>>3 int leaflongs; -int leafbytes_real; // (portalleafs_real+63)>>3 +int leafbytes_real; // (portalleafs_real+63)>>3, not used for Q2. /* Options - TODO: collect these in a struct */ bool fastvis; @@ -659,11 +659,16 @@ static void ClusterFlow(int clusternum, leafbits_t *buffer, const mbsp_t *bsp) * increment totalvis by * (# of real leafs in this cluster) x (# of real leafs visible from this cluster) */ + if (bsp->loadversion->game->id == GAME_QUAKE_II) { + // FIXME: not sure what this is supposed to be? + totalvis += numvis; + } else { for (i = 0; i < portalleafs_real; i++) { if (clustermap[i] == clusternum) { totalvis += numvis; } } + } /* Allocate for worst case where RLE might grow the data (unlikely) */ if (bsp->loadversion->game->id == GAME_QUAKE_II) { @@ -684,9 +689,9 @@ static void ClusterFlow(int clusternum, leafbits_t *buffer, const mbsp_t *bsp) leaf->visofs = dest - vismap; // Set pointers - for (i = 0; i < portalleafs_real; i++) { - if (bsp->dleafs[i + 1].cluster == clusternum) { - bsp->dleafs[i + 1].visofs = leaf->visofs; + for (i = 1; i < bsp->numleafs; i++) { + if (bsp->dleafs[i].cluster == clusternum) { + bsp->dleafs[i].visofs = leaf->visofs; } } @@ -753,7 +758,10 @@ void CalcVis(const mbsp_t *bsp) // // assemble the leaf vis lists by oring and compressing the portal lists // - if (portalleafs == portalleafs_real) { + if (portalleafs == portalleafs_real && bsp->loadversion->game->id != GAME_QUAKE_II) { + // Legacy, non-detail Q1 vis codepath + // FIXME: Should be possible to remove this and just use ClusterFlow even on Q1 maps + // with no detail. for (i = 0; i < portalleafs; i++) LeafFlow(i, &bsp->dleafs[i + 1], bsp); } else { @@ -768,9 +776,15 @@ void CalcVis(const mbsp_t *bsp) int64_t avg = totalvis; - avg /= static_cast(portalleafs_real); + if (bsp->loadversion->game->id == GAME_QUAKE_II) { + avg /= static_cast(portalleafs); - LogPrint("average leafs visible: {}\n", avg); + LogPrint("average clusters visible: {}\n", avg); + } else { + avg /= static_cast(portalleafs_real); + + LogPrint("average leafs visible: {}\n", avg); + } } /* @@ -1023,8 +1037,8 @@ static void LoadPortals(const std::filesystem::path &name, mbsp_t *bsp) FError("unable to parse {} HEADER\n", PORTALFILE); if (bsp->loadversion->game->id == GAME_QUAKE_II) { - portalleafs_real = bsp->numleafs; - LogPrint("{:6} leafs\n", portalleafs_real); + // since q2bsp has native cluster support, we shouldn't look at portalleafs_real at all. + portalleafs_real = 0; LogPrint("{:6} clusters\n", portalleafs); LogPrint("{:6} portals\n", numportals); } else { @@ -1036,6 +1050,9 @@ static void LoadPortals(const std::filesystem::path &name, mbsp_t *bsp) count = fscanf(f.get(), "%i\n%i\n%i\n", &portalleafs_real, &portalleafs, &numportals); if (count != 3) FError("unable to parse {} HEADER\n", PORTALFILE); + if (bsp->loadversion->game->id == GAME_QUAKE_II) { + FError("%s can not be used with Q2\n", PORTALFILE2); + } LogPrint("{:6} leafs\n", portalleafs_real); LogPrint("{:6} clusters\n", portalleafs); LogPrint("{:6} portals\n", numportals); @@ -1043,6 +1060,9 @@ static void LoadPortals(const std::filesystem::path &name, mbsp_t *bsp) count = fscanf(f.get(), "%i\n%i\n%i\n", &portalleafs, &numportals, &portalleafs_real); if (count != 3) FError("unable to parse {} HEADER\n", PORTALFILE); + if (bsp->loadversion->game->id == GAME_QUAKE_II) { + Error("%s: %s can not be used with Q2\n", __func__, PORTALFILEAM); + } LogPrint("{:6} leafs\n", portalleafs_real); LogPrint("{:6} clusters\n", portalleafs); LogPrint("{:6} portals\n", numportals); @@ -1052,7 +1072,12 @@ static void LoadPortals(const std::filesystem::path &name, mbsp_t *bsp) leafbytes = ((portalleafs + 63) & ~63) >> 3; leaflongs = leafbytes / sizeof(long); + if (bsp->loadversion->game->id == GAME_QUAKE_II) { + // not used in Q2 + leafbytes_real = 0; + } else { leafbytes_real = ((portalleafs_real + 63) & ~63) >> 3; + } // each file portal is split into two memory portals portals = new portal_t[numportals * 2] { }; @@ -1129,13 +1154,10 @@ static void LoadPortals(const std::filesystem::path &name, mbsp_t *bsp) } /* Load the cluster expansion map if needed */ - /*if (bsp->loadversion->game->id == GAME_QUAKE_II) { - clustermap = new int[bsp->numleafs]; - - for (int32_t i = 0; i < bsp->numleafs; i++) { - clustermap[i] = bsp->dleafs[i + 1].cluster; - } - } else */if (portalleafs != portalleafs_real) { + if (bsp->loadversion->game->id == GAME_QUAKE_II) { + // not used + clustermap = nullptr; + } else if (portalleafs != portalleafs_real) { clustermap = new int[portalleafs_real]; if (!strcmp(magic, PORTALFILE2)) { for (i = 0; i < portalleafs; i++) {