More Q2 support

This commit is contained in:
Jonathan 2021-09-04 18:49:01 -04:00
parent 047bc1299a
commit b01fddf7f1
7 changed files with 156 additions and 105 deletions

View File

@ -135,7 +135,7 @@ PrintModelInfo(const mbsp_t *bsp)
/*
* Quick hack to check verticies of faces lie on the correct plane
*/
#define ON_EPSILON 0.01
#define PLANE_ON_EPSILON 0.01
static void
CheckBSPFacesPlanar(const mbsp_t *bsp)
@ -157,7 +157,7 @@ CheckBSPFacesPlanar(const mbsp_t *bsp)
const float *point = bsp->dvertexes[vertnum].point;
const float dist = DotProduct(plane.normal, point) - plane.dist;
if (dist < -ON_EPSILON || dist > ON_EPSILON)
if (dist < -PLANE_ON_EPSILON || dist > PLANE_ON_EPSILON)
printf("WARNING: face %d, point %d off plane by %f\n",
(int)(face - bsp->dfaces), j, dist);
}

View File

@ -22,17 +22,17 @@
#include <common/bspfile.hh>
/* hexen2, quake2 */
const bspversion_t bspver_generic { NO_VERSION, NO_VERSION, "mbsp", "generic BSP", false, false };
const bspversion_t bspver_q1 { BSPVERSION, NO_VERSION, "bsp29", "Quake BSP", false, false };
const bspversion_t bspver_bsp2 { BSP2VERSION, NO_VERSION, "bsp2", "Quake BSP2", false, false };
const bspversion_t bspver_bsp2rmq { BSP2RMQVERSION, NO_VERSION, "bsp2rmq", "Quake BSP2-RMQ", false, false };
const bspversion_t bspver_generic { NO_VERSION, NO_VERSION, "mbsp", "generic BSP", false, false };
const bspversion_t bspver_q1 { BSPVERSION, NO_VERSION, "bsp29", "Quake BSP", false, false };
const bspversion_t bspver_bsp2 { BSP2VERSION, NO_VERSION, "bsp2", "Quake BSP2", false, false };
const bspversion_t bspver_bsp2rmq { BSP2RMQVERSION, NO_VERSION, "bsp2rmq", "Quake BSP2-RMQ", false, false };
/* Hexen II doesn't use a separate version, but we can still use a separate tag/name for it */
const bspversion_t bspver_h2 { BSPVERSION, NO_VERSION, "hexen2", "Hexen II BSP", true, false };
const bspversion_t bspver_h2bsp2 { BSP2VERSION, NO_VERSION, "hexen2bsp2", "Hexen II BSP2", true, false };
const bspversion_t bspver_h2bsp2rmq { BSP2RMQVERSION, NO_VERSION, "hexen2bsp2rmq", "Hexen II BSP2-RMQ", true, false };
const bspversion_t bspver_hl { BSPHLVERSION, NO_VERSION, "hl", "Half-Life BSP", false, false };
const bspversion_t bspver_q2 { Q2_BSPIDENT, Q2_BSPVERSION, "q2bsp", "Quake II BSP", false, true };
const bspversion_t bspver_qbism { Q2_QBISMIDENT, Q2_BSPVERSION, "qbism", "Quake II Qbism BSP", false, true };
const bspversion_t bspver_h2 { BSPVERSION, NO_VERSION, "hexen2", "Hexen II BSP", true, false };
const bspversion_t bspver_h2bsp2 { BSP2VERSION, NO_VERSION, "hexen2bsp2", "Hexen II BSP2", true, false };
const bspversion_t bspver_h2bsp2rmq { BSP2RMQVERSION, NO_VERSION, "hexen2bsp2rmq", "Hexen II BSP2-RMQ", true, false };
const bspversion_t bspver_hl { BSPHLVERSION, NO_VERSION, "hl", "Half-Life BSP", false, false };
const bspversion_t bspver_q2 { Q2_BSPIDENT, Q2_BSPVERSION, "q2bsp", "Quake II BSP", false, true };
const bspversion_t bspver_qbism { Q2_QBISMIDENT, Q2_BSPVERSION, "qbism", "Quake II Qbism BSP", false, true };
static const char *
BSPVersionString(const bspversion_t *version)
@ -579,15 +579,17 @@ void Q2_SwapBSPFile (q2bsp_t *bsp, qboolean todisk)
//
// visibility
//
if (todisk)
j = bsp->dvis->numclusters;
else
j = LittleLong(bsp->dvis->numclusters);
bsp->dvis->numclusters = LittleLong (bsp->dvis->numclusters);
for (i=0 ; i<j ; i++)
{
bsp->dvis->bitofs[i][0] = LittleLong (bsp->dvis->bitofs[i][0]);
bsp->dvis->bitofs[i][1] = LittleLong (bsp->dvis->bitofs[i][1]);
if (bsp->dvis) {
if (todisk)
j = bsp->dvis->numclusters;
else
j = LittleLong(bsp->dvis->numclusters);
bsp->dvis->numclusters = LittleLong (bsp->dvis->numclusters);
for (i=0 ; i<j ; i++)
{
bsp->dvis->bitofs[i][0] = LittleLong (bsp->dvis->bitofs[i][0]);
bsp->dvis->bitofs[i][1] = LittleLong (bsp->dvis->bitofs[i][1]);
}
}
}
@ -1176,6 +1178,10 @@ static std::vector<uint8_t> CalcPHS(int32_t portalclusters, const uint8_t *visda
static dvis_t *
MBSPtoQ2_CopyVisData(const uint8_t *visdata, int *visdatasize, int numleafs, const mleaf_t *leafs) {
if (!*visdatasize) {
return nullptr;
}
int32_t num_clusters = 0;
for (int32_t i = 0; i < numleafs; i++) {

View File

@ -51,20 +51,6 @@
#define MAX_ENT_KEY 32
#define MAX_ENT_VALUE 1024
struct bspversion_t
{
/* identifier value, the first int32_t in the header */
int32_t ident;
/* version value, if supported; use NO_VERSION if a version is not required */
int32_t version;
/* short name used for command line args, etc */
const char *short_name;
/* full display name for printing */
const char *name;
bool hexen2;
bool quake2;
};
#define NO_VERSION -1
#define BSPVERSION 29
@ -75,31 +61,6 @@ struct bspversion_t
#define Q2_BSPVERSION 38
#define Q2_QBISMIDENT (('P'<<24)+('S'<<16)+('B'<<8)+'Q')
extern const bspversion_t bspver_generic;
extern const bspversion_t bspver_q1;
extern const bspversion_t bspver_h2;
extern const bspversion_t bspver_h2bsp2;
extern const bspversion_t bspver_h2bsp2rmq;
extern const bspversion_t bspver_bsp2;
extern const bspversion_t bspver_bsp2rmq;
extern const bspversion_t bspver_hl;
extern const bspversion_t bspver_q2;
extern const bspversion_t bspver_qbism;
/* table of supported versions */
constexpr const bspversion_t *const bspversions[] = {
&bspver_generic,
&bspver_q1,
&bspver_h2,
&bspver_h2bsp2,
&bspver_h2bsp2rmq,
&bspver_bsp2,
&bspver_bsp2rmq,
&bspver_hl,
&bspver_q2,
&bspver_qbism
};
typedef struct {
int32_t fileofs;
int32_t filelen;
@ -247,15 +208,15 @@ typedef struct {
#define CONTENTS_SKY -6
#define CONTENTS_MIN CONTENTS_SKY
#define CONTENTS_CLIP -7 /* compiler internal use only */
#define CONTENTS_HINT -8 /* compiler internal use only */
#define CONTENTS_HINT -7 /* compiler internal use only */
#define CONTENTS_CLIP -8 /* compiler internal use only */
#define CONTENTS_ORIGIN -9 /* compiler internal use only */
#define CONTENTS_DETAIL -10 /* compiler internal use only */
#define CONTENTS_DETAIL_ILLUSIONARY -11 /* compiler internal use only */
#define CONTENTS_DETAIL_FENCE -12 /* compiler internal use only */
#define CONTENTS_ILLUSIONARY_VISBLOCKER -13
#define CONTENTS_FENCE -15 /* compiler internal use only */
#define CONTENTS_LADDER -16 /* reserved for engine use */
//#define CONTENTS_FENCE -15 /* compiler internal use only */
//#define CONTENTS_LADDER -16 /* reserved for engine use */
// Q2 contents (from qfiles.h)
@ -886,6 +847,8 @@ struct q2bsp_qbism_t {
uint8_t dpop[256];
};
struct bspversion_t;
struct mbsp_t {
const bspversion_t *loadversion;
@ -982,6 +945,46 @@ typedef struct {
bspxentry_t *bspxentries;
} bspdata_t;
// BSP version struct & instances
struct bspversion_t
{
/* identifier value, the first int32_t in the header */
int32_t ident;
/* version value, if supported; use NO_VERSION if a version is not required */
int32_t version;
/* short name used for command line args, etc */
const char *short_name;
/* full display name for printing */
const char *name;
bool hexen2;
bool quake2;
};
extern const bspversion_t bspver_generic;
extern const bspversion_t bspver_q1;
extern const bspversion_t bspver_h2;
extern const bspversion_t bspver_h2bsp2;
extern const bspversion_t bspver_h2bsp2rmq;
extern const bspversion_t bspver_bsp2;
extern const bspversion_t bspver_bsp2rmq;
extern const bspversion_t bspver_hl;
extern const bspversion_t bspver_q2;
extern const bspversion_t bspver_qbism;
/* table of supported versions */
constexpr const bspversion_t *const bspversions[] = {
&bspver_generic,
&bspver_q1,
&bspver_h2,
&bspver_h2bsp2,
&bspver_h2bsp2rmq,
&bspver_bsp2,
&bspver_bsp2rmq,
&bspver_hl,
&bspver_q2,
&bspver_qbism
};
void LoadBSPFile(char *filename, bspdata_t *bspdata); //returns the filename as contained inside a bsp
void WriteBSPFile(const char *filename, bspdata_t *bspdata);
void PrintBSPFileSizes(const bspdata_t *bspdata);

View File

@ -367,8 +367,5 @@ extern options_t options;
#include <qbsp/util.hh>
int qbsp_main(int argc, const char **argv);
void ProcessEntity(mapentity_t *entity, const int hullnum);
void CreateSingleHull(const int hullnum);
void CreateHulls(void);
#endif

View File

@ -418,7 +418,10 @@ ParseEpair(parser_t *parser, mapentity_t *entity)
GetVectorForKey(entity, epair->key, entity->origin);
} else if (!Q_strcasecmp(epair->key, "classname")) {
if (!Q_strcasecmp(epair->value, "info_player_start")) {
if (rgfStartSpots & info_player_start)
// Quake II uses multiple starts for level transitions/backtracking.
// TODO: instead, this should check targetnames. There should only be
// one info_player_start per targetname.
if (!options.target_version->quake2 && (rgfStartSpots & info_player_start))
Message(msgWarning, warnMultipleStarts);
rgfStartSpots |= info_player_start;
} else if (!Q_strcasecmp(epair->value, "info_player_deathmatch")) {

View File

@ -39,7 +39,7 @@ options_t options;
ProcessEntity
===============
*/
void
static void
ProcessEntity(mapentity_t *entity, const int hullnum)
{
int i, firstface;
@ -406,23 +406,21 @@ void BSPX_Brushes_AddModel(struct bspxbrushes_s *ctx, int modelnum, brush_t *bru
perbrush.maxs[2] = LittleFloat(b->maxs[2]);
switch(b->contents)
{
//contents should match the engine.
case CONTENTS_EMPTY: //really an error, but whatever
case CONTENTS_SOLID: //these are okay
case CONTENTS_WATER:
case CONTENTS_SLIME:
case CONTENTS_LAVA:
case CONTENTS_SKY:
perbrush.contents = b->contents;
break;
//contents should match the engine.
case CONTENTS_CLIP:
perbrush.contents = -8;
perbrush.contents = b->contents;
break;
// case CONTENTS_LADDER:
// perbrush.contents = -16;
// break;
default:
Message(msgWarning, "Uknown contents: %i. Translating to solid.", b->contents);
Message(msgWarning, "Unknown contents: %i. Translating to solid.", b->contents);
perbrush.contents = CONTENTS_SOLID;
break;
}
@ -537,7 +535,7 @@ CreateSingleHull
=================
*/
void
static void
CreateSingleHull(const int hullnum)
{
int i;
@ -560,7 +558,7 @@ CreateHulls
=================
*/
void
static void
CreateHulls(void)
{
/* create the hulls sequentially */
@ -600,14 +598,19 @@ EnsureTexturesLoaded()
wadlist_tried_loading = true;
wadlist = NULL;
wadstring = ValueForKey(pWorldEnt(), "_wad");
if (!wadstring[0])
wadstring = ValueForKey(pWorldEnt(), "wad");
if (!wadstring[0])
Message(msgWarning, warnNoWadKey);
else
wadlist = WADList_Init(wadstring);
// Quake II doesn't use wads, .wal's are loaded from pak/loose files
if (!options.target_version->quake2) {
wadlist = NULL;
wadstring = ValueForKey(pWorldEnt(), "_wad");
if (!wadstring[0])
wadstring = ValueForKey(pWorldEnt(), "wad");
if (!wadstring[0])
Message(msgWarning, warnNoWadKey);
else
wadlist = WADList_Init(wadstring);
} else {
wadstring = "";
}
if (!wadlist) {
if (wadstring[0])
@ -959,7 +962,7 @@ ParseOptions(char *szOptions)
szTok = GetTok(szTok + strlen(szTok) + 1, szEnd);
}
// combine format flags
// if we wanted hexen2, update it now
if (hexen2) {
if (options.target_version == &bspver_bsp2) {
options.target_version = &bspver_h2bsp2;
@ -969,6 +972,11 @@ ParseOptions(char *szOptions)
options.target_version = &bspver_h2;
}
}
// force specific flags for Q2
if (options.target_version->quake2) {
options.fNoclip = true;
}
}

View File

@ -30,6 +30,10 @@
static void
AssertVanillaContentType(int content)
{
if (options.target_version->quake2) {
return;
}
switch (content) {
case CONTENTS_EMPTY:
case CONTENTS_SOLID:
@ -44,7 +48,7 @@ AssertVanillaContentType(int content)
}
static int
RemapContentsForExport(int content)
RemapContentsForExport_(int content)
{
if (content == CONTENTS_DETAIL_FENCE) {
/*
@ -61,6 +65,34 @@ RemapContentsForExport(int content)
return content;
}
static int
RemapContentsForExport(int content)
{
content = RemapContentsForExport_(content);
if (options.target_version->quake2) {
switch (content) {
case CONTENTS_EMPTY:
return 0;
case CONTENTS_SOLID:
case CONTENTS_SKY:
return Q2_CONTENTS_SOLID;
case CONTENTS_WATER:
return Q2_CONTENTS_WATER;
case CONTENTS_SLIME:
return Q2_CONTENTS_SLIME;
case CONTENTS_LAVA:
return Q2_CONTENTS_LAVA;
case CONTENTS_CLIP:
return Q2_CONTENTS_PLAYERCLIP | Q2_CONTENTS_MONSTERCLIP;
default:
Error("dunno what to do with contents %i\n", content);
}
}
return content;
}
/**
* Returns the output plane number
*/
@ -194,14 +226,11 @@ ExportLeaf(mapentity_t *entity, node_t *node)
/*
* write bounding box info
* (VectorCopy doesn't work since dest are shorts)
*/
dleaf->mins[0] = (short)node->mins[0];
dleaf->mins[1] = (short)node->mins[1];
dleaf->mins[2] = (short)node->mins[2];
dleaf->maxs[0] = (short)node->maxs[0];
dleaf->maxs[1] = (short)node->maxs[1];
dleaf->maxs[2] = (short)node->maxs[2];
for (int32_t i = 0; i < 3; i++) {
dleaf->mins[i] = node->mins[i];
dleaf->maxs[i] = node->maxs[i];
}
dleaf->visofs = -1; // no vis info yet
@ -223,6 +252,8 @@ ExportLeaf(mapentity_t *entity, node_t *node)
static_cast<int>(map.exported_marksurfaces.size()) - dleaf->firstmarksurface;
// FIXME-Q2: fill in other things
dleaf->area = 0;
dleaf->cluster = node->viscluster;
}
/*
@ -241,13 +272,10 @@ ExportDrawNodes(mapentity_t *entity, node_t *node)
dnode = &map.exported_nodes_bsp29[ourNodeIndex];
// VectorCopy doesn't work since dest are shorts
dnode->mins[0] = (short)node->mins[0];
dnode->mins[1] = (short)node->mins[1];
dnode->mins[2] = (short)node->mins[2];
dnode->maxs[0] = (short)node->maxs[0];
dnode->maxs[1] = (short)node->maxs[1];
dnode->maxs[2] = (short)node->maxs[2];
for (int32_t i = 0; i < 3; i++) {
dnode->mins[i] = node->mins[i];
dnode->maxs[i] = node->maxs[i];
}
dnode->planenum = ExportMapPlane(node->planenum);
dnode->firstface = node->firstface;
@ -256,7 +284,9 @@ ExportDrawNodes(mapentity_t *entity, node_t *node)
// recursively output the other nodes
for (i = 0; i < 2; i++) {
if (node->children[i]->planenum == -1) {
if (node->children[i]->contents == CONTENTS_SOLID)
// In Q2, all leaves must have their own ID even if they share solidity.
// (probably for collision purposes? makes sense given they store leafbrushes)
if (!options.target_version->quake2 && node->children[i]->contents == CONTENTS_SOLID)
dnode->children[i] = -1;
else {
int nextLeafIndex = static_cast<int>(map.exported_leafs_bsp29.size());
@ -304,9 +334,9 @@ ExportDrawNodes(mapentity_t *entity, node_t *headnode, int firstface)
{
if (headnode->contents < 0)
ExportLeaf(entity, headnode);
else
ExportDrawNodes(entity, headnode);
ExportLeaf(entity, headnode);
else
ExportDrawNodes(entity, headnode);
}
// count how many leafs were exported by the above calls
@ -335,7 +365,7 @@ BeginBSPFile(void)
// Leave room for leaf 0 (must be solid)
map.exported_leafs_bsp29.push_back({});
map.exported_leafs_bsp29.back().contents = CONTENTS_SOLID; // FIXME-Q2: use Q2_CONTENTS_SOLID
map.exported_leafs_bsp29.back().contents = RemapContentsForExport(CONTENTS_SOLID);
Q_assert(map.exported_leafs_bsp29.size() == 1);
}
@ -438,6 +468,10 @@ WriteBSPFile()
CopyString(map.exported_entities, true, &bspdata.data.mbsp.entdatasize, (void**)&bspdata.data.mbsp.dentdata);
CopyString(map.exported_texdata, false, &bspdata.data.mbsp.texdatasize, (void**)&bspdata.data.mbsp.dtexdata);
bspdata.data.mbsp.numareas = 1;
bspdata.data.mbsp.dareas = (darea_t *) malloc(sizeof(darea_t));
bspdata.data.mbsp.dareas->firstareaportal = bspdata.data.mbsp.dareas->numareaportals = 0;
// TODO: pass bspx lumps to generic bsp code so they are written
//GenLump("LMSHIFT", BSPX_LMSHIFT, 1);