Fixed many issues with q2bsp support, it now works properly.
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sun, 8 Jun 2014 16:57:58 +0000 (16:57 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Sun, 8 Jun 2014 16:57:58 +0000 (16:57 +0000)
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
gl_rmain.c
image.c
image.h
model_brush.c
model_brush.h
model_shared.c
model_shared.h
palette.c
palette.h
sv_main.c

index f7e77a3..e8e17f1 100644 (file)
@@ -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};
index 2c15ac9..7b074b7 100644 (file)
@@ -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 c61e976..470ea17 100644 (file)
--- 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)
 {
@@ -359,6 +355,19 @@ qboolean LoadPCX_QWSkin(const unsigned char *f, int filesize, unsigned char *pix
 }
 
 /*
+============
+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;
+}
+
+/*
 =========================================================
 
 TARGA LOADING
@@ -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 cb50ca7..dd555a8 100644 (file)
--- 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);
 
index cb09da6..9f8d70e 100644 (file)
@@ -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;
 
index 37f780a..609a041 100644 (file)
@@ -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
+
 
 
 /*
index 6fd2527..ec6a94a 100644 (file)
@@ -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);
                                }
index 1de9257..04d8cb4 100644 (file)
@@ -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;
 
index 9fb36de..7b6fb8d 100644 (file)
--- 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)
index f904465..1b2cd2e 100644 (file)
--- 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);
index 5fcbae2..d17d20a 100644 (file)
--- 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)
        {