diff --git a/common/bspfile.c b/common/bspfile.c index 551a29f6..4e2c7f20 100644 --- a/common/bspfile.c +++ b/common/bspfile.c @@ -904,6 +904,17 @@ ConvertBSPFormat(int32_t version, bspdata_t *bspdata) BSPVersionString(bspdata->version), BSPVersionString(version)); } +static int +isHexen2(const dheader_t *header) +{ + /* + the world should always have some face. + however, if the sizes are wrong then we're actually reading headnode[6]. hexen2 only used 5 hulls, so this should be 0 in hexen2, and not in quake. + */ + const dmodelq1_t *modelsq1 = (const dmodelq1_t*)((const byte *)header + header->lumps[LUMP_MODELS].fileofs); + return !modelsq1->numfaces; +} + /* * ========================================================================= * ... @@ -990,20 +1001,54 @@ CopyLump(const dheader_t *header, int lumpnum, void *destptr) length = header->lumps[lumpnum].filelen; ofs = header->lumps[lumpnum].fileofs; - if (length % lumpspec->size) - Error("%s: odd %s lump size", __func__, lumpspec->name); - if (buffer) free(buffer); - buffer = *bufferptr = malloc(length + 1); - if (!buffer) - Error("%s: allocation of %i bytes failed.", __func__, length); + if (lumpnum == LUMP_MODELS && !isHexen2(header)) + { /*convert in-place. no need to care about endian here.*/ + const dmodelq1_t *in = (const dmodelq1_t*)((const byte *)header + ofs); + dmodel_t *out; + int i, j; + if (length % sizeof(dmodelq1_t)) + Error("%s: odd %s lump size", __func__, lumpspec->name); + length /= sizeof(dmodelq1_t); - memcpy(buffer, (const byte *)header + ofs, length); - buffer[length] = 0; /* In case of corrupt entity lump */ + buffer = *bufferptr = malloc(length * sizeof(dmodel_t)); + if (!buffer) + Error("%s: allocation of %i bytes failed.", __func__, length); + out = (dmodel_t*)buffer; + for (i = 0; i < length; i++) + { + for (j = 0; j < 3; j++) + { + out[i].mins[j] = in[i].mins[j]; + out[i].maxs[j] = in[i].maxs[j]; + out[i].origin[j] = in[i].origin[j]; + } + for (j = 0; j < MAX_MAP_HULLS_Q1; j++) + out[i].headnode[j] = in[i].headnode[j]; + for ( ; j < MAX_MAP_HULLS_H2; j++) + out[i].headnode[j] = 0; + out[i].visleafs = in[i].visleafs; + out[i].firstface = in[i].firstface; + out[i].numfaces = in[i].numfaces; + } + return length; + } + else + { + if (length % lumpspec->size) + Error("%s: odd %s lump size", __func__, lumpspec->name); - return length / lumpspec->size; + buffer = *bufferptr = malloc(length + 1); + if (!buffer) + Error("%s: allocation of %i bytes failed.", __func__, length); + + memcpy(buffer, (const byte *)header + ofs, length); + buffer[length] = 0; /* In case of corrupt entity lump */ + + return length / lumpspec->size; + } } /* @@ -1032,6 +1077,14 @@ LoadBSPFile(const char *filename, bspdata_t *bspdata) header->lumps[i].filelen = LittleLong(header->lumps[i].filelen); } + if (isHexen2(header)) + { + logprint("BSP appears to be from hexen2\n"); + bspdata->hullcount = MAX_MAP_HULLS_H2; + } + else + bspdata->hullcount = MAX_MAP_HULLS_Q1; + /* copy the data */ if (header->version == BSPVERSION) { bsp29_t *bsp = &bspdata->data.bsp29; @@ -1147,6 +1200,39 @@ AddLump(bspfile_t *bspfile, int lumpnum, const void *data, int count) SafeWrite(bspfile->file, pad, size % 4); } +static void +AddModelsLump(bspfile_t *bspfile, bspdata_t *bspdata, const void *data, int count) +{ + if (bspdata->hullcount == MAX_MAP_HULLS_Q1) + { /*convert in-place. no need to care about endian here.*/ + lump_t *lump = &bspfile->header.lumps[LUMP_MODELS]; + const dmodel_t *in = data; + dmodelq1_t *out = malloc(count * sizeof(dmodelq1_t)); + int i, j; + for (i = 0; i < count; i++) + { + for (j = 0; j < 3; j++) + { + out[i].mins[j] = in[i].mins[j]; + out[i].maxs[j] = in[i].maxs[j]; + out[i].origin[j] = in[i].origin[j]; + } + for (j = 0; j < MAX_MAP_HULLS_Q1; j++) + out[i].headnode[j] = in[i].headnode[j]; + out[i].visleafs = in[i].visleafs; + out[i].firstface = in[i].firstface; + out[i].numfaces = in[i].numfaces; + } + lump->fileofs = LittleLong(ftell(bspfile->file)); + lump->filelen = LittleLong(sizeof(dmodelq1_t) * count); + SafeWrite(bspfile->file, out, lump->filelen); + free(out); + return; + } + else + AddLump(bspfile, LUMP_MODELS, data, count); +} + /* * ============= * WriteBSPFile @@ -1182,7 +1268,7 @@ WriteBSPFile(const char *filename, bspdata_t *bspdata) AddLump(&bspfile, LUMP_MARKSURFACES, bsp->dmarksurfaces, bsp->nummarksurfaces); AddLump(&bspfile, LUMP_SURFEDGES, bsp->dsurfedges, bsp->numsurfedges); AddLump(&bspfile, LUMP_EDGES, bsp->dedges, bsp->numedges); - AddLump(&bspfile, LUMP_MODELS, bsp->dmodels, bsp->nummodels); + AddModelsLump(&bspfile, bspdata, bsp->dmodels, bsp->nummodels); AddLump(&bspfile, LUMP_LIGHTING, bsp->dlightdata, bsp->lightdatasize); AddLump(&bspfile, LUMP_VISIBILITY, bsp->dvisdata, bsp->visdatasize); @@ -1203,7 +1289,7 @@ WriteBSPFile(const char *filename, bspdata_t *bspdata) AddLump(&bspfile, LUMP_MARKSURFACES, bsp->dmarksurfaces, bsp->nummarksurfaces); AddLump(&bspfile, LUMP_SURFEDGES, bsp->dsurfedges, bsp->numsurfedges); AddLump(&bspfile, LUMP_EDGES, bsp->dedges, bsp->numedges); - AddLump(&bspfile, LUMP_MODELS, bsp->dmodels, bsp->nummodels); + AddModelsLump(&bspfile, bspdata, bsp->dmodels, bsp->nummodels); AddLump(&bspfile, LUMP_LIGHTING, bsp->dlightdata, bsp->lightdatasize); AddLump(&bspfile, LUMP_VISIBILITY, bsp->dvisdata, bsp->visdatasize); @@ -1224,7 +1310,7 @@ WriteBSPFile(const char *filename, bspdata_t *bspdata) AddLump(&bspfile, LUMP_MARKSURFACES, bsp->dmarksurfaces, bsp->nummarksurfaces); AddLump(&bspfile, LUMP_SURFEDGES, bsp->dsurfedges, bsp->numsurfedges); AddLump(&bspfile, LUMP_EDGES, bsp->dedges, bsp->numedges); - AddLump(&bspfile, LUMP_MODELS, bsp->dmodels, bsp->nummodels); + AddModelsLump(&bspfile, bspdata, bsp->dmodels, bsp->nummodels); AddLump(&bspfile, LUMP_LIGHTING, bsp->dlightdata, bsp->lightdatasize); AddLump(&bspfile, LUMP_VISIBILITY, bsp->dvisdata, bsp->visdatasize); diff --git a/include/common/bspfile.h b/include/common/bspfile.h index cfad18d7..7f74f5de 100644 --- a/include/common/bspfile.h +++ b/include/common/bspfile.h @@ -27,7 +27,10 @@ /* upper design bounds */ -#define MAX_MAP_HULLS 4 +#define MAX_MAP_HULLS_Q1 4 +#define MAX_MAP_HULLS_H2 8 +#define MAX_MAP_HULLS MAX_MAP_HULLS_H2 + #define MAX_MAP_MODELS 256 #define MAX_MAP_BRUSHES 4096 #define MAX_MAP_PLANES 16384 @@ -88,11 +91,21 @@ typedef struct { float mins[3]; float maxs[3]; float origin[3]; - int32_t headnode[MAX_MAP_HULLS]; + int32_t headnode[MAX_MAP_HULLS_Q1]; int32_t visleafs; /* not including the solid leaf 0 */ int32_t firstface; int32_t numfaces; -} dmodel_t; +} dmodelq1_t; +typedef struct { + float mins[3]; + float maxs[3]; + float origin[3]; + int32_t headnode[MAX_MAP_HULLS_H2]; + int32_t visleafs; /* not including the solid leaf 0 */ + int32_t firstface; + int32_t numfaces; +} dmodelh2_t; +typedef dmodelh2_t dmodel_t; typedef struct { int32_t nummiptex; @@ -418,6 +431,7 @@ typedef struct { typedef struct { int32_t version; + int hullcount; union { bsp29_t bsp29; bsp2rmq_t bsp2rmq; diff --git a/man/qbsp.1 b/man/qbsp.1 index 73a91ee9..e5fb37c4 100644 --- a/man/qbsp.1 +++ b/man/qbsp.1 @@ -72,6 +72,8 @@ Switch to the cheap spatial subdivion bsp heuristic when splitting nodes of this size (in any dimension). This gives much faster qbsp processing times on large maps and should generate better bsp trees as well. From txqbsp-xt, thanks rebb. (default 1024, 0 to disable) +.IP "\fB\-hexen2\fP" +Generate a hexen2 bsp. .SH "SPECIAL TEXTURE NAMES" .PP diff --git a/qbsp/brush.c b/qbsp/brush.c index afe839fb..03e0f66a 100644 --- a/qbsp/brush.c +++ b/qbsp/brush.c @@ -838,18 +838,64 @@ LoadBrush(const mapbrush_t *mapbrush, const vec3_t rotate_offset, return NULL; } - if (hullnum == 1) { - vec3_t size[2] = { {-16, -16, -32}, {16, 16, 24} }; + if (options.hexen2) + { + if (hullnum == 1) { + vec3_t size[2] = { {-16, -16, -32}, {16, 16, 24} }; + ExpandBrush(&hullbrush, size, facelist); + FreeBrushFaces(facelist); + facelist = CreateBrushFaces(&hullbrush, rotate_offset, hullnum); + } + else if (hullnum == 2) { + vec3_t size[2] = { {-24, -24, -20}, {24, 24, 20} }; + ExpandBrush(&hullbrush, size, facelist); + FreeBrushFaces(facelist); + facelist = CreateBrushFaces(&hullbrush, rotate_offset, hullnum); + } + else if (hullnum == 3) { + vec3_t size[2] = { {-16, -16, -12}, {16, 16, 16} }; + ExpandBrush(&hullbrush, size, facelist); + FreeBrushFaces(facelist); + facelist = CreateBrushFaces(&hullbrush, rotate_offset, hullnum); + } + else if (hullnum == 4) { +#if 0 + if (options.hexen2 == 1) { /*original game*/ + vec3_t size[2] = { {-40, -40, -42}, {40, 40, 42} }; + ExpandBrush(&hullbrush, size, facelist); + FreeBrushFaces(facelist); + facelist = CreateBrushFaces(&hullbrush, rotate_offset, hullnum); + } else +#endif + { /*mission pack*/ + vec3_t size[2] = { {-8, -8, -8}, {8, 8, 8} }; + ExpandBrush(&hullbrush, size, facelist); + FreeBrushFaces(facelist); + facelist = CreateBrushFaces(&hullbrush, rotate_offset, hullnum); + } + } + else if (hullnum == 5) { + vec3_t size[2] = { {-48, -48, -50}, {48, 48, 50} }; + ExpandBrush(&hullbrush, size, facelist); + FreeBrushFaces(facelist); + facelist = CreateBrushFaces(&hullbrush, rotate_offset, hullnum); + } + } + else + { + if (hullnum == 1) { + vec3_t size[2] = { {-16, -16, -32}, {16, 16, 24} }; - ExpandBrush(&hullbrush, size, facelist); - FreeBrushFaces(facelist); - facelist = CreateBrushFaces(&hullbrush, rotate_offset, hullnum); - } else if (hullnum == 2) { - vec3_t size[2] = { {-32, -32, -64}, {32, 32, 24} }; + ExpandBrush(&hullbrush, size, facelist); + FreeBrushFaces(facelist); + facelist = CreateBrushFaces(&hullbrush, rotate_offset, hullnum); + } else if (hullnum == 2) { + vec3_t size[2] = { {-32, -32, -64}, {32, 32, 24} }; - ExpandBrush(&hullbrush, size, facelist); - FreeBrushFaces(facelist); - facelist = CreateBrushFaces(&hullbrush, rotate_offset, hullnum); + ExpandBrush(&hullbrush, size, facelist); + FreeBrushFaces(facelist); + facelist = CreateBrushFaces(&hullbrush, rotate_offset, hullnum); + } } // create the brush diff --git a/qbsp/bspfile.c b/qbsp/bspfile.c index a523d1db..a497baf1 100644 --- a/qbsp/bspfile.c +++ b/qbsp/bspfile.c @@ -96,10 +96,33 @@ AddLump(FILE *f, int Type) for (i = 0, entity = map.entities; i < map.numentities; i++, entity++) { entities = &entity->lumps[Type]; if (entities->data) { - ret = fwrite(entities->data, MemSize[Type], entities->count, f); - if (ret != entities->count) - Error("Failure writing to file"); - cLen += entities->count * MemSize[Type]; + if (Type == LUMP_MODELS && !options.hexen2) { + const dmodel_t *in = entities->data; + dmodelq1_t out; + int j, k; + for (j = 0; j < entities->count; j++) + { + for (k = 0; k < 3; k++) { + out.mins[k] = in[j].mins[k]; + out.maxs[k] = in[j].maxs[k]; + out.origin[k] = in[j].origin[k]; + } + for (k = 0; k < MAX_MAP_HULLS_Q1; k++) + out.headnode[k] = in[j].headnode[k]; + out.visleafs = in[j].visleafs; + out.firstface = in[j].firstface; + out.numfaces = in[j].numfaces; + ret = fwrite(&out, sizeof(out), 1, f); + if (ret != 1) + Error("Failure writing to file"); + } + cLen += entities->count * sizeof(out); + } else { + ret = fwrite(entities->data, MemSize[Type], entities->count, f); + if (ret != entities->count) + Error("Failure writing to file"); + cLen += entities->count * MemSize[Type]; + } } } diff --git a/qbsp/bspfile.h b/qbsp/bspfile.h index 7c8dfa76..d3d1f8e7 100644 --- a/qbsp/bspfile.h +++ b/qbsp/bspfile.h @@ -56,12 +56,22 @@ typedef struct { #define BSP_LUMPS 15 typedef struct { +#define MAX_MAP_HULLS_Q1 4 float mins[3], maxs[3]; float origin[3]; - int32_t headnode[4]; /* 4 for backward compat, only 3 hulls exist */ + int32_t headnode[MAX_MAP_HULLS_Q1]; /* 4 for backward compat, only 3 hulls exist */ int32_t visleafs; /* not including the solid leaf 0 */ int32_t firstface, numfaces; -} dmodel_t; +} dmodelq1_t; +typedef struct { +#define MAX_MAP_HULLS_H2 8 + float mins[3], maxs[3]; + float origin[3]; + int32_t headnode[MAX_MAP_HULLS_H2]; /* hexen2 only uses 6 */ + int32_t visleafs; /* not including the solid leaf 0 */ + int32_t firstface, numfaces; +} dmodelh2_t; +typedef dmodelh2_t dmodel_t; typedef struct { int32_t version; diff --git a/qbsp/qbsp.c b/qbsp/qbsp.c index 09c567a1..96f5f8b4 100644 --- a/qbsp/qbsp.c +++ b/qbsp/qbsp.c @@ -289,6 +289,13 @@ CreateHulls(void) CreateSingleHull(1); CreateSingleHull(2); + + if (options.hexen2) + { /*note: h2mp doesn't use hull 2 automatically, however gamecode can explicitly set ent.hull=3 to access it*/ + CreateSingleHull(3); + CreateSingleHull(4); + CreateSingleHull(5); + } } @@ -504,6 +511,8 @@ ParseOptions(char *szOptions) options.fOldleak = true; else if (!strcasecmp(szTok, "nopercent")) options.fNopercent = true; + else if (!strcasecmp(szTok, "hexen2")) + options.hexen2 = true; else if (!strcasecmp(szTok, "bsp2")) { options.BSPVersion = BSP2VERSION; MemSize = MemSize_BSP2; diff --git a/qbsp/qbsp.h b/qbsp/qbsp.h index 137f325c..343b21fe 100644 --- a/qbsp/qbsp.h +++ b/qbsp/qbsp.h @@ -448,6 +448,7 @@ typedef struct options_s { bool fNopercent; bool forceGoodTree; bool fixRotateObjTexture; + int hexen2;/*2 if the worldspawn mission pack flag was set*/ int BSPVersion; int dxSubdivide; int dxLeakDist; diff --git a/qbsp/writebsp.c b/qbsp/writebsp.c index 9640fa17..0e4d2e4d 100644 --- a/qbsp/writebsp.c +++ b/qbsp/writebsp.c @@ -235,7 +235,8 @@ ExportClipNodes(mapentity_t *entity, node_t *nodes, const int hullnum) /* Worth special-casing for entity 0 (no modification needed) */ diff = clipcount - model->headnode[1]; if (diff != 0) { - model->headnode[1] += diff; + for (i = 1; i < hullnum; i++) + model->headnode[i] += diff; if (options.BSPVersion == BSPVERSION) { bsp29_dclipnode_t *clipnode = clipnodes->data; for (i = 0; i < oldcount; i++, clipnode++) {