From 89365949bc9a0e2ac4f9ff0fbccdd74b37f0cc36 Mon Sep 17 00:00:00 2001 From: Shpoike Date: Thu, 4 Jul 2019 21:08:17 -0600 Subject: [PATCH] Squashed commit of the following: commit 8ca122916d3d070994224a246fd212386fcd5b21 Author: Eric Wasylishen Date: Thu Jul 4 20:54:23 2019 -0600 fix linker error (radlights is in 2 .cpp files) commit 8f06fc9c8d5a305aac075fa96503e80a0a1e840b Author: Eric Wasylishen Date: Thu Jul 4 20:53:57 2019 -0600 add clarification comment commit 038a553fb0fe57b4b1cce6dbcf0596aab940948a Author: Eric Wasylishen Date: Thu Jul 4 20:30:27 2019 -0600 fix tests compilation commit 02e94ec507ee354374b58827f527f036995ddcc8 Author: Eric Wasylishen Date: Thu Jul 4 19:59:25 2019 -0600 hlbsp: use q_snprintf commit 669a5a7911237a3d8135ebc5670d60e5fea68044 Author: Eric Wasylishen Date: Thu Jul 4 19:56:21 2019 -0600 Reformat commit 66537609826764c582bca21d8c24670da44b53d4 Merge: 347a455 ac3553c Author: Eric Wasylishen Date: Thu Jul 4 19:28:06 2019 -0600 Merge remote-tracking branch 'origin/master' into hlbsp # Conflicts: # include/qbsp/qbsp.hh commit 347a455102b8be66fbe45092916f3d3062b5634a Author: Shpoike Date: Thu Mar 28 02:18:16 2019 +0000 Misc HLBSP tweaks Added support for multiple -wadpath args. Added -xwadpath (for eg valve/*.wad to avoid bloat/license issues). Reworked hint brushes - the non-hint surfaces can use any name, just so long as they're not 'hint', for compat with zhlt. Support 'bevel' and 'null' textures for compat with zhlt. commit 7fb22c73671fc4c29fa89388d695ba7f6c2a22ba Author: Shpoike Date: Thu Mar 28 01:59:42 2019 +0000 Support for halflife-style .rad surface lights (probably too bright, but the basics work). commit 2da504fb6250c75697928ee7f0988315cd6b7799 Author: Shpoike Date: Thu Mar 28 01:56:27 2019 +0000 Add minus-prefixed textures for halflife's randomised-textures feature, instead of getting crash-to-desktop errors when the map is loaded. commit 6366199bdb08962e3170ef97901d4218eeac5ba0 Author: Shpoike Date: Thu Mar 21 04:51:24 2019 +0000 First attempt at hlbsp output. --- common/bspfile.cc | 19 ++++-- include/common/bspfile.hh | 1 + include/light/entities.hh | 1 + include/qbsp/brush.hh | 2 +- include/qbsp/bspfile.hh | 1 + include/qbsp/map.hh | 1 + include/qbsp/qbsp.hh | 20 ++++++- include/qbsp/wad.hh | 2 +- light/bounce.cc | 2 +- light/entities.cc | 46 ++++++++++++++ light/light.cc | 16 ++++- light/ltface.cc | 4 +- man/qbsp.1 | 24 +++++--- qbsp/brush.cc | 87 ++++++++++++++++++--------- qbsp/bspfile.cc | 1 + qbsp/globals.cc | 2 +- qbsp/map.cc | 16 ++++- qbsp/outside.cc | 2 +- qbsp/qbsp.cc | 42 +++++++++---- qbsp/surfaces.cc | 6 +- qbsp/test_qbsp.cc | 4 +- qbsp/wad.cc | 123 ++++++++++++++++++++++++++------------ qbsp/writebsp.cc | 6 +- 23 files changed, 315 insertions(+), 113 deletions(-) diff --git a/common/bspfile.cc b/common/bspfile.cc index ad0218f5..b30e2f08 100644 --- a/common/bspfile.cc +++ b/common/bspfile.cc @@ -33,6 +33,8 @@ BSPVersionString(int32_t version) return "BSP2rmq"; case BSP2VERSION: return "BSP2"; + case BSPHLVERSION: + return "HLBSP"; case Q2_BSPVERSION: return "Q2BSP"; default: @@ -49,6 +51,8 @@ BSPVersionSupported(int32_t version) case BSPVERSION: case BSP2VERSION: case BSP2RMQVERSION: + case BSPHLVERSION: + return true; case Q2_BSPVERSION: return true; default: @@ -579,7 +583,7 @@ SwapBSPFile(bspdata_t *bspdata, swaptype_t swap) return; } - if (bspdata->version == BSPVERSION) { + if (bspdata->version == BSPVERSION || bspdata->version == BSPHLVERSION) { bsp29_t *bsp = &bspdata->data.bsp29; SwapBSPVertexes(bsp->numvertexes, bsp->dvertexes); @@ -1567,7 +1571,7 @@ ConvertBSPFormat(int32_t version, bspdata_t *bspdata) // conversions to GENERIC_BSP - if (bspdata->version == BSPVERSION && version == GENERIC_BSP) { + if ((bspdata->version == BSPVERSION || bspdata->version == BSPHLVERSION) && version == GENERIC_BSP) { const bsp29_t *bsp29 = &bspdata->data.bsp29; mbsp_t *mbsp = &bspdata->data.mbsp; @@ -1777,7 +1781,7 @@ ConvertBSPFormat(int32_t version, bspdata_t *bspdata) // conversions from GENERIC_BSP - if (bspdata->version == GENERIC_BSP && version == BSPVERSION) { + if (bspdata->version == GENERIC_BSP && (version == BSPVERSION || version == BSPHLVERSION)) { bsp29_t *bsp29 = &bspdata->data.bsp29; const mbsp_t *mbsp = &bspdata->data.mbsp; @@ -2089,6 +2093,7 @@ CopyLump(const dheader_t *header, int lumpnum, void *destptr) switch (header->version) { case BSPVERSION: + case BSPHLVERSION: lumpspec = &lumpspec_bsp29[lumpnum]; break; case BSP2RMQVERSION: @@ -2351,7 +2356,7 @@ LoadBSPFile(char *filename, bspdata_t *bspdata) Q2_CopyLump (header, Q2_LUMP_POP, &bsp->dpop); } - if (version == BSPVERSION) { + if (version == BSPVERSION || version == BSPHLVERSION) { dheader_t *header = (dheader_t *)file_data; bsp29_t *bsp = &bspdata->data.bsp29; @@ -2488,6 +2493,7 @@ AddLump(bspfile_t *bspfile, int lumpnum, const void *data, int count) switch (bspfile->version) { case BSPVERSION: + case BSPHLVERSION: size = lumpspec_bsp29[lumpnum].size * count; break; case BSP2RMQVERSION: @@ -2584,7 +2590,8 @@ WriteBSPFile(const char *filename, bspdata_t *bspdata) SafeWrite(bspfile.file, &bspfile.q1header, sizeof(bspfile.q1header)); } - if (bspdata->version == BSPVERSION) { + if (bspdata->version == BSPVERSION || + bspdata->version == BSPHLVERSION) { bsp29_t *bsp = &bspdata->data.bsp29; AddLump(&bspfile, LUMP_PLANES, bsp->dplanes, bsp->numplanes); @@ -2766,7 +2773,7 @@ PrintBSPFileSizes(const bspdata_t *bspdata) logprint("%7s %-12s %10i\n", "", "entdata", bsp->entdatasize); } - if (bspdata->version == BSPVERSION) { + if (bspdata->version == BSPVERSION || bspdata->version == BSPHLVERSION) { const bsp29_t *bsp = &bspdata->data.bsp29; const lumpspec_t *lumpspec = lumpspec_bsp29; diff --git a/include/common/bspfile.hh b/include/common/bspfile.hh index 64975f44..812655db 100644 --- a/include/common/bspfile.hh +++ b/include/common/bspfile.hh @@ -55,6 +55,7 @@ #define BSPVERSION 29 #define BSP2RMQVERSION (('B' << 24) | ('S' << 16) | ('P' << 8) | '2') #define BSP2VERSION ('B' | ('S' << 8) | ('P' << 16) | ('2' << 24)) +#define BSPHLVERSION 30 //24bit lighting, and private palettes in the textures lump. #define Q2_BSPIDENT (('P'<<24)+('S'<<16)+('B'<<8)+'I') #define Q2_BSPVERSION 38 diff --git a/include/light/entities.hh b/include/light/entities.hh index df9e4ae1..6c8df0cb 100644 --- a/include/light/entities.hh +++ b/include/light/entities.hh @@ -180,6 +180,7 @@ std::string WorldValueForKey(const std::string &key); void LoadEntities(const globalconfig_t &cfg, const mbsp_t *bsp); void SetupLights(const globalconfig_t &cfg, const mbsp_t *bsp); +bool ParseLightsFile(const char *fname); void WriteEntitiesToString(mbsp_t *bsp); void EstimateVisibleBoundsAtPoint(const vec3_t point, vec3_t mins, vec3_t maxs); diff --git a/include/qbsp/brush.hh b/include/qbsp/brush.hh index 1ffffff2..756f038c 100644 --- a/include/qbsp/brush.hh +++ b/include/qbsp/brush.hh @@ -41,7 +41,7 @@ int Brush_ListCountWithCFlags(const brush_t *brush, int cflags); int Brush_ListCount(const brush_t *brush); int Brush_NumFaces(const brush_t *brush); -brush_t *LoadBrush(const mapbrush_t *mapbrush, const vec3_t rotate_offset, const int hullnum); +brush_t *LoadBrush(const mapbrush_t *mapbrush, int contents, const vec3_t rotate_offset, const int hullnum); void FreeBrushes(mapentity_t *ent); int FindPlane(const vec3_t normal, const vec_t dist, int *side); diff --git a/include/qbsp/bspfile.hh b/include/qbsp/bspfile.hh index f2d179eb..33cd7917 100644 --- a/include/qbsp/bspfile.hh +++ b/include/qbsp/bspfile.hh @@ -33,6 +33,7 @@ extern "C" { typedef uint8_t byte; #define BSPVERSION 29 +#define BSPHLVERSION 30 #define BSP2RMQVERSION (('B' << 24) | ('S' << 16) | ('P' << 8) | '2') #define BSP2VERSION ('B' | ('S' << 8) | ('P' << 16) | ('2' << 24)) diff --git a/include/qbsp/map.hh b/include/qbsp/map.hh index 036d7809..dcf7ccdd 100644 --- a/include/qbsp/map.hh +++ b/include/qbsp/map.hh @@ -71,6 +71,7 @@ public: int firstface; int numfaces; brushformat_t format; + int contents; mapbrush_t() : firstface(0), numfaces(0), format(brushformat_t::NORMAL) {} const mapface_t &face(int i) const; diff --git a/include/qbsp/qbsp.hh b/include/qbsp/qbsp.hh index e8650b2a..15bb4d2a 100644 --- a/include/qbsp/qbsp.hh +++ b/include/qbsp/qbsp.hh @@ -111,6 +111,8 @@ #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 */ // Special contents flags for the compiler only #define CFLAGS_STRUCTURAL_COVERED_BY_DETAIL (1U << 0) @@ -140,6 +142,7 @@ #define TEX_PHONG_ANGLE_CONCAVE_MASK (255ULL << TEX_PHONG_ANGLE_CONCAVE_SHIFT) /* 8 bit value. if non zero, overrides _phong_angle for concave joints. */ #define TEX_NOBOUNCE (1ULL << 53) /* light doesn't bounce off this face */ #define TEX_NOMINLIGHT (1ULL << 54) /* opt out of minlight on this face */ +#define TEX_NOEXPAND (1ULL << 55) /* don't expand this face for larger clip hulls */ /* * The quality of the bsp output is highly sensitive to these epsilon values. @@ -368,7 +371,11 @@ public: float midsplitSurfFraction; char szMapName[512]; char szBSPName[512]; - char wadPath[512]; + struct + { + char *path; + bool external; //wads from this path are not to be embedded into the bsp, but will instead require the engine to load them from elsewhere. strongly recommended for eg halflife.wad + } wadPaths[16]; vec_t on_epsilon; bool fObjExport; bool fOmitDetail; @@ -380,7 +387,15 @@ public: bool fLeakTest; bool fContentHack; vec_t worldExtent; - + + + ~options_t() { + for (int i = 0; i < sizeof(wadPaths)/sizeof(wadPaths[0]); i++) + { + free(wadPaths[i].path); + wadPaths[i].path = nullptr; + } + } options_t() { memset(this, 0, sizeof(options_t)); @@ -390,7 +405,6 @@ public: this->fVerbose = true; this->szMapName[0] = 0; this->szBSPName[0] = 0; - this->wadPath[0] = 0; /* Default to the original Quake BSP Version... */ this->BSPVersion = BSPVERSION; diff --git a/include/qbsp/wad.hh b/include/qbsp/wad.hh index cf28173e..0c848e7c 100644 --- a/include/qbsp/wad.hh +++ b/include/qbsp/wad.hh @@ -65,7 +65,7 @@ typedef struct wad_s { struct wad_s *next; } wad_t; -wad_t *WADList_AddWad(const char *fpath, wad_t *current_wadlist); +wad_t *WADList_AddWad(const char *fpath, bool external, wad_t *current_wadlist); wad_t *WADList_Init(const char *wadstring); void WADList_Process(const wad_t *wadlist); void WADList_Free(wad_t *wadlist); diff --git a/light/bounce.cc b/light/bounce.cc index d368a148..3e473794 100644 --- a/light/bounce.cc +++ b/light/bounce.cc @@ -46,7 +46,7 @@ using namespace polylib; mutex radlights_lock; map texturecolors; -std::vector radlights; +static std::vector radlights; std::map> radlightsByFacenum; class patch_t { diff --git a/light/entities.cc b/light/entities.cc index 3572d85e..4286b3a0 100644 --- a/light/entities.cc +++ b/light/entities.cc @@ -32,6 +32,7 @@ using strings = std::vector; std::vector all_lights; std::vector all_suns; std::vector entdicts; +static std::vector radlights; const std::vector& GetLights() { return all_lights; @@ -1587,8 +1588,53 @@ static void GL_SubdivideSurface (const bsp2_dface_t *face, const modelinfo_t *fa SubdividePolygon (face, face_modelinfo, bsp, face->numedges, verts[0], surflight_subdivide); } +bool ParseLightsFile(const char *fname) +{ //note: this creates dupes. super bright light! (and super slow, too) + light_t l; + char buf[1024]; + char gah[256]; + const char *t; + float r, g, b; + FILE *f = fopen(fname, "r"); + if(!f) + return false; + while(!feof(f)) + { + fgets(buf, sizeof(buf), f); + t = buf; + + t = COM_Parse(buf); + if (!t) + continue; + entdict_t d = {}; + d["_surface"] = std::string(com_token); + t = COM_Parse(t); + r = atof(com_token); + t = COM_Parse(t); + g = atof(com_token); + t = COM_Parse(t); + b = atof(com_token); + q_snprintf(gah, sizeof(gah), "%f %f %f", r,g,b); + d["_color"] = std::string(gah); + t = COM_Parse(t); + d["light"] = std::string(com_token); + //might be hdr rgbi values here + + radlights.push_back(d); + } + fclose(f); + return true; +} + static void MakeSurfaceLights(const mbsp_t *bsp) { + for (entdict_t &l : radlights) { + light_t entity {}; + entity.epairs = &l; + entity.settings().setSettings(*entity.epairs, false); + surfacelight_templates.push_back(entity); + } + for (light_t &entity : all_lights) { std::string tex = ValueForKey(&entity, "_surface"); if (!tex.empty()) { diff --git a/light/light.cc b/light/light.cc index ef8daa4b..c48cefd1 100644 --- a/light/light.cc +++ b/light/light.cc @@ -772,7 +772,8 @@ static void PrintUsage() " -lux write .lux file\n" " -bspxlit writes rgb data into the bsp itself\n" " -bspx writes both rgb and directions data into the bsp itself\n" -" -novanilla implies -bspxlit. don't write vanilla lighting\n"); +" -novanilla implies -bspxlit. don't write vanilla lighting\n" +" -light filename.rad loads a file\n"); printf("\n"); printf("Overridable worldspawn keys:\n"); @@ -961,6 +962,9 @@ light_main(int argc, const char **argv) write_luxfile |= 2; } else if (!strcmp(argv[i], "-novanilla")) { scaledonly = true; + } else if ( !strcmp( argv[ i ], "-light" ) ) { + if (!ParseLightsFile(argv[++i])) + logprint( "Unable to read surfacelights file %s\n", argv[i] ); } else if ( !strcmp( argv[ i ], "-lmscale" ) ) { lmscaleoverride = argv[++i]; } else if (!strcmp(argv[i], "-soft")) { @@ -1140,6 +1144,14 @@ light_main(int argc, const char **argv) DefaultExtension(source, ".lit"); remove(source); } + + { + StripExtension(source); + DefaultExtension(source, ".rad"); + if (strcmp(source, "lights.rad")) + ParseLightsFile("lights.rad"); //generic/default name + ParseLightsFile(source); //map-specific file name + } StripExtension(source); DefaultExtension(source, ".bsp"); @@ -1189,7 +1201,7 @@ light_main(int argc, const char **argv) if (!onlyents) { - if (loadversion != Q2_BSPVERSION) //mxd. No lit for Quake 2 + if (loadversion != Q2_BSPVERSION && bsp->loadversion != BSPHLVERSION) //mxd. No lit for Quake 2 CheckLitNeeded(cfg); SetupDirt(cfg); diff --git a/light/ltface.cc b/light/ltface.cc index 632b4bd3..6ec38c12 100644 --- a/light/ltface.cc +++ b/light/ltface.cc @@ -3026,7 +3026,7 @@ WriteLightmaps(const mbsp_t *bsp, bsp2_dface_t *face, facesup_t *facesup, const int size = (lightsurf->texsize[0] + 1) * (lightsurf->texsize[1] + 1); // q2 support - if (bsp->loadversion == Q2_BSPVERSION) + if (bsp->loadversion == Q2_BSPVERSION || bsp->loadversion == BSPHLVERSION) size *= 3; byte *out, *lit, *lux; @@ -3084,7 +3084,7 @@ WriteLightmaps(const mbsp_t *bsp, bsp2_dface_t *face, facesup_t *facesup, const *lit++ = color[1]; *lit++ = color[2]; - if (bsp->loadversion == Q2_BSPVERSION) { + if (bsp->loadversion == Q2_BSPVERSION || bsp->loadversion == BSPHLVERSION) { *out++ = color[0]; *out++ = color[1]; *out++ = color[2]; diff --git a/man/qbsp.1 b/man/qbsp.1 index 887d2572..6715e458 100644 --- a/man/qbsp.1 +++ b/man/qbsp.1 @@ -30,7 +30,8 @@ Print out more .map information .IP "\fB-noverbose\fP" Print out almost no information at all .IP "\fB-splitspecial\fP" -Doesn't combine sky and water faces into one large face +Doesn't combine sky and water faces into one large face. +This allows for statically lit water. .IP "\fB-transwater\fP" Computes portal information for transparent water (default) .IP "\fB-notranswater\fP" @@ -53,19 +54,27 @@ Create an old-style QBSP .PTS file (default is new) Makes it a compile error if a leak is detected. .IP "\fB-nopercent\fP" Prevents output of percent completion information +.IP "\fB-hexen2\fP" +Generate a hexen2 bsp. This can be used in addition to -bsp2 to avoid clipnode issues. .IP "\fB-bsp2\fP" Create the output BSP file in BSP2 format. Allows the creation of much larger and more complex maps than the original BSP 29 format). .IP "\fB-2psb\fP" Create the output BSP file in 2PSB format. This an earlier version of the BSP2 format, supported by the RMQ engine (and thus is also known as the -BSP2rmq or RMQe bsp format). original BSP 29 format). +BSP2rmq or RMQe bsp format). +.IP "\fB-hlbsp\fP" +Create the output BSP file in Half-Life's format. +Note that the hull size differences prevent this from being generally usable for the vanilla quake gamecode. +This cannot be used in combination with the -bsp2 argument. .IP "\fB-leakdist [n]\fP" Space between leakfile points (default 2) .IP "\fB-subdivide [n]\fP" -Use different texture subdivision (default 240) +Use different texture subdivision (default 240). Lower values will harm framerates. Higher values may not be supported. DP+FTEQW+QSS support up to 4080 (unless lightmap scaling is in use), but such values will cause other engines to crash-to-console. .IP "\fB-wadpath \fP" -Search this directory for wad files (default is cwd) +Search this directory for wad files (default is cwd). Multiple -wadpath args may be used. This argument is ignored for wads specified using an absolute path. +.IP "\fB-xwadpath \fP" +Like -wadpath, except textures found using the specified path will NOT be embedded into the bsp (equivelent to -notex, but for only textures from specific wads). You should use this for wads like halflife's standard wad files, but q1bsps require an engine extension and players are not nearly as likely to have the same wad version. .IP "\fB-oldrottex\fP" Use old method of texturing rotate_ brushes where the mapper aligns textures for the object at (0 0 0). @@ -74,14 +83,13 @@ 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. .IP "\fB-wrbrushes\fP" (bspx) Includes a list of brushes for brush-based collision. +This allows for arbitrary collision sizes in engines that support it, currently only FTEQW. .IP "\fB-wrbrushesonly\fP" -"-wrbrushes" combined with "-noclip" argument. +"-wrbrushes" combined with "-noclip" argument. This is NOT backwards compatible. .IP "\fB-notex\fP" -Write only placeholder textures, to depend upon replacements. +Write only placeholder textures, to depend upon replacements. This avoids inclusion of third-party copyrighted images inside your maps, but is not backwards compatible but will work in FTEQW and QSS. .IP "\fB-omitdetail\fP" Detail brushes are omitted from the compile. .IP "\fB-convert \fP" diff --git a/qbsp/brush.cc b/qbsp/brush.cc index 7dbc66eb..99d65bb2 100644 --- a/qbsp/brush.cc +++ b/qbsp/brush.cc @@ -33,6 +33,7 @@ typedef struct hullbrush_s { const mapbrush_t *srcbrush; + int contents; int numfaces; vec3_t mins; vec3_t maxs; @@ -382,12 +383,13 @@ CreateBrushFaces(hullbrush_t *hullbrush, const vec3_t rotate_offset, mapface = hullbrush->faces; for (i = 0; i < hullbrush->numfaces; i++, mapface++) { - if (!hullnum) { + if (!hullnum && hullbrush->contents == CONTENTS_HINT) { /* Don't generate hintskip faces */ const mtexinfo_t &texinfo = map.mtexinfos.at(mapface->texinfo); const char *texname = map.miptex.at(texinfo.miptex).c_str(); - if (!Q_strcasecmp(texname, "hintskip")) - continue; + + if (Q_strcasecmp(texname, "hint")) + continue; // anything texname other than "hint" in a hint brush is treated as "hintskip", and discarded } w = BaseWindingForPlane(&mapface->plane); @@ -764,6 +766,8 @@ ExpandBrush(hullbrush_t *hullbrush, vec3_t hull_size[2], face_t *facelist) // expand all of the planes mapface = hullbrush->faces; for (i = 0; i < hullbrush->numfaces; i++, mapface++) { + if (mapface->flags & TEX_NOEXPAND) + continue; VectorCopy(vec3_origin, corner); for (x = 0; x < 3; x++) { if (mapface->plane.normal[x] > 0) @@ -814,28 +818,33 @@ static int Brush_GetContents(const mapbrush_t *mapbrush) { const char *texname; - const mapface_t &mapface = mapbrush->face(0); - const mtexinfo_t &texinfo = map.mtexinfos.at(mapface.texinfo); - texname = map.miptex.at(texinfo.miptex).c_str(); - if (!Q_strcasecmp(texname, "origin")) - return CONTENTS_ORIGIN; - if (!Q_strcasecmp(texname, "hint") || !Q_strcasecmp(texname, "hintskip")) - return CONTENTS_HINT; - if (!Q_strcasecmp(texname, "clip")) - return CONTENTS_CLIP; + //check for strong content indicators + for (int i = 0; i < mapbrush->numfaces; i++) + { + const mapface_t &mapface = mapbrush->face(i); + const mtexinfo_t &texinfo = map.mtexinfos.at(mapface.texinfo); + texname = map.miptex.at(texinfo.miptex).c_str(); - if (texname[0] == '*') { - if (!Q_strncasecmp(texname + 1, "lava", 4)) - return CONTENTS_LAVA; - if (!Q_strncasecmp(texname + 1, "slime", 5)) - return CONTENTS_SLIME; - return CONTENTS_WATER; + if (!Q_strcasecmp(texname, "origin")) + return CONTENTS_ORIGIN; + if (!Q_strcasecmp(texname, "hint")) + return CONTENTS_HINT; + if (!Q_strcasecmp(texname, "clip")) + return CONTENTS_CLIP; + + if (texname[0] == '*') { + if (!Q_strncasecmp(texname + 1, "lava", 4)) + return CONTENTS_LAVA; + if (!Q_strncasecmp(texname + 1, "slime", 5)) + return CONTENTS_SLIME; + return CONTENTS_WATER; + } + + if (!Q_strncasecmp(texname, "sky", 3)) + return CONTENTS_SKY; } - - if (!Q_strncasecmp(texname, "sky", 3)) - return CONTENTS_SKY; - + //and anything else is assumed to be a regular solid. return CONTENTS_SOLID; } @@ -847,7 +856,7 @@ LoadBrush Converts a mapbrush to a bsp brush =============== */ -brush_t *LoadBrush(const mapbrush_t *mapbrush, const vec3_t rotate_offset, const int hullnum) +brush_t *LoadBrush(const mapbrush_t *mapbrush, int contents, const vec3_t rotate_offset, const int hullnum) { hullbrush_t hullbrush; brush_t *brush; @@ -858,6 +867,7 @@ brush_t *LoadBrush(const mapbrush_t *mapbrush, const vec3_t rotate_offset, const Error("brush->faces >= MAX_FACES (%d), source brush on line %d", MAX_FACES, mapbrush->face(0).linenum); + hullbrush.contents = contents; hullbrush.srcbrush = mapbrush; hullbrush.numfaces = mapbrush->numfaces; for (int i=0; inumfaces; i++) @@ -877,7 +887,28 @@ brush_t *LoadBrush(const mapbrush_t *mapbrush, const vec3_t rotate_offset, const return NULL; } - if (options.hexen2) + if (options.BSPVersion == BSPHLVERSION) + { + if (hullnum == 1) { + vec3_t size[2] = { {-16, -16, -36}, {16, 16, 36} }; + ExpandBrush(&hullbrush, size, facelist); + FreeBrushFaces(facelist); + facelist = CreateBrushFaces(&hullbrush, rotate_offset, hullnum); + } + else if (hullnum == 2) { + vec3_t size[2] = { {-32, -32, -32}, {32, 32, 32} }; + ExpandBrush(&hullbrush, size, facelist); + FreeBrushFaces(facelist); + facelist = CreateBrushFaces(&hullbrush, rotate_offset, hullnum); + } + else if (hullnum == 3) { + vec3_t size[2] = { {-16, -16, -18}, {16, 16, 18} }; + ExpandBrush(&hullbrush, size, facelist); + FreeBrushFaces(facelist); + facelist = CreateBrushFaces(&hullbrush, rotate_offset, hullnum); + } + } + else if (options.hexen2) { if (hullnum == 1) { vec3_t size[2] = { {-16, -16, -32}, {16, 16, 24} }; @@ -940,6 +971,7 @@ brush_t *LoadBrush(const mapbrush_t *mapbrush, const vec3_t rotate_offset, const // create the brush brush = (brush_t *)AllocMem(BRUSH, 1, true); + brush->contents = contents; brush->faces = facelist; VectorCopy(hullbrush.mins, brush->mins); VectorCopy(hullbrush.maxs, brush->maxs); @@ -1076,7 +1108,7 @@ Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnum) continue; } - brush_t *brush = LoadBrush(mapbrush, vec3_origin, 0); + brush_t *brush = LoadBrush(mapbrush, contents, vec3_origin, 0); if (brush) { vec3_t origin; VectorAdd(brush->mins, brush->maxs, origin); @@ -1200,7 +1232,7 @@ Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnum) */ if (contents == CONTENTS_CLIP) { if (hullnum <= 0) { - brush_t *brush = LoadBrush(mapbrush, rotate_offset, hullnum); + brush_t *brush = LoadBrush(mapbrush, contents, rotate_offset, hullnum); if (brush) { AddToBounds(dst, brush->mins); AddToBounds(dst, brush->maxs); @@ -1242,12 +1274,11 @@ Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnum) if (hullnum && contents == CONTENTS_SKY) contents = CONTENTS_SOLID; - brush_t *brush = LoadBrush(mapbrush, rotate_offset, hullnum); + brush_t *brush = LoadBrush(mapbrush, contents, rotate_offset, hullnum); if (!brush) continue; dst->numbrushes++; - brush->contents = contents; brush->lmshift = lmshift; brush->cflags = cflags; diff --git a/qbsp/bspfile.cc b/qbsp/bspfile.cc index d2cb2e35..77d56c81 100644 --- a/qbsp/bspfile.cc +++ b/qbsp/bspfile.cc @@ -54,6 +54,7 @@ LoadBSPFile(void) switch (header->version) { case BSPVERSION: + case BSPHLVERSION: MemSize = MemSize_BSP29; break; case BSP2RMQVERSION: diff --git a/qbsp/globals.cc b/qbsp/globals.cc index 6fbc216d..df88b997 100644 --- a/qbsp/globals.cc +++ b/qbsp/globals.cc @@ -132,7 +132,7 @@ const char *rgszWarnings[cWarnings] = { "Point (%.3f %.3f %.3f) off plane by %2.4f", "Couldn't create brush faces", - "Reached occupant at (%.0f %.0f %.0f), no filling performed.", + "Reached occupant \"%s\" at (%.0f %.0f %.0f), no filling performed.", "Portal siding direction is wrong", "New portal was clipped away in CutNodePortals_r near (%.3f %.3f %.3f)", "Winding outside node", diff --git a/qbsp/map.cc b/qbsp/map.cc index 735896d5..c383163e 100644 --- a/qbsp/map.cc +++ b/qbsp/map.cc @@ -204,6 +204,18 @@ IsSkipName(const char *name) return true; if (!Q_strcasecmp(name, "*lavaskip")) return true; + if (!Q_strcasecmp(name, "bevel")) //zhlt compat + return true; + if (!Q_strcasecmp(name, "null")) //zhlt compat + return true; + return false; +} + +static bool +IsNoExpandName(const char *name) +{ + if (!Q_strcasecmp(name, "bevel")) //zhlt compat + return true; return false; } @@ -292,6 +304,8 @@ FindTexinfoEnt(mtexinfo_t *texinfo, const mapentity_t *entity) flags |= TEX_HINT; if (IsSpecialName(texname)) flags |= TEX_SPECIAL; + if (IsNoExpandName(texname)) + flags |= TEX_NOEXPAND; if (atoi(ValueForKey(entity, "_dirt")) == -1) flags |= TEX_NODIRT; if (atoi(ValueForKey(entity, "_bounce")) == -1) @@ -2331,7 +2345,7 @@ TestExpandBrushes(const mapentity_t *src) for (int i = 0; i < src->nummapbrushes; i++) { const mapbrush_t *mapbrush = &src->mapbrush(i); - brush_t *hull1brush = LoadBrush(mapbrush, vec3_origin, 1); + brush_t *hull1brush = LoadBrush(mapbrush, CONTENTS_SOLID, vec3_origin, 1); if (hull1brush != nullptr) hull1brushes.push_back(hull1brush); diff --git a/qbsp/outside.cc b/qbsp/outside.cc index d55235e8..d2e54c39 100644 --- a/qbsp/outside.cc +++ b/qbsp/outside.cc @@ -436,7 +436,7 @@ FillOutside(node_t *node, const int hullnum) Q_assert(leakentity != nullptr); const vec_t *origin = leakentity->origin; - Message(msgWarning, warnMapLeak, origin[0], origin[1], origin[2]); + Message(msgWarning, warnMapLeak, ValueForKey(leakentity, "classname"), origin[0], origin[1], origin[2]); if (map.leakfile) return false; diff --git a/qbsp/qbsp.cc b/qbsp/qbsp.cc index 98f72cba..84bfded5 100644 --- a/qbsp/qbsp.cc +++ b/qbsp/qbsp.cc @@ -567,7 +567,9 @@ CreateHulls(void) CreateSingleHull(1); CreateSingleHull(2); - if (options.hexen2) + if (options.BSPVersion == BSPHLVERSION) + CreateSingleHull(3); + else 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); @@ -680,14 +682,16 @@ PrintOptions(void) " -nooldaxis Uses alternate texture alignment which was default in tyrutils-ericw v0.15.1 and older\n" " -forcegoodtree Force use of expensive processing for SolidBSP stage\n" " -nopercent Prevents output of percent completion information\n" - " -hexen2 Generate a BSP compatible with hexen2 engines\n" " -wrbrushes (bspx) Includes a list of brushes for brush-based collision\n" " -wrbrushesonly -wrbrushes with -noclip\n" + " -hexen2 Generate a BSP compatible with hexen2 engines\n" + " -hlbsp Request output in Half-Life bsp format\n" " -bsp2 Request output in bsp2 format\n" " -2psb Request output in 2psb format (RMQ compatible)\n" " -leakdist [n] Space between leakfile points (default 2)\n" " -subdivide [n] Use different texture subdivision (default 240)\n" - " -wadpath Search this directory for wad files\n" + " -wadpath Search this directory for wad files (mips will be embedded unless -notex)\n" + " -xwadpath Search this directory for wad files (mips will NOT be embedded, avoiding texture license issues)\n" " -oldrottex Use old rotate_ brush texturing aligned at (0 0 0)\n" " -maxnodesize [n]Triggers simpler BSP Splitting when node exceeds size (default 1024, 0 to disable)\n" " -epsilon [n] Customize ON_EPSILON (default 0.0001)\n" @@ -825,7 +829,10 @@ ParseOptions(char *szOptions) options.fbspx_brushes = true; options.fNoclip = true; } - else if (!Q_strcasecmp(szTok, "bsp2")) { + else if (!Q_strcasecmp(szTok, "hlbsp")) { + options.BSPVersion = BSPHLVERSION; + MemSize = MemSize_BSP29; + } else if (!Q_strcasecmp(szTok, "bsp2")) { options.BSPVersion = BSP2VERSION; MemSize = MemSize_BSP2; } else if (!Q_strcasecmp(szTok, "2psb")) { @@ -843,15 +850,25 @@ ParseOptions(char *szOptions) Error("Invalid argument to option %s", szTok); options.dxSubdivide = atoi(szTok2); szTok = szTok2; - } else if (!Q_strcasecmp(szTok, "wadpath")) { + } else if (!Q_strcasecmp(szTok, "wadpath") || !Q_strcasecmp(szTok, "xwadpath")) { szTok2 = GetTok(szTok + strlen(szTok) + 1, szEnd); if (!szTok2) Error("Invalid argument to option %s", szTok); - strcpy(options.wadPath, szTok2); + int i; + for (i = 0; i < sizeof(options.wadPaths)/sizeof(options.wadPaths[0]); i++) + { + if (options.wadPaths[i].path) + continue; + options.wadPaths[i].external = !!Q_strcasecmp(szTok, "wadpath"); + options.wadPaths[i].path = strdup(szTok2); + /* Remove trailing /, if any */ + if (options.wadPaths[i].path[strlen(options.wadPaths[i].path) - 1] == '/') + options.wadPaths[i].path[strlen(options.wadPaths[i].path) - 1] = 0; + break; + } + if (i == sizeof(options.wadPaths)/sizeof(options.wadPaths[0])) + Error("too many -wadpath args"); szTok = szTok2; - /* Remove trailing /, if any */ - if (options.wadPath[strlen(options.wadPath) - 1] == '/') - options.wadPath[strlen(options.wadPath) - 1] = 0; } else if (!Q_strcasecmp(szTok, "oldrottex")) { options.fixRotateObjTexture = false; } else if (!Q_strcasecmp(szTok, "maxnodesize")) { @@ -990,9 +1007,10 @@ InitQBSP(int argc, const char **argv) Message(msgFile, IntroString); /* If no wadpath given, default to the map directory */ - if (options.wadPath[0] == 0) { - strcpy(options.wadPath, options.szMapName); - StripFilename(options.wadPath); + if (!options.wadPaths[0].path) { + options.wadPaths[0].external = false; + options.wadPaths[0].path = strdup(options.szMapName); + StripFilename(options.wadPaths[0].path); } // Remove already existing files diff --git a/qbsp/surfaces.cc b/qbsp/surfaces.cc index 813f90f7..5f471462 100644 --- a/qbsp/surfaces.cc +++ b/qbsp/surfaces.cc @@ -306,7 +306,7 @@ GetEdge(mapentity_t *entity, const vec3_t p1, const vec3_t p2, // search for an existing edge from v2->v1 const std::pair edge_hash_key = std::make_pair(v2, v1); - if (options.BSPVersion == BSPVERSION) { + if (options.BSPVersion == BSPVERSION || options.BSPVersion == BSPHLVERSION) { bsp29_dedge_t *edge; auto it = hashedges.find(edge_hash_key); @@ -464,7 +464,7 @@ EmitFace_Internal(mapentity_t *entity, face_t *face) static void EmitFace(mapentity_t *entity, face_t *face) { - if (options.BSPVersion == BSPVERSION) + if (options.BSPVersion == BSPVERSION || options.BSPVersion == BSPHLVERSION) EmitFace_Internal(entity, face); else EmitFace_Internal(entity, face); @@ -594,7 +594,7 @@ MakeFaceEdges(mapentity_t *entity, node_t *headnode) edges->count = edges->index; } - if (map.cTotal[LUMP_VERTEXES] > 65535 && options.BSPVersion == BSPVERSION) + if (map.cTotal[LUMP_VERTEXES] > 65535 && (options.BSPVersion == BSPVERSION || options.BSPVersion == BSPHLVERSION)) Error("Too many vertices (%d > 65535). Recompile with the \"-bsp2\" flag to lift this restriction.", map.cTotal[LUMP_VERTEXES]); surfedges->data = AllocMem(BSP_SURFEDGE, surfedges->count, true); diff --git a/qbsp/test_qbsp.cc b/qbsp/test_qbsp.cc index 6bec21cb..2eef6fc4 100644 --- a/qbsp/test_qbsp.cc +++ b/qbsp/test_qbsp.cc @@ -115,7 +115,7 @@ TEST(qbsp, duplicatePlanes) { EXPECT_EQ(0, worldspawn.numbrushes); EXPECT_EQ(6, worldspawn.mapbrush(0).numfaces); - brush_t *brush = LoadBrush(&worldspawn.mapbrush(0), vec3_origin, 0); + brush_t *brush = LoadBrush(&worldspawn.mapbrush(0), CONTENTS_SOLID, vec3_origin, 0); ASSERT_NE(nullptr, brush); EXPECT_EQ(6, Brush_NumFaces(brush)); FreeBrush(brush); @@ -141,7 +141,7 @@ static brush_t *load128x128x32Brush() mapentity_t worldspawn = LoadMap(map); Q_assert(1 == worldspawn.nummapbrushes); - brush_t *brush = LoadBrush(&worldspawn.mapbrush(0), vec3_origin, 0); + brush_t *brush = LoadBrush(&worldspawn.mapbrush(0), CONTENTS_SOLID, vec3_origin, 0); Q_assert(nullptr != brush); brush->contents = CONTENTS_SOLID; diff --git a/qbsp/wad.cc b/qbsp/wad.cc index 8c6d2e7e..f8187c9d 100644 --- a/qbsp/wad.cc +++ b/qbsp/wad.cc @@ -31,14 +31,26 @@ static void WADList_AddAnimationFrames(const wad_t *wadlist); static texture_t *textures; +byte thepalette[768] = // Quake palette +{ + 0,0,0,15,15,15,31,31,31,47,47,47,63,63,63,75,75,75,91,91,91,107,107,107,123,123,123,139,139,139,155,155,155,171,171,171,187,187,187,203,203,203,219,219,219,235,235,235,15,11,7,23,15,11,31,23,11,39,27,15,47,35,19,55,43,23,63,47,23,75,55,27,83,59,27,91,67,31,99,75,31,107,83,31,115,87,31,123,95,35,131,103,35,143,111,35,11,11,15,19,19,27,27,27,39,39,39,51,47,47,63,55,55,75,63,63,87,71,71,103,79,79,115,91,91,127,99,99, + 139,107,107,151,115,115,163,123,123,175,131,131,187,139,139,203,0,0,0,7,7,0,11,11,0,19,19,0,27,27,0,35,35,0,43,43,7,47,47,7,55,55,7,63,63,7,71,71,7,75,75,11,83,83,11,91,91,11,99,99,11,107,107,15,7,0,0,15,0,0,23,0,0,31,0,0,39,0,0,47,0,0,55,0,0,63,0,0,71,0,0,79,0,0,87,0,0,95,0,0,103,0,0,111,0,0,119,0,0,127,0,0,19,19,0,27,27,0,35,35,0,47,43,0,55,47,0,67, + 55,0,75,59,7,87,67,7,95,71,7,107,75,11,119,83,15,131,87,19,139,91,19,151,95,27,163,99,31,175,103,35,35,19,7,47,23,11,59,31,15,75,35,19,87,43,23,99,47,31,115,55,35,127,59,43,143,67,51,159,79,51,175,99,47,191,119,47,207,143,43,223,171,39,239,203,31,255,243,27,11,7,0,27,19,0,43,35,15,55,43,19,71,51,27,83,55,35,99,63,43,111,71,51,127,83,63,139,95,71,155,107,83,167,123,95,183,135,107,195,147,123,211,163,139,227,179,151, + 171,139,163,159,127,151,147,115,135,139,103,123,127,91,111,119,83,99,107,75,87,95,63,75,87,55,67,75,47,55,67,39,47,55,31,35,43,23,27,35,19,19,23,11,11,15,7,7,187,115,159,175,107,143,163,95,131,151,87,119,139,79,107,127,75,95,115,67,83,107,59,75,95,51,63,83,43,55,71,35,43,59,31,35,47,23,27,35,19,19,23,11,11,15,7,7,219,195,187,203,179,167,191,163,155,175,151,139,163,135,123,151,123,111,135,111,95,123,99,83,107,87,71,95,75,59,83,63, + 51,67,51,39,55,43,31,39,31,23,27,19,15,15,11,7,111,131,123,103,123,111,95,115,103,87,107,95,79,99,87,71,91,79,63,83,71,55,75,63,47,67,55,43,59,47,35,51,39,31,43,31,23,35,23,15,27,19,11,19,11,7,11,7,255,243,27,239,223,23,219,203,19,203,183,15,187,167,15,171,151,11,155,131,7,139,115,7,123,99,7,107,83,0,91,71,0,75,55,0,59,43,0,43,31,0,27,15,0,11,7,0,0,0,255,11,11,239,19,19,223,27,27,207,35,35,191,43, + 43,175,47,47,159,47,47,143,47,47,127,47,47,111,47,47,95,43,43,79,35,35,63,27,27,47,19,19,31,11,11,15,43,0,0,59,0,0,75,7,0,95,7,0,111,15,0,127,23,7,147,31,7,163,39,11,183,51,15,195,75,27,207,99,43,219,127,59,227,151,79,231,171,95,239,191,119,247,211,139,167,123,59,183,155,55,199,195,55,231,227,87,127,191,255,171,231,255,215,255,255,103,0,0,139,0,0,179,0,0,215,0,0,255,0,0,255,243,147,255,247,199,255,255,255,159,91,83 +}; + static bool -WAD_LoadInfo(wad_t *wad) +WAD_LoadInfo(wad_t *wad, bool external) { wadinfo_t *hdr = &wad->header; - int i, len, lumpinfosize, disksize; + int i, len, lumpinfosize; dmiptex_t miptex; texture_t *tex; + external |= options.fNoTextures; + len = fread(hdr, 1, sizeof(wadinfo_t), wad->file); if (len != sizeof(wadinfo_t)) return false; @@ -62,8 +74,16 @@ WAD_LoadInfo(wad_t *wad) for (i = 0; i < wad->header.numlumps; i++) { fseek(wad->file, wad->lumps[i].filepos, SEEK_SET); len = fread(&miptex, 1, sizeof(miptex), wad->file); + if (len == sizeof(miptex)) { + int w = LittleLong(miptex.width); + int h = LittleLong(miptex.height); + wad->lumps[i].size = sizeof(miptex) + (w>>0)*(h>>0) + (w>>1)*(h>>1) + (w>>2)*(h>>2) + (w>>3)*(h>>3); + if (options.BSPVersion == BSPHLVERSION) + wad->lumps[i].size += 2+3*256; //palette size+palette data + wad->lumps[i].size = (wad->lumps[i].size+3) & ~3; //keep things aligned if we can. + tex = (texture_t *)AllocMem(OTHER, sizeof(texture_t), true); tex->next = textures; textures = tex; @@ -72,31 +92,20 @@ WAD_LoadInfo(wad_t *wad) tex->width = miptex.width; tex->height = miptex.height; + //if we're not going to embed it into the bsp, set its size now so we know how much to actually store. + if (external) + wad->lumps[i].size = wad->lumps[i].disksize = sizeof(dmiptex_t); + //printf("Created texture_t %s %d %d\n", tex->name, tex->width, tex->height); } - } - - if (wad->version == 2) - return true; - - /* - * WAD3 format includes a palette after the mipmap data. - * Reduce the disksize in the lumpinfo so we can treat it like WAD2. - */ - for (i = 0; i < wad->header.numlumps; i++) { - fseek(wad->file, wad->lumps[i].filepos, SEEK_SET); - len = fread(&miptex, 1, sizeof(miptex), wad->file); - if (len != sizeof(miptex)) - return false; - disksize = sizeof(miptex) + (miptex.width * miptex.height / 64 * 85); - if (disksize < wad->lumps[i].disksize) - wad->lumps[i].disksize = disksize; + else + wad->lumps[i].size = 0; } return true; } -wad_t *WADList_AddWad(const char *fpath, wad_t *current_wadlist) +wad_t *WADList_AddWad(const char *fpath, bool external, wad_t *current_wadlist) { wad_t wad = {0}; @@ -104,12 +113,13 @@ wad_t *WADList_AddWad(const char *fpath, wad_t *current_wadlist) if (wad.file) { if (options.fVerbose) Message(msgLiteral, "Opened WAD: %s\n", fpath); - if (WAD_LoadInfo(&wad)) { + if (WAD_LoadInfo(&wad, external)) { wad_t *newwad = (wad_t *)AllocMem(OTHER, sizeof(wad), true); memcpy(newwad, &wad, sizeof(wad)); newwad->next = current_wadlist; // FIXME: leaves file open? + // (currently needed so that mips can be loaded later, as needed) return newwad; } else { @@ -141,18 +151,22 @@ WADList_Init(const char *wadstring) while (*pos && *pos != ';') pos++; - if (!options.wadPath[0] || IsAbsolutePath(fname)) { + if (!options.wadPaths[0].path || IsAbsolutePath(fname)) { fpath = (char *)AllocMem(OTHER, (pos - fname) + 1, false); q_snprintf(fpath, (pos - fname) + 1, "%s", fname); + wadlist = WADList_AddWad(fpath, false, wadlist); + FreeMem(fpath, OTHER, strlen(fpath) + 1); } else { - pathlen = strlen(options.wadPath) + 1 + (pos - fname); - fpath = (char *)AllocMem(OTHER, pathlen + 1, true); - q_snprintf(fpath, pathlen + 1, "%s/%s", options.wadPath, fname); + for (int i = 0; i < sizeof(options.wadPaths)/sizeof(options.wadPaths[0]) && options.wadPaths[i].path; i++) + { + pathlen = strlen(options.wadPaths[i].path) + 1 + (pos - fname); + fpath = (char *)AllocMem(OTHER, pathlen + 1, true); + q_snprintf(fpath, pathlen + 1, "%s/%s", options.wadPaths[i].path, fname); + wadlist = WADList_AddWad(fpath, options.wadPaths[i].external, wadlist); + FreeMem(fpath, OTHER, strlen(fpath) + 1); + } } - - wadlist = WADList_AddWad(fpath, wadlist); - FreeMem(fpath, OTHER, strlen(fpath) + 1); pos++; } @@ -204,10 +218,7 @@ WADList_Process(const wad_t *wadlist) for (i = 0; i < map.nummiptex(); i++) { texture = WADList_FindTexture(wadlist, map.miptex.at(i).c_str()); if (texture) { - if (options.fNoTextures) - texdata->count += sizeof(dmiptex_t); - else - texdata->count += texture->disksize; + texdata->count += texture->size; } } @@ -265,7 +276,7 @@ WAD_LoadLump(const wad_t *wad, const char *name, byte *dest) for (i = 0; i < wad->header.numlumps; i++) { if (!Q_strcasecmp(name, wad->lumps[i].name)) { fseek(wad->file, wad->lumps[i].filepos, SEEK_SET); - if (options.fNoTextures) + if (wad->lumps[i].disksize == sizeof(dmiptex_t)) { size = fread(dest, 1, sizeof(dmiptex_t), wad->file); if (size != sizeof(dmiptex_t)) @@ -274,10 +285,46 @@ WAD_LoadLump(const wad_t *wad, const char *name, byte *dest) ((dmiptex_t*)dest)->offsets[i] = 0; return sizeof(dmiptex_t); } - size = fread(dest, 1, wad->lumps[i].disksize, wad->file); - if (size != wad->lumps[i].disksize) - Error("Failure reading from file"); - return wad->lumps[i].disksize; + + if (wad->lumps[i].size != wad->lumps[i].disksize) + { + logprint("Texture %s is %i bytes in wad, packed to %i bytes in bsp\n", name, wad->lumps[i].disksize, wad->lumps[i].size); + std::vector data(wad->lumps[i].disksize); + size = fread(data.data(), 1, wad->lumps[i].disksize, wad->file); + if (size != wad->lumps[i].disksize) + Error("Failure reading from file"); + auto out = (dmiptex_t *)dest; + auto in = (dmiptex_t *)data.data(); + *out = *in; + out->offsets[0] = sizeof(*out); + out->offsets[1] = out->offsets[0] + (in->width>>0)*(in->height>>0); + out->offsets[2] = out->offsets[1] + (in->width>>1)*(in->height>>1); + out->offsets[3] = out->offsets[2] + (in->width>>2)*(in->height>>2); + auto palofs = out->offsets[3] + (in->width>>3)*(in->height>>3); + memcpy(dest+out->offsets[0], data.data()+(in->offsets[0]), (in->width>>0)*(in->height>>0)); + memcpy(dest+out->offsets[1], data.data()+(in->offsets[1]), (in->width>>1)*(in->height>>1)); + memcpy(dest+out->offsets[2], data.data()+(in->offsets[2]), (in->width>>2)*(in->height>>2)); + memcpy(dest+out->offsets[3], data.data()+(in->offsets[3]), (in->width>>3)*(in->height>>3)); + + if (options.BSPVersion == BSPHLVERSION) + { //palette size. 256 in little endian. + dest[palofs+0] = ((256>>0)&0xff); + dest[palofs+1] = ((256>>8)&0xff); + + //now the palette + if (wad->version == 3) + memcpy(dest+palofs+2, data.data()+(in->offsets[3]+(in->width>>3)*(in->height>>3)+2), 3*256); + else + memcpy(dest+palofs+2, thepalette, 3*256); //FIXME: quake palette or something. + } + } + else + { + size = fread(dest, 1, wad->lumps[i].disksize, wad->file); + if (size != wad->lumps[i].disksize) + Error("Failure reading from file"); + } + return wad->lumps[i].size; } } @@ -292,7 +339,7 @@ WADList_AddAnimationFrames(const wad_t *wadlist) oldcount = map.nummiptex(); for (i = 0; i < oldcount; i++) { - if (map.miptex.at(i)[0] != '+') + if (map.miptex.at(i)[0] != '+' && (options.BSPVersion!=BSPHLVERSION||map.miptex.at(i)[0] != '-')) continue; std::string name = map.miptex.at(i); diff --git a/qbsp/writebsp.cc b/qbsp/writebsp.cc index 801b3fde..2ec664e3 100644 --- a/qbsp/writebsp.cc +++ b/qbsp/writebsp.cc @@ -298,7 +298,7 @@ ExportClipNodes(mapentity_t *entity, node_t *nodes, const int hullnum) model->headnode[hullnum] = clipcount + oldcount; CountClipNodes_r(entity, nodes); - if (clipnodes->count > MAX_BSP_CLIPNODES && options.BSPVersion == BSPVERSION) + if (clipnodes->count > MAX_BSP_CLIPNODES && (options.BSPVersion == BSPVERSION || options.BSPVersion == BSPHLVERSION)) Error("Clipnode count exceeds bsp 29 max (%d > %d)", clipnodes->count, MAX_BSP_CLIPNODES); @@ -313,7 +313,7 @@ ExportClipNodes(mapentity_t *entity, node_t *nodes, const int hullnum) if (diff != 0) { for (i = 1; i < hullnum; i++) model->headnode[i] += diff; - if (options.BSPVersion == BSPVERSION) { + if (options.BSPVersion == BSPVERSION || options.BSPVersion == BSPHLVERSION) { bsp29_dclipnode_t *clipnode = (bsp29_dclipnode_t *)clipnodes->data; for (i = 0; i < oldcount; i++, clipnode++) { if (clipnode->children[0] < MAX_BSP_CLIPNODES) @@ -334,7 +334,7 @@ ExportClipNodes(mapentity_t *entity, node_t *nodes, const int hullnum) } map.cTotal[LUMP_CLIPNODES] = clipcount + oldcount; - if (options.BSPVersion == BSPVERSION) + if (options.BSPVersion == BSPVERSION || options.BSPVersion == BSPHLVERSION) ExportClipNodes_BSP29(entity, nodes); else ExportClipNodes_BSP2(entity, nodes);