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 * 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 static void
CheckBSPFacesPlanar(const mbsp_t *bsp) CheckBSPFacesPlanar(const mbsp_t *bsp)
@ -157,7 +157,7 @@ CheckBSPFacesPlanar(const mbsp_t *bsp)
const float *point = bsp->dvertexes[vertnum].point; const float *point = bsp->dvertexes[vertnum].point;
const float dist = DotProduct(plane.normal, point) - plane.dist; 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", printf("WARNING: face %d, point %d off plane by %f\n",
(int)(face - bsp->dfaces), j, dist); (int)(face - bsp->dfaces), j, dist);
} }

View File

@ -579,6 +579,7 @@ void Q2_SwapBSPFile (q2bsp_t *bsp, qboolean todisk)
// //
// visibility // visibility
// //
if (bsp->dvis) {
if (todisk) if (todisk)
j = bsp->dvis->numclusters; j = bsp->dvis->numclusters;
else else
@ -589,6 +590,7 @@ void Q2_SwapBSPFile (q2bsp_t *bsp, qboolean todisk)
bsp->dvis->bitofs[i][0] = LittleLong (bsp->dvis->bitofs[i][0]); bsp->dvis->bitofs[i][0] = LittleLong (bsp->dvis->bitofs[i][0]);
bsp->dvis->bitofs[i][1] = LittleLong (bsp->dvis->bitofs[i][1]); 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 * static dvis_t *
MBSPtoQ2_CopyVisData(const uint8_t *visdata, int *visdatasize, int numleafs, const mleaf_t *leafs) { MBSPtoQ2_CopyVisData(const uint8_t *visdata, int *visdatasize, int numleafs, const mleaf_t *leafs) {
if (!*visdatasize) {
return nullptr;
}
int32_t num_clusters = 0; int32_t num_clusters = 0;
for (int32_t i = 0; i < numleafs; i++) { for (int32_t i = 0; i < numleafs; i++) {

View File

@ -51,20 +51,6 @@
#define MAX_ENT_KEY 32 #define MAX_ENT_KEY 32
#define MAX_ENT_VALUE 1024 #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 NO_VERSION -1
#define BSPVERSION 29 #define BSPVERSION 29
@ -75,31 +61,6 @@ struct bspversion_t
#define Q2_BSPVERSION 38 #define Q2_BSPVERSION 38
#define Q2_QBISMIDENT (('P'<<24)+('S'<<16)+('B'<<8)+'Q') #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 { typedef struct {
int32_t fileofs; int32_t fileofs;
int32_t filelen; int32_t filelen;
@ -247,15 +208,15 @@ typedef struct {
#define CONTENTS_SKY -6 #define CONTENTS_SKY -6
#define CONTENTS_MIN CONTENTS_SKY #define CONTENTS_MIN CONTENTS_SKY
#define CONTENTS_CLIP -7 /* compiler internal use only */ #define CONTENTS_HINT -7 /* compiler internal use only */
#define CONTENTS_HINT -8 /* compiler internal use only */ #define CONTENTS_CLIP -8 /* compiler internal use only */
#define CONTENTS_ORIGIN -9 /* compiler internal use only */ #define CONTENTS_ORIGIN -9 /* compiler internal use only */
#define CONTENTS_DETAIL -10 /* compiler internal use only */ #define CONTENTS_DETAIL -10 /* compiler internal use only */
#define CONTENTS_DETAIL_ILLUSIONARY -11 /* compiler internal use only */ #define CONTENTS_DETAIL_ILLUSIONARY -11 /* compiler internal use only */
#define CONTENTS_DETAIL_FENCE -12 /* compiler internal use only */ #define CONTENTS_DETAIL_FENCE -12 /* compiler internal use only */
#define CONTENTS_ILLUSIONARY_VISBLOCKER -13 #define CONTENTS_ILLUSIONARY_VISBLOCKER -13
#define CONTENTS_FENCE -15 /* compiler internal use only */ //#define CONTENTS_FENCE -15 /* compiler internal use only */
#define CONTENTS_LADDER -16 /* reserved for engine use */ //#define CONTENTS_LADDER -16 /* reserved for engine use */
// Q2 contents (from qfiles.h) // Q2 contents (from qfiles.h)
@ -886,6 +847,8 @@ struct q2bsp_qbism_t {
uint8_t dpop[256]; uint8_t dpop[256];
}; };
struct bspversion_t;
struct mbsp_t { struct mbsp_t {
const bspversion_t *loadversion; const bspversion_t *loadversion;
@ -982,6 +945,46 @@ typedef struct {
bspxentry_t *bspxentries; bspxentry_t *bspxentries;
} bspdata_t; } 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 LoadBSPFile(char *filename, bspdata_t *bspdata); //returns the filename as contained inside a bsp
void WriteBSPFile(const char *filename, bspdata_t *bspdata); void WriteBSPFile(const char *filename, bspdata_t *bspdata);
void PrintBSPFileSizes(const bspdata_t *bspdata); void PrintBSPFileSizes(const bspdata_t *bspdata);

View File

@ -367,8 +367,5 @@ extern options_t options;
#include <qbsp/util.hh> #include <qbsp/util.hh>
int qbsp_main(int argc, const char **argv); 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 #endif

View File

@ -418,7 +418,10 @@ ParseEpair(parser_t *parser, mapentity_t *entity)
GetVectorForKey(entity, epair->key, entity->origin); GetVectorForKey(entity, epair->key, entity->origin);
} else if (!Q_strcasecmp(epair->key, "classname")) { } else if (!Q_strcasecmp(epair->key, "classname")) {
if (!Q_strcasecmp(epair->value, "info_player_start")) { 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); Message(msgWarning, warnMultipleStarts);
rgfStartSpots |= info_player_start; rgfStartSpots |= info_player_start;
} else if (!Q_strcasecmp(epair->value, "info_player_deathmatch")) { } else if (!Q_strcasecmp(epair->value, "info_player_deathmatch")) {

View File

@ -39,7 +39,7 @@ options_t options;
ProcessEntity ProcessEntity
=============== ===============
*/ */
void static void
ProcessEntity(mapentity_t *entity, const int hullnum) ProcessEntity(mapentity_t *entity, const int hullnum)
{ {
int i, firstface; 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]); perbrush.maxs[2] = LittleFloat(b->maxs[2]);
switch(b->contents) switch(b->contents)
{ {
//contents should match the engine.
case CONTENTS_EMPTY: //really an error, but whatever case CONTENTS_EMPTY: //really an error, but whatever
case CONTENTS_SOLID: //these are okay case CONTENTS_SOLID: //these are okay
case CONTENTS_WATER: case CONTENTS_WATER:
case CONTENTS_SLIME: case CONTENTS_SLIME:
case CONTENTS_LAVA: case CONTENTS_LAVA:
case CONTENTS_SKY: case CONTENTS_SKY:
perbrush.contents = b->contents;
break;
//contents should match the engine.
case CONTENTS_CLIP: case CONTENTS_CLIP:
perbrush.contents = -8; perbrush.contents = b->contents;
break; break;
// case CONTENTS_LADDER: // case CONTENTS_LADDER:
// perbrush.contents = -16; // perbrush.contents = -16;
// break; // break;
default: 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; perbrush.contents = CONTENTS_SOLID;
break; break;
} }
@ -537,7 +535,7 @@ CreateSingleHull
================= =================
*/ */
void static void
CreateSingleHull(const int hullnum) CreateSingleHull(const int hullnum)
{ {
int i; int i;
@ -560,7 +558,7 @@ CreateHulls
================= =================
*/ */
void static void
CreateHulls(void) CreateHulls(void)
{ {
/* create the hulls sequentially */ /* create the hulls sequentially */
@ -600,6 +598,8 @@ EnsureTexturesLoaded()
wadlist_tried_loading = true; wadlist_tried_loading = true;
// Quake II doesn't use wads, .wal's are loaded from pak/loose files
if (!options.target_version->quake2) {
wadlist = NULL; wadlist = NULL;
wadstring = ValueForKey(pWorldEnt(), "_wad"); wadstring = ValueForKey(pWorldEnt(), "_wad");
if (!wadstring[0]) if (!wadstring[0])
@ -608,6 +608,9 @@ EnsureTexturesLoaded()
Message(msgWarning, warnNoWadKey); Message(msgWarning, warnNoWadKey);
else else
wadlist = WADList_Init(wadstring); wadlist = WADList_Init(wadstring);
} else {
wadstring = "";
}
if (!wadlist) { if (!wadlist) {
if (wadstring[0]) if (wadstring[0])
@ -959,7 +962,7 @@ ParseOptions(char *szOptions)
szTok = GetTok(szTok + strlen(szTok) + 1, szEnd); szTok = GetTok(szTok + strlen(szTok) + 1, szEnd);
} }
// combine format flags // if we wanted hexen2, update it now
if (hexen2) { if (hexen2) {
if (options.target_version == &bspver_bsp2) { if (options.target_version == &bspver_bsp2) {
options.target_version = &bspver_h2bsp2; options.target_version = &bspver_h2bsp2;
@ -969,6 +972,11 @@ ParseOptions(char *szOptions)
options.target_version = &bspver_h2; 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 static void
AssertVanillaContentType(int content) AssertVanillaContentType(int content)
{ {
if (options.target_version->quake2) {
return;
}
switch (content) { switch (content) {
case CONTENTS_EMPTY: case CONTENTS_EMPTY:
case CONTENTS_SOLID: case CONTENTS_SOLID:
@ -44,7 +48,7 @@ AssertVanillaContentType(int content)
} }
static int static int
RemapContentsForExport(int content) RemapContentsForExport_(int content)
{ {
if (content == CONTENTS_DETAIL_FENCE) { if (content == CONTENTS_DETAIL_FENCE) {
/* /*
@ -61,6 +65,34 @@ RemapContentsForExport(int content)
return 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 * Returns the output plane number
*/ */
@ -194,14 +226,11 @@ ExportLeaf(mapentity_t *entity, node_t *node)
/* /*
* write bounding box info * write bounding box info
* (VectorCopy doesn't work since dest are shorts)
*/ */
dleaf->mins[0] = (short)node->mins[0]; for (int32_t i = 0; i < 3; i++) {
dleaf->mins[1] = (short)node->mins[1]; dleaf->mins[i] = node->mins[i];
dleaf->mins[2] = (short)node->mins[2]; dleaf->maxs[i] = node->maxs[i];
dleaf->maxs[0] = (short)node->maxs[0]; }
dleaf->maxs[1] = (short)node->maxs[1];
dleaf->maxs[2] = (short)node->maxs[2];
dleaf->visofs = -1; // no vis info yet 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; static_cast<int>(map.exported_marksurfaces.size()) - dleaf->firstmarksurface;
// FIXME-Q2: fill in other things // 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]; dnode = &map.exported_nodes_bsp29[ourNodeIndex];
// VectorCopy doesn't work since dest are shorts for (int32_t i = 0; i < 3; i++) {
dnode->mins[0] = (short)node->mins[0]; dnode->mins[i] = node->mins[i];
dnode->mins[1] = (short)node->mins[1]; dnode->maxs[i] = node->maxs[i];
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];
dnode->planenum = ExportMapPlane(node->planenum); dnode->planenum = ExportMapPlane(node->planenum);
dnode->firstface = node->firstface; dnode->firstface = node->firstface;
@ -256,7 +284,9 @@ ExportDrawNodes(mapentity_t *entity, node_t *node)
// recursively output the other nodes // recursively output the other nodes
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
if (node->children[i]->planenum == -1) { 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; dnode->children[i] = -1;
else { else {
int nextLeafIndex = static_cast<int>(map.exported_leafs_bsp29.size()); int nextLeafIndex = static_cast<int>(map.exported_leafs_bsp29.size());
@ -335,7 +365,7 @@ BeginBSPFile(void)
// Leave room for leaf 0 (must be solid) // Leave room for leaf 0 (must be solid)
map.exported_leafs_bsp29.push_back({}); 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); 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_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); 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 // TODO: pass bspx lumps to generic bsp code so they are written
//GenLump("LMSHIFT", BSPX_LMSHIFT, 1); //GenLump("LMSHIFT", BSPX_LMSHIFT, 1);