From 5aacdd17d58330d1c6f6a312b72e0118895befb4 Mon Sep 17 00:00:00 2001 From: havoc Date: Sun, 8 Jun 2014 16:57:58 +0000 Subject: [PATCH] Fixed many issues with q2bsp support, it now works properly. Added sv_mapformat_quake2 cvar so that qc can check for this map format and change behavior if needed (also added sv_mapformat_quake3 cvar). git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@12087 d7cf8633-e32d-0410-b094-e92efae38249 --- cl_parse.c | 13 +++++ gl_rmain.c | 5 +- image.c | 59 +++++++++++++++------ image.h | 9 +++- model_brush.c | 139 ++++++++++++++++++++++++++++++++++++++----------- model_brush.h | 3 ++ model_shared.c | 3 -- model_shared.h | 9 ++-- palette.c | 26 +++++++++ palette.h | 2 + sv_main.c | 6 +++ 11 files changed, 215 insertions(+), 59 deletions(-) diff --git a/cl_parse.c b/cl_parse.c index f7e77a3b..e8e17f1c 100644 --- a/cl_parse.c +++ b/cl_parse.c @@ -380,6 +380,7 @@ void CL_KeepaliveMessage (qboolean readmessages) void CL_ParseEntityLump(char *entdata) { + qboolean loadedsky = false; const char *data; char key[128], value[MAX_INPUTLINE]; FOG_clear(); // LordHavoc: no fog until set @@ -408,11 +409,20 @@ void CL_ParseEntityLump(char *entdata) return; // error strlcpy (value, com_token, sizeof (value)); if (!strcmp("sky", key)) + { + loadedsky = true; R_SetSkyBox(value); + } else if (!strcmp("skyname", key)) // non-standard, introduced by QuakeForge... sigh. + { + loadedsky = true; R_SetSkyBox(value); + } else if (!strcmp("qlsky", key)) // non-standard, introduced by QuakeLives (EEK) + { + loadedsky = true; R_SetSkyBox(value); + } else if (!strcmp("fog", key)) { FOG_clear(); // so missing values get good defaults @@ -455,6 +465,9 @@ void CL_ParseEntityLump(char *entdata) r_refdef.fog_height_texturename[63] = 0; } } + + if (!loadedsky && cl.worldmodel->brush.isq2bsp) + R_SetSkyBox("unit1_"); } static const vec3_t defaultmins = {-4096, -4096, -4096}; diff --git a/gl_rmain.c b/gl_rmain.c index 2c15ac96..7b074b7e 100644 --- a/gl_rmain.c +++ b/gl_rmain.c @@ -3400,9 +3400,6 @@ skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags, qboole skinframe->fog = NULL; skinframe->reflect = NULL; skinframe->hasalpha = false; - skinframe->q2flags = image_q2flags; - skinframe->q2value = image_q2value; - skinframe->q2contents = image_q2contents; // we could store the q2animname here too if (ddsbase) @@ -10664,7 +10661,7 @@ static void R_DrawTextureSurfaceList_Sky(int texturenumsurfaces, const msurface_ // in Quake3 maps as it causes problems with q3map2 sky tricks, // and skymasking also looks very bad when noclipping outside the // level, so don't use it then either. - if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->type == mod_brushq1 && r_q1bsp_skymasking.integer && !r_refdef.viewcache.world_novis && !r_trippy.integer) + if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.skymasking && r_q1bsp_skymasking.integer && !r_refdef.viewcache.world_novis && !r_trippy.integer) { R_Mesh_ResetTextureState(); if (skyrendermasked) diff --git a/image.c b/image.c index c61e976a..470ea179 100644 --- a/image.c +++ b/image.c @@ -7,10 +7,6 @@ int image_width; int image_height; -int image_q2flags; -int image_q2value; -int image_q2contents; -char image_q2animname[32]; static void Image_CopyAlphaFromBlueBGRA(unsigned char *outpixels, const unsigned char *inpixels, int w, int h) { @@ -358,6 +354,19 @@ qboolean LoadPCX_QWSkin(const unsigned char *f, int filesize, unsigned char *pix return true; } +/* +============ +LoadPCX +============ +*/ +qboolean LoadPCX_PaletteOnly(const unsigned char *f, int filesize, unsigned char *palette768b) +{ + if (filesize < 768) + return false; + memcpy(palette768b, f + filesize - 768, 768); + return true; +} + /* ========================================================= @@ -761,18 +770,13 @@ static unsigned char *LoadWAL_BGRA (const unsigned char *f, int filesize, int *m image_width = LittleLong(inwal->width); image_height = LittleLong(inwal->height); - image_q2flags = LittleLong(inwal->flags); - image_q2value = LittleLong(inwal->value); - image_q2contents = LittleLong(inwal->contents); - memcpy(image_q2animname, inwal->animname, sizeof(inwal->animname)); - image_q2animname[sizeof(image_q2animname)-1] = 0; if (image_width > 32768 || image_height > 32768 || image_width <= 0 || image_height <= 0) { Con_Printf("LoadWAL: invalid size %ix%i\n", image_width, image_height); return NULL; } - if (filesize < (int) sizeof(q2wal_t) + (int) LittleLong(inwal->offsets[0]) + image_width * image_height) + if (filesize < (int) LittleLong(inwal->offsets[0]) + image_width * image_height) { Con_Print("LoadWAL: invalid WAL file\n"); return NULL; @@ -784,10 +788,37 @@ static unsigned char *LoadWAL_BGRA (const unsigned char *f, int filesize, int *m Con_Printf("LoadWAL: not enough memory for %i by %i image\n", image_width, image_height); return NULL; } - Image_Copy8bitBGRA(f + LittleLong(inwal->offsets[0]), image_buffer, image_width * image_height, palette_bgra_complete); + Image_Copy8bitBGRA(f + LittleLong(inwal->offsets[0]), image_buffer, image_width * image_height, q2palette_bgra_complete); return image_buffer; } +qboolean LoadWAL_GetMetadata(const unsigned char *f, int filesize, int *retwidth, int *retheight, int *retflags, int *retvalue, int *retcontents, char *retanimname32c) +{ + unsigned char *image_buffer; + const q2wal_t *inwal = (const q2wal_t *)f; + + if (filesize < (int) sizeof(q2wal_t)) + { + Con_Print("LoadWAL: invalid WAL file\n"); + *retwidth = 16; + *retheight = 16; + *retflags = 0; + *retvalue = 0; + *retcontents = 0; + memset(retanimname32c, 0, 32); + return false; + } + + *retwidth = LittleLong(inwal->width); + *retheight = LittleLong(inwal->height); + *retflags = LittleLong(inwal->flags); + *retvalue = LittleLong(inwal->value); + *retcontents = LittleLong(inwal->contents); + memcpy(retanimname32c, inwal->animname, 32); + retanimname32c[31] = 0; + return true; +} + void Image_StripImageExtension (const char *in, char *out, size_t size_out) { @@ -797,7 +828,7 @@ void Image_StripImageExtension (const char *in, char *out, size_t size_out) return; ext = FS_FileExtension(in); - if (ext && (!strcmp(ext, "tga") || !strcmp(ext, "pcx") || !strcmp(ext, "lmp") || !strcmp(ext, "png") || !strcmp(ext, "jpg"))) + if (ext && (!strcmp(ext, "tga") || !strcmp(ext, "pcx") || !strcmp(ext, "lmp") || !strcmp(ext, "png") || !strcmp(ext, "jpg") || !strcmp(ext, "wal"))) FS_StripExtension(in, out, size_out); else strlcpy(out, in, size_out); @@ -963,10 +994,6 @@ unsigned char *loadimagepixelsbgra (const char *filename, qboolean complain, qbo int mymiplevel = miplevel ? *miplevel : 0; image_width = 0; image_height = 0; - image_q2flags = 0; - image_q2value = 0; - image_q2contents = 0; - image_q2animname[0] = 0; data = format->loadfunc(f, (int)filesize, &mymiplevel); Mem_Free(f); if (data) diff --git a/image.h b/image.h index cb50ca72..dd555a8f 100644 --- a/image.h +++ b/image.h @@ -2,8 +2,7 @@ #ifndef IMAGE_H #define IMAGE_H -extern int image_width, image_height, image_q2flags, image_q2value, image_q2contents; -extern char image_q2animname[32]; +extern int image_width, image_height; // swizzle components (even converting number of components) and flip images @@ -29,6 +28,12 @@ unsigned char *loadimagepixelsbgra (const char *filename, qboolean complain, qbo // loads an 8bit pcx image into a 296x194x8bit buffer, with cropping as needed qboolean LoadPCX_QWSkin(const unsigned char *f, int filesize, unsigned char *pixels, int outwidth, int outheight); +// loads the palette from an 8bit pcx image into your provided array +qboolean LoadPCX_PaletteOnly(const unsigned char *f, int filesize, unsigned char *palette768b); + +// get the metadata from a Quake2 wal file +qboolean LoadWAL_GetMetadata(const unsigned char *f, int filesize, int *retwidth, int *retheight, int *retflags, int *retvalue, int *retcontents, char *retanimname32c); + // loads a texture, as a texture rtexture_t *loadtextureimage (rtexturepool_t *pool, const char *filename, qboolean complain, int flags, qboolean allowFixtrans, qboolean sRGB); diff --git a/model_brush.c b/model_brush.c index cb09da68..9f8d70e1 100644 --- a/model_brush.c +++ b/model_brush.c @@ -2524,6 +2524,10 @@ static void Mod_Q1BSP_LoadFaces(sizebuf_t *sb) surface->lightmapinfo->texinfo = loadmodel->brushq1.texinfo + texinfoindex; surface->texture = loadmodel->data_textures + surface->lightmapinfo->texinfo->textureindex; + // Q2BSP doesn't use lightmaps on sky or warped surfaces (water), but still has a lightofs of 0 + if (lightmapoffset == 0 && (surface->texture->q2flags & (Q2SURF_SKY | Q2SURF_WARP))) + lightmapoffset = -1; + //surface->flags = surface->texture->flags; //if (LittleShort(in->side)) // surface->flags |= SURF_PLANEBACK; @@ -2598,7 +2602,7 @@ static void Mod_Q1BSP_LoadFaces(sizebuf_t *sb) surface->lightmapinfo->samples = NULL; #if 1 // give non-lightmapped water a 1x white lightmap - if (surface->texture->name[0] == '*' && (surface->lightmapinfo->texinfo->q1flags & TEX_SPECIAL) && ssize <= 256 && tsize <= 256) + if (!loadmodel->brush.isq2bsp && surface->texture->name[0] == '*' && (surface->lightmapinfo->texinfo->q1flags & TEX_SPECIAL) && ssize <= 256 && tsize <= 256) { surface->lightmapinfo->samples = (unsigned char *)Mem_Alloc(loadmodel->mempool, ssize * tsize * 3); surface->lightmapinfo->styles[0] = 0; @@ -3775,9 +3779,12 @@ void Mod_Q1BSP_Load(dp_model_t *mod, void *buffer, void *bufferend) mod->type = mod_brushq1; - mod->brush.isbsp2 = false; mod->brush.ishlbsp = false; + mod->brush.isbsp2rmqe = false; + mod->brush.isbsp2 = false; mod->brush.isq2bsp = false; + mod->brush.isq3bsp = false; + mod->brush.skymasking = true; i = MSG_ReadLittleLong(&sb); switch(i) { @@ -4269,7 +4276,7 @@ static void Mod_Q2BSP_LoadNodes(sizebuf_t *sb) static void Mod_Q2BSP_LoadTexinfo(sizebuf_t *sb) { mtexinfo_t *out; - int i, j, k, count; + int i, j, k, l, count; int structsize = 76; int maxtextures = 1024; // hardcoded limit of quake2 engine, so we may as well use it as an upper bound char filename[MAX_QPATH]; @@ -4307,8 +4314,21 @@ static void Mod_Q2BSP_LoadTexinfo(sizebuf_t *sb) { texture_t *tx = loadmodel->data_textures + j; int q2flags = out->q2flags; - Mod_LoadTextureFromQ3Shader(tx, filename, true, true, MATERIALFLAG_WALL); - tx->q2flags = q2flags; // override the flags from the wal + unsigned char *walfile = NULL; + fs_offset_t walfilesize = 0; + Mod_LoadTextureFromQ3Shader(tx, filename, true, true, TEXF_MIPMAP | TEXF_ISWORLD | TEXF_PICMIP | TEXF_COMPRESS); + // now read the .wal file to get metadata (even if a .tga was overriding it, we still need the wal data) + walfile = FS_LoadFile(filename, tempmempool, true, &walfilesize); + if (walfile) + { + int w, h; + char q2animname32c[32]; + LoadWAL_GetMetadata(walfile, (int)walfilesize, &w, &h, &q2flags, &tx->q2value, &tx->q2contents, q2animname32c); + tx->width = w; + tx->height = h; + tx->q2flags = q2flags; + Mem_Free(walfile); + } // also modify the texture to have the correct contents and such based on flags // note that we create multiple texture_t structures if q2flags differs if (q2flags & Q2SURF_LIGHT) @@ -4322,6 +4342,7 @@ static void Mod_Q2BSP_LoadTexinfo(sizebuf_t *sb) if (q2flags & Q2SURF_SKY) { // sky is a rather specific thing + q2flags &= ~Q2SURF_NODRAW; // quake2 had a slightly different meaning than we have in mind here... tx->basematerialflags = MATERIALFLAG_SKY | MATERIALFLAG_NOSHADOW; tx->supercontents = SUPERCONTENTS_SKY | SUPERCONTENTS_NODROP | SUPERCONTENTS_OPAQUE; tx->surfaceflags = Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT | Q3SURFACEFLAG_NOMARKS | Q3SURFACEFLAG_NODLIGHT | Q3SURFACEFLAG_NOLIGHTMAP; @@ -4329,7 +4350,7 @@ static void Mod_Q2BSP_LoadTexinfo(sizebuf_t *sb) if (q2flags & Q2SURF_WARP) { // we use a scroll instead of a warp - tx->basematerialflags |= MATERIALFLAG_WATERSCROLL; + tx->basematerialflags |= MATERIALFLAG_WATERSCROLL | MATERIALFLAG_FULLBRIGHT; // if it's also transparent, we can enable the WATERSHADER // but we do not set the WATERALPHA flag because we don't // want to honor r_wateralpha in q2bsp @@ -4341,11 +4362,17 @@ static void Mod_Q2BSP_LoadTexinfo(sizebuf_t *sb) { tx->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED; tx->basealpha = 0.3333f; + tx->supercontents &= ~SUPERCONTENTS_OPAQUE; + if (tx->q2contents & Q2CONTENTS_SOLID) + tx->q2contents = (tx->q2contents & ~Q2CONTENTS_SOLID) | Q2CONTENTS_WINDOW; } if (q2flags & Q2SURF_TRANS66) { tx->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED; tx->basealpha = 0.6667f; + tx->supercontents &= ~SUPERCONTENTS_OPAQUE; + if (tx->q2contents & Q2CONTENTS_SOLID) + tx->q2contents = (tx->q2contents & ~Q2CONTENTS_SOLID) | Q2CONTENTS_WINDOW; } if (q2flags & Q2SURF_FLOWING) { @@ -4355,8 +4382,19 @@ static void Mod_Q2BSP_LoadTexinfo(sizebuf_t *sb) } if (q2flags & Q2SURF_NODRAW) { - tx->basematerialflags |= MATERIALFLAG_NODRAW; + tx->basematerialflags = MATERIALFLAG_NODRAW | MATERIALFLAG_NOSHADOW; } + if (tx->q2contents & (Q2CONTENTS_TRANSLUCENT | Q2CONTENTS_MONSTERCLIP | Q2CONTENTS_PLAYERCLIP)) + tx->q2contents |= Q2CONTENTS_DETAIL; + if (!(tx->q2contents & (Q2CONTENTS_SOLID | Q2CONTENTS_WINDOW | Q2CONTENTS_AUX | Q2CONTENTS_LAVA | Q2CONTENTS_SLIME | Q2CONTENTS_WATER | Q2CONTENTS_MIST | Q2CONTENTS_PLAYERCLIP | Q2CONTENTS_MONSTERCLIP | Q2CONTENTS_MIST))) + tx->q2contents |= Q2CONTENTS_SOLID; + if (tx->q2flags & (Q2SURF_HINT | Q2SURF_SKIP)) + tx->q2contents = 0; + tx->supercontents = Mod_Q2BSP_SuperContentsFromNativeContents(loadmodel, tx->q2contents); + // set the current values to the base values + tx->currentframe = tx; + tx->currentskinframe = tx->skinframes[0]; + tx->currentmaterialflags = tx->basematerialflags; loadmodel->num_texturesperskin++; loadmodel->num_textures = loadmodel->num_texturesperskin; } @@ -4393,7 +4431,7 @@ static void Mod_Q2BSP_LoadTexinfo(sizebuf_t *sb) for (j = i;j >= 0 && t->anim_total[0] < (int)(sizeof(t->anim_frames[0])/sizeof(t->anim_frames[0][0]));j = loadmodel->brushq1.texinfo[j].q2nexttexinfo) { // detect looping and stop there - if (loadmodel->brushq1.texinfo[j].textureindex == out->textureindex) + if (t->anim_total[0] && loadmodel->brushq1.texinfo[j].textureindex == out->textureindex) break; t->anim_frames[0][t->anim_total[0]++] = &loadmodel->data_textures[loadmodel->brushq1.texinfo[j].textureindex]; } @@ -4407,16 +4445,13 @@ static void Mod_Q2BSP_LoadTexinfo(sizebuf_t *sb) // note that this can overwrite the rest of the sequence - so if the // start of a sequence is found later than the other parts of the // sequence, it will go back and rewrite them correctly. - for (j = i;j >= 0;j = loadmodel->brushq1.texinfo[j].q2nexttexinfo) + for (k = 0;k < t->anim_total[0];k++) { - texture_t *txj = &loadmodel->data_textures[loadmodel->brushq1.texinfo[j].textureindex]; - txj->animated = t->animated; - txj->anim_total[0] = t->anim_total[0]; - txj->anim_total[1] = t->anim_total[1]; - for (k = 0;k < t->anim_total[0];k++) - txj->anim_frames[0][k] = t->anim_frames[0][k]; - for (k = 0;k < t->anim_total[1];k++) - txj->anim_frames[1][k] = t->anim_frames[1][k]; + texture_t *txk = t->anim_frames[0][k]; + txk->animated = t->animated; + txk->anim_total[0] = t->anim_total[0]; + for (l = 0;l < t->anim_total[0];l++) + txk->anim_frames[0][l] = t->anim_frames[0][l]; } } } @@ -4431,7 +4466,7 @@ static void Mod_Q2BSP_LoadLighting(sizebuf_t *sb) static void Mod_Q2BSP_LoadLeafs(sizebuf_t *sb) { mleaf_t *out; - int i, j, count, firstmarksurface, nummarksurfaces; + int i, j, count, firstmarksurface, nummarksurfaces, firstmarkbrush, nummarkbrushes; int structsize = 28; if (sb->cursize % structsize) @@ -4457,6 +4492,8 @@ static void Mod_Q2BSP_LoadLeafs(sizebuf_t *sb) firstmarksurface = (unsigned short)MSG_ReadLittleShort(sb); nummarksurfaces = (unsigned short)MSG_ReadLittleShort(sb); + firstmarkbrush = (unsigned short)MSG_ReadLittleShort(sb); + nummarkbrushes = (unsigned short)MSG_ReadLittleShort(sb); for (j = 0;j < 4;j++) out->ambient_sound_level[j] = 0; @@ -4478,6 +4515,18 @@ static void Mod_Q2BSP_LoadLeafs(sizebuf_t *sb) out->firstleafsurface = NULL; out->numleafsurfaces = 0; } + + if (firstmarkbrush >= 0 && firstmarkbrush + nummarkbrushes <= loadmodel->brush.num_leafbrushes) + { + out->firstleafbrush = loadmodel->brush.data_leafbrushes + firstmarkbrush; + out->numleafbrushes = nummarkbrushes; + } + else + { + Con_Printf("Mod_Q2BSP_LoadLeafs: invalid leafbrush range %i:%i outside range %i:%i\n", firstmarkbrush, firstmarkbrush+nummarkbrushes, 0, loadmodel->brush.num_leafbrushes); + out->firstleafbrush = NULL; + out->numleafbrushes = 0; + } } } @@ -4509,7 +4558,7 @@ static void Mod_Q2BSP_LoadBrushSides(sizebuf_t *sb) if (sb->cursize % structsize) Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name); count = sb->cursize / structsize; - out = (q3mbrushside_t *)Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_brushsides * sizeof(*out)); + out = (q3mbrushside_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); loadmodel->brush.data_brushsides = out; loadmodel->brush.num_brushsides = count; @@ -4521,9 +4570,17 @@ static void Mod_Q2BSP_LoadBrushSides(sizebuf_t *sb) Host_Error("Mod_Q2BSP_LoadBrushSides: invalid planeindex %i (%i planes)", n, loadmodel->brush.num_planes); out->plane = loadmodel->brush.data_planes + n; n = MSG_ReadLittleShort(sb); - if (n < 0 || n >= loadmodel->num_textures) - Host_Error("Mod_Q2BSP_LoadBrushSides: invalid texinfo index %i (%i texinfos)", n, loadmodel->brushq1.numtexinfo); - out->texture = loadmodel->data_textures + loadmodel->brushq1.texinfo[n].textureindex; + if (n >= 0) + { + if (n >= loadmodel->brushq1.numtexinfo) + Host_Error("Mod_Q2BSP_LoadBrushSides: invalid texinfo index %i (%i texinfos)", n, loadmodel->brushq1.numtexinfo); + out->texture = loadmodel->data_textures + loadmodel->brushq1.texinfo[n].textureindex; + } + else + { + //Con_Printf("Mod_Q2BSP_LoadBrushSides: brushside %i has texinfo index %i < 0, changing to generic texture!\n", i, n); + out->texture = &mod_q1bsp_texture_solid; + } } } @@ -4537,7 +4594,7 @@ static void Mod_Q2BSP_LoadBrushes(sizebuf_t *sb) if (sb->cursize % structsize) Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name); count = sb->cursize / structsize; - out = (q3mbrush_t *)Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_brushes * sizeof(*out)); + out = (q3mbrush_t *)Mem_Alloc(loadmodel->mempool, count * sizeof(*out)); loadmodel->brush.data_brushes = out; loadmodel->brush.num_brushes = count; @@ -4576,6 +4633,8 @@ static void Mod_Q2BSP_LoadBrushes(sizebuf_t *sb) planes[j].q3surfaceflags = out->firstbrushside[j].texture->surfaceflags; planes[j].texture = out->firstbrushside[j].texture; q3surfaceflags |= planes[j].q3surfaceflags; + // LordHavoc: kind of a mean hack here, but we want the surfaces to have the brush contents + out->firstbrushside[j].texture->supercontents = supercontents; } // make the colbrush from the planes out->colbrushf = Collision_NewBrushFromPlanes(loadmodel->mempool, out->numbrushsides, planes, out->texture->supercontents, q3surfaceflags, out->texture, true); @@ -4587,22 +4646,29 @@ static void Mod_Q2BSP_LoadBrushes(sizebuf_t *sb) Mem_Free(planes); } +static void Mod_Q2BSP_LoadPOP(sizebuf_t *sb) +{ + // this is probably a "proof of purchase" lump of some sort, it seems to be 0 size in most bsp files (but not q2dm1.bsp for instance) + sb->readcount = sb->cursize; +} static void Mod_Q2BSP_LoadAreas(sizebuf_t *sb) { // we currently don't use areas, they represent closable doors as vis blockers + sb->readcount = sb->cursize; } static void Mod_Q2BSP_LoadAreaPortals(sizebuf_t *sb) { // we currently don't use areas, they represent closable doors as vis blockers + sb->readcount = sb->cursize; } static void Mod_Q2BSP_LoadSubmodels(sizebuf_t *sb) { mmodel_t *out; int i, count; - int structsize = 56; + int structsize = 48; if (sb->cursize % structsize) Host_Error ("Mod_Q2BSP_LoadSubmodels: funny lump size in %s", loadmodel->name); @@ -4627,7 +4693,6 @@ static void Mod_Q2BSP_LoadSubmodels(sizebuf_t *sb) out->origin[1] = MSG_ReadLittleFloat(sb); out->origin[2] = MSG_ReadLittleFloat(sb); out->headnode[0] = MSG_ReadLittleLong(sb); - out->visleafs = MSG_ReadLittleLong(sb); out->firstface = MSG_ReadLittleLong(sb); out->numfaces = MSG_ReadLittleLong(sb); } @@ -4669,9 +4734,12 @@ static void Mod_Q2BSP_Load(dp_model_t *mod, void *buffer, void *bufferend) mod->type = mod_brushq2; - mod->brush.isbsp2 = false; mod->brush.ishlbsp = false; + mod->brush.isbsp2rmqe = false; + mod->brush.isbsp2 = false; mod->brush.isq2bsp = true; // q1bsp loaders mostly work but we need a few tweaks + mod->brush.isq3bsp = false; + mod->brush.skymasking = true; mod->modeldatatypestring = "Q2BSP"; i = MSG_ReadLittleLong(&sb); @@ -4748,12 +4816,13 @@ static void Mod_Q2BSP_Load(dp_model_t *mod, void *buffer, void *bufferend) Mod_Q2BSP_LoadLighting(&lumpsb[Q2LUMP_LIGHTING]); Mod_Q1BSP_LoadPlanes(&lumpsb[Q2LUMP_PLANES]); Mod_Q2BSP_LoadTexinfo(&lumpsb[Q2LUMP_TEXINFO]); - Mod_Q2BSP_LoadBrushes(&lumpsb[Q2LUMP_BRUSHES]); Mod_Q2BSP_LoadBrushSides(&lumpsb[Q2LUMP_BRUSHSIDES]); + Mod_Q2BSP_LoadBrushes(&lumpsb[Q2LUMP_BRUSHES]); Mod_Q1BSP_LoadFaces(&lumpsb[Q2LUMP_FACES]); Mod_Q1BSP_LoadLeaffaces(&lumpsb[Q2LUMP_LEAFFACES]); Mod_Q2BSP_LoadLeafBrushes(&lumpsb[Q2LUMP_LEAFBRUSHES]); Mod_Q2BSP_LoadVisibility(&lumpsb[Q2LUMP_VISIBILITY]); + Mod_Q2BSP_LoadPOP(&lumpsb[Q2LUMP_POP]); Mod_Q2BSP_LoadAreas(&lumpsb[Q2LUMP_AREAS]); Mod_Q2BSP_LoadAreaPortals(&lumpsb[Q2LUMP_AREAPORTALS]); Mod_Q2BSP_LoadLeafs(&lumpsb[Q2LUMP_LEAFS]); @@ -4811,6 +4880,7 @@ static void Mod_Q2BSP_Load(dp_model_t *mod, void *buffer, void *bufferend) mod = loadmodel; for (i = 0;i < loadmodel->brush.numsubmodels;i++) { + mnode_t *rootnode = NULL; int firstbrush = loadmodel->brush.num_brushes, lastbrush = 0; if (i > 0) { @@ -4848,11 +4918,17 @@ static void Mod_Q2BSP_Load(dp_model_t *mod, void *buffer, void *bufferend) mod->nummodelsurfaces = bm->numfaces; // set node/leaf parents for this submodel - Mod_Q1BSP_LoadNodes_RecursiveSetParent(mod->brush.data_nodes + bm->headnode[0], NULL); + // note: if the root of this submodel is a leaf (headnode[0] < 0) then there is nothing to do... + // (this happens in base3.bsp) + if (bm->headnode[0] >= 0) + rootnode = mod->brush.data_nodes + bm->headnode[0]; + else + rootnode = (mnode_t*)(mod->brush.data_leafs + -1 - bm->headnode[0]); + Mod_Q1BSP_LoadNodes_RecursiveSetParent(rootnode, NULL); // make the model surface list (used by shadowing/lighting) mod->sortedmodelsurfaces = (int *)datapointer;datapointer += mod->nummodelsurfaces * sizeof(int); - Mod_Q2BSP_FindSubmodelBrushRange_r(mod, mod->brush.data_nodes + bm->headnode[0], &firstbrush, &lastbrush); + Mod_Q2BSP_FindSubmodelBrushRange_r(mod, rootnode, &firstbrush, &lastbrush); if (firstbrush <= lastbrush) { mod->firstmodelbrush = firstbrush; @@ -7615,6 +7691,11 @@ static void Mod_Q3BSP_Load(dp_model_t *mod, void *buffer, void *bufferend) mod->modeldatatypestring = "Q3BSP"; mod->type = mod_brushq3; + mod->brush.ishlbsp = false; + mod->brush.isbsp2rmqe = false; + mod->brush.isbsp2 = false; + mod->brush.isq2bsp = false; + mod->brush.isq3bsp = true; mod->numframes = 2; // although alternate textures are not supported it is annoying to complain about no such frame 1 mod->numskins = 1; diff --git a/model_brush.h b/model_brush.h index 37f780ad..609a041e 100644 --- a/model_brush.h +++ b/model_brush.h @@ -324,6 +324,9 @@ typedef struct q2dmodel_s #define Q2SURF_FLOWING 0x40 // scroll towards angle #define Q2SURF_NODRAW 0x80 // don't bother referencing the texture +#define Q2SURF_HINT 0x100 // make a primary bsp splitter +#define Q2SURF_SKIP 0x200 // completely ignore, allowing non-closed brushes + /* diff --git a/model_shared.c b/model_shared.c index 6fd25273..ec6a94ab 100644 --- a/model_shared.c +++ b/model_shared.c @@ -2747,9 +2747,6 @@ nothing GL_ZERO GL_ONE { if(texture->skinframes[0]->hasalpha) texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW; - texture->q2flags = texture->skinframes[0]->q2flags; - texture->q2value = texture->skinframes[0]->q2value; - texture->q2contents = texture->skinframes[0]->q2contents; if (texture->q2contents) texture->supercontents = Mod_Q2BSP_SuperContentsFromNativeContents(loadmodel, texture->q2contents); } diff --git a/model_shared.h b/model_shared.h index 1de92574..04d8cb4f 100644 --- a/model_shared.h +++ b/model_shared.h @@ -82,11 +82,6 @@ typedef struct skinframe_s qboolean qgeneratemerged; qboolean qgeneratenmap; qboolean qgenerateglow; - // for q2 wal files we have some extra info - int q2flags; - int q2value; - int q2contents; - // we could also store the q2animname from the wal but we have no current need of it } skinframe_t; @@ -767,6 +762,10 @@ typedef struct model_brush_s qboolean isbsp2; // true if this model is a Quake2 .bsp file (IBSP38) qboolean isq2bsp; + // true if this model is a Quake3 .bsp file (IBSP46) + qboolean isq3bsp; + // true if this model is a Quake1/Quake2 .bsp file where skymasking capability exists + qboolean skymasking; // string of entity definitions (.map format) char *entities; diff --git a/palette.c b/palette.c index 9fb36de3..7b6fb8d6 100644 --- a/palette.c +++ b/palette.c @@ -1,5 +1,6 @@ #include "quakedef.h" +#include "image.h" cvar_t r_colormap_palette = {0, "r_colormap_palette", "gfx/colormap_palette.lmp", "name of a palette lmp file to override the shirt/pants colors of player models. It consists of 16 shirt colors, 16 scoreboard shirt colors, 16 pants colors and 16 scoreboard pants colors"}; @@ -22,6 +23,8 @@ unsigned int palette_bgra_transparent[256]; unsigned int palette_bgra_embeddedpic[256]; unsigned char palette_featureflags[256]; +unsigned int q2palette_bgra_complete[256]; + // John Carmack said the quake palette.lmp can be considered public domain because it is not an important asset to id, so I include it here as a fallback if no external palette file is found. unsigned char host_quakepal[768] = { @@ -183,6 +186,27 @@ static void Palette_SetupSpecialPalettes(void) palette_bgra_font[0] = 0; } +static void Palette_LoadQ2Colormap(void) +{ + fs_offset_t filesize; + unsigned char * q2colormapfile = FS_LoadFile("pics/colormap.pcx", tempmempool, true, &filesize); + if (q2colormapfile && filesize >= 768) + { + unsigned char q2palette_rgb[256][3]; + unsigned char *out = (unsigned char *) q2palette_bgra_complete; // palette is accessed as 32bit for speed reasons, but is created as 8bit bytes + int i; + LoadPCX_PaletteOnly(q2colormapfile, filesize, q2palette_rgb[0]); + for (i = 0;i < 256;i++) + { + out[i*4+2] = q2palette_rgb[i][0]; + out[i*4+1] = q2palette_rgb[i][1]; + out[i*4+0] = q2palette_rgb[i][2]; + out[i*4+3] = 255; + } + Mem_Free(q2colormapfile); + } +} + void BuildGammaTable8(float prescale, float gamma, float scale, float base, float contrastboost, unsigned char *out, int rampsize) { int i, adjusted; @@ -326,6 +350,8 @@ static void Palette_Load(void) } Palette_SetupSpecialPalettes(); + + Palette_LoadQ2Colormap(); } void Palette_Init(void) diff --git a/palette.h b/palette.h index f9044653..1b2cd2e7 100644 --- a/palette.h +++ b/palette.h @@ -29,6 +29,8 @@ extern unsigned int palette_bgra_transparent[256]; extern unsigned int palette_bgra_embeddedpic[256]; extern unsigned char palette_featureflags[256]; +extern unsigned int q2palette_bgra_complete[256]; + // used by hardware gamma functions in vid_* files void BuildGammaTable8(float prescale, float gamma, float scale, float base, float contrastboost, unsigned char *out, int rampsize); void BuildGammaTable16(float prescale, float gamma, float scale, float base, float contrastboost, unsigned short *out, int rampsize); diff --git a/sv_main.c b/sv_main.c index 5fcbae2e..d17d20a1 100644 --- a/sv_main.c +++ b/sv_main.c @@ -193,6 +193,8 @@ cvar_t sv_autodemo_perclient_nameformat = {CVAR_SAVE, "sv_autodemo_perclient_nam cvar_t sv_autodemo_perclient_discardable = {CVAR_SAVE, "sv_autodemo_perclient_discardable", "0", "Allow game code to decide whether a demo should be kept or discarded."}; cvar_t halflifebsp = {0, "halflifebsp", "0", "indicates the current map is hlbsp format (useful to know because of different bounding box sizes)"}; +cvar_t sv_mapformat_is_quake2 = {0, "sv_mapformat_is_quake2", "0", "indicates the current map is q2bsp format (useful to know because of different entity behaviors, .frame on submodels and other things)"}; +cvar_t sv_mapformat_is_quake3 = {0, "sv_mapformat_is_quake3", "0", "indicates the current map is q2bsp format (useful to know because of different entity behaviors)"}; server_t sv; server_static_t svs; @@ -603,6 +605,8 @@ void SV_Init (void) Cvar_RegisterVariable (&sv_autodemo_perclient_discardable); Cvar_RegisterVariable (&halflifebsp); + Cvar_RegisterVariable (&sv_mapformat_is_quake2); + Cvar_RegisterVariable (&sv_mapformat_is_quake3); sv_mempool = Mem_AllocPool("server", 0, NULL); } @@ -3343,6 +3347,8 @@ void SV_SpawnServer (const char *server) cls.signon = 0; Cvar_SetValue("halflifebsp", worldmodel->brush.ishlbsp); + Cvar_SetValue("sv_mapformat_is_quake2", worldmodel->brush.isq2bsp); + Cvar_SetValue("sv_mapformat_is_quake3", worldmodel->brush.isq3bsp); if(*sv_random_seed.string) { -- 2.39.2