]> git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_brush.c
added cvar r_fixtrans_auto to automatically call fixtrans on all textures that are...
[xonotic/darkplaces.git] / model_brush.c
index 096c0ee49724b22c73e4208630282ca5652649bd..ebcd7bf1cc40995351e277e4e6dfa9465ae8ad87 100644 (file)
@@ -629,7 +629,7 @@ static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int nu
        double t1, t2;
 
        // variables that need to be stored on the stack when recursing
-       dclipnode_t *node;
+       mclipnode_t *node;
        int side;
        double midf, mid[3];
 
@@ -824,10 +824,26 @@ static void Mod_Q1BSP_TraceBox(struct model_s *model, int frame, trace_t *trace,
                rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
        else if (model->brush.ismcbsp)
        {
-               if (boxsize[2] < 48) // pick the nearest of 40 or 56
-                       rhc.hull = &model->brushq1.hulls[2]; // 16x16x40
-               else
-                       rhc.hull = &model->brushq1.hulls[1]; // 16x16x56
+               int i;
+               float vdist, dist;
+               int vdisti = 0;
+
+               vdist = 0;      // shut up compiler warning
+
+       // find the closest hull size (this algorithm probably sucks, a qc field to override it might be in order...)
+               for (i = 1; i < model->brushq1.numhulls; i++)
+               {
+                       dist = fabs(model->brushq1.hulls[i].clip_size[0] - boxsize[0]) +
+                                       fabs(model->brushq1.hulls[i].clip_size[1] - boxsize[1]) +
+                                       fabs(model->brushq1.hulls[i].clip_size[2] - boxsize[2]) * 0.25;
+
+                       if (!vdisti || dist < vdist)
+                       {
+                               vdisti = i;
+                               vdist = dist;
+                       }
+               }
+               rhc.hull = &model->brushq1.hulls[vdisti];
        }
        else if (model->brush.ishlbsp)
        {
@@ -925,7 +941,7 @@ void Collision_ClipTrace_Box(trace_t *trace, const vec3_t cmins, const vec3_t cm
 #else
        RecursiveHullCheckTraceInfo_t rhc;
        static hull_t box_hull;
-       static dclipnode_t box_clipnodes[6];
+       static mclipnode_t box_clipnodes[6];
        static mplane_t box_planes[6];
        // fill in a default trace
        memset(&rhc, 0, sizeof(rhc));
@@ -1378,6 +1394,14 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l)
                        tx->surfaceflags = mod_q1bsp_texture_solid.surfaceflags;
                }
                tx->currentframe = tx;
+
+               // clear water settings
+               tx->reflectmin = 0;
+               tx->reflectmax = 1;
+               tx->refractfactor = 1;
+               Vector4Set(tx->refractcolor4f, 1, 1, 1, 1);
+               tx->reflectfactor = 1;
+               Vector4Set(tx->reflectcolor4f, 1, 1, 1, 1);
        }
 
        if (!m)
@@ -1435,7 +1459,7 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l)
                }
 
                if ((mtwidth & 15) || (mtheight & 15))
-                       Con_DPrintf("%s: warning: texture \"%s\" in \"%s\" is not 16 aligned\n", loadmodel->name, dmiptex->name);
+                       Con_DPrintf("%s: warning: texture \"%s\" is not 16 aligned\n", loadmodel->name, dmiptex->name);
 
                // LordHavoc: force all names to lowercase
                for (j = 0;name[j];j++)
@@ -1486,7 +1510,7 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l)
                        {
                                if (loadmodel->isworldmodel)
                                {
-                                       data = loadimagepixels(tx->name, false, 0, 0);
+                                       data = loadimagepixels(tx->name, false, 0, 0, false);
                                        if (data)
                                        {
                                                R_Q1BSP_LoadSplitSky(data, image_width, image_height, 4);
@@ -1498,9 +1522,9 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l)
                        }
                        else
                        {
-                               skinframe = R_SkinFrame_LoadExternal(gamemode == GAME_TENEBRAE ? tx->name : va("textures/%s/%s", mapname, tx->name), TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | (r_picmipworld.integer ? TEXF_PICMIP : 0), false);
+                               skinframe = R_SkinFrame_LoadExternal(gamemode == GAME_TENEBRAE ? tx->name : va("textures/%s/%s", mapname, tx->name), TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | (r_picmipworld.integer ? TEXF_PICMIP : 0) | TEXF_COMPRESS, false);
                                if (!skinframe)
-                                       skinframe = R_SkinFrame_LoadExternal(gamemode == GAME_TENEBRAE ? tx->name : va("textures/%s", tx->name), TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | (r_picmipworld.integer ? TEXF_PICMIP : 0), false);
+                                       skinframe = R_SkinFrame_LoadExternal(gamemode == GAME_TENEBRAE ? tx->name : va("textures/%s", tx->name), TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | (r_picmipworld.integer ? TEXF_PICMIP : 0) | TEXF_COMPRESS, false);
                                if (!skinframe)
                                {
                                        // did not find external texture, load it from the bsp or wad3
@@ -1537,7 +1561,7 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l)
                                if (strncmp(tx->name,"*lava",5)
                                 && strncmp(tx->name,"*teleport",9)
                                 && strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
-                                       tx->basematerialflags |= MATERIALFLAG_WATERALPHA | MATERIALFLAG_NOSHADOW;
+                                       tx->basematerialflags |= MATERIALFLAG_WATERALPHA | MATERIALFLAG_NOSHADOW | MATERIALFLAG_WATERSHADER;
                                tx->basematerialflags |= MATERIALFLAG_WATER | MATERIALFLAG_LIGHTBOTHSIDES | MATERIALFLAG_NOSHADOW;
                        }
                        else if (!strncmp(tx->name, "sky", 3))
@@ -1761,13 +1785,13 @@ static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
        int i, j, k;
        if (!data)
                return;
-       if (!COM_ParseToken_Simple(&data, false))
+       if (!COM_ParseToken_Simple(&data, false, false))
                return; // error
        if (com_token[0] != '{')
                return; // error
        while (1)
        {
-               if (!COM_ParseToken_Simple(&data, false))
+               if (!COM_ParseToken_Simple(&data, false, false))
                        return; // error
                if (com_token[0] == '}')
                        break; // end of worldspawn
@@ -1777,7 +1801,7 @@ static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
                        strlcpy(key, com_token, sizeof(key));
                while (key[strlen(key)-1] == ' ') // remove trailing spaces
                        key[strlen(key)-1] = 0;
-               if (!COM_ParseToken_Simple(&data, false))
+               if (!COM_ParseToken_Simple(&data, false, false))
                        return; // error
                dpsnprintf(value, sizeof(value), "%s", com_token);
                if (!strcmp("wad", key)) // for HalfLife maps
@@ -2167,13 +2191,14 @@ static qboolean Mod_Q1BSP_AllocLightmapBlock(int *lineused, int totalwidth, int
        return true;
 }
 
+extern cvar_t gl_max_size;
 static void Mod_Q1BSP_LoadFaces(lump_t *l)
 {
        dface_t *in;
        msurface_t *surface;
-       int i, j, count, surfacenum, planenum, smax, tmax, ssize, tsize, firstedge, numedges, totalverts, totaltris, lightmapnumber;
-       float texmins[2], texmaxs[2], val, lightmaptexcoordscale;
-#define LIGHTMAPSIZE 256
+       int i, j, count, surfacenum, planenum, smax, tmax, ssize, tsize, firstedge, numedges, totalverts, totaltris, lightmapnumber, lightmapsize, totallightmapsamples;
+       float texmins[2], texmaxs[2], val;
+#define LIGHTMAPSIZE 1024
        rtexture_t *lightmaptexture, *deluxemaptexture;
        int lightmap_lineused[LIGHTMAPSIZE];
 
@@ -2190,7 +2215,7 @@ static void Mod_Q1BSP_LoadFaces(lump_t *l)
        totaltris = 0;
        for (surfacenum = 0, in = (dface_t *)(mod_base + l->fileofs);surfacenum < count;surfacenum++, in++)
        {
-               numedges = LittleShort(in->numedges);
+               numedges = (unsigned short)LittleShort(in->numedges);
                totalverts += numedges;
                totaltris += numedges - 2;
        }
@@ -2200,7 +2225,8 @@ static void Mod_Q1BSP_LoadFaces(lump_t *l)
        lightmaptexture = NULL;
        deluxemaptexture = r_texture_blanknormalmap;
        lightmapnumber = 1;
-       lightmaptexcoordscale = 1.0f / (float)LIGHTMAPSIZE;
+       lightmapsize = bound(256, gl_max_size.integer, LIGHTMAPSIZE);
+       totallightmapsamples = 0;
 
        totalverts = 0;
        totaltris = 0;
@@ -2210,16 +2236,16 @@ static void Mod_Q1BSP_LoadFaces(lump_t *l)
 
                // FIXME: validate edges, texinfo, etc?
                firstedge = LittleLong(in->firstedge);
-               numedges = LittleShort(in->numedges);
+               numedges = (unsigned short)LittleShort(in->numedges);
                if ((unsigned int) firstedge > (unsigned int) loadmodel->brushq1.numsurfedges || (unsigned int) numedges > (unsigned int) loadmodel->brushq1.numsurfedges || (unsigned int) firstedge + (unsigned int) numedges > (unsigned int) loadmodel->brushq1.numsurfedges)
                        Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)", firstedge, numedges, loadmodel->brushq1.numsurfedges);
-               i = LittleShort(in->texinfo);
+               i = (unsigned short)LittleShort(in->texinfo);
                if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
                        Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)", i, loadmodel->brushq1.numtexinfo);
                surface->lightmapinfo->texinfo = loadmodel->brushq1.texinfo + i;
                surface->texture = surface->lightmapinfo->texinfo->texture;
 
-               planenum = LittleShort(in->planenum);
+               planenum = (unsigned short)LittleShort(in->planenum);
                if ((unsigned int) planenum >= (unsigned int) loadmodel->brush.num_planes)
                        Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)", planenum, loadmodel->brush.num_planes);
 
@@ -2317,53 +2343,94 @@ static void Mod_Q1BSP_LoadFaces(lump_t *l)
                // check if we should apply a lightmap to this
                if (!(surface->lightmapinfo->texinfo->flags & TEX_SPECIAL) || surface->lightmapinfo->samples)
                {
-                       int i, iu, iv, lightmapx, lightmapy;
-                       float u, v, ubase, vbase, uscale, vscale;
-
                        if (ssize > 256 || tsize > 256)
                                Host_Error("Bad surface extents");
+
+                       if (lightmapsize < ssize)
+                               lightmapsize = ssize;
+                       if (lightmapsize < tsize)
+                               lightmapsize = tsize;
+
+                       totallightmapsamples += ssize*tsize;
+
                        // force lightmap upload on first time seeing the surface
+                       //
+                       // additionally this is used by the later code to see if a
+                       // lightmap is needed on this surface (rather than duplicating the
+                       // logic above)
                        surface->cached_dlight = true;
-                       // stainmap for permanent marks on walls
-                       surface->lightmapinfo->stainsamples = (unsigned char *)Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
-                       // clear to white
-                       memset(surface->lightmapinfo->stainsamples, 255, ssize * tsize * 3);
+               }
+       }
 
-                       // find a place for this lightmap
-                       if (!lightmaptexture || !Mod_Q1BSP_AllocLightmapBlock(lightmap_lineused, LIGHTMAPSIZE, LIGHTMAPSIZE, ssize, tsize, &lightmapx, &lightmapy))
+       // small maps (such as ammo boxes especially) don't need big lightmap
+       // textures, so this code tries to guess a good size based on
+       // totallightmapsamples (size of the lightmaps lump basically), as well as
+       // trying to max out the gl_max_size if there is a lot of lightmap data to
+       // store
+       // additionally, never choose a lightmapsize that is smaller than the
+       // largest surface encountered (as it would fail)
+       // and finally, limit it to the size of our lineused array
+       i = lightmapsize;
+       for (lightmapsize = 64;lightmapsize < LIGHTMAPSIZE && (lightmapsize < i || (lightmapsize < gl_max_size.integer && totallightmapsamples*2 > lightmapsize*lightmapsize));lightmapsize*=2)
+               ;
+
+       // now that we've decided the lightmap texture size, we can do the rest
+       if (cls.state != ca_dedicated)
+       {
+               for (surfacenum = 0, surface = loadmodel->data_surfaces;surfacenum < count;surfacenum++, surface++)
+               {
+                       // check if we should apply a lightmap to this
+                       if (surface->cached_dlight)
                        {
-                               // allocate a texture pool if we need it
-                               if (loadmodel->texturepool == NULL && cls.state != ca_dedicated)
-                                       loadmodel->texturepool = R_AllocTexturePool();
-                               // could not find room, make a new lightmap
-                               lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%i", lightmapnumber), LIGHTMAPSIZE, LIGHTMAPSIZE, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
-                               if (loadmodel->brushq1.nmaplightdata)
-                                       deluxemaptexture = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%i", lightmapnumber), LIGHTMAPSIZE, LIGHTMAPSIZE, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
-                               lightmapnumber++;
-                               memset(lightmap_lineused, 0, sizeof(lightmap_lineused));
-                               Mod_Q1BSP_AllocLightmapBlock(lightmap_lineused, LIGHTMAPSIZE, LIGHTMAPSIZE, ssize, tsize, &lightmapx, &lightmapy);
-                       }
+                               int i, iu, iv, lightmapx, lightmapy;
+                               float u, v, ubase, vbase, uscale, vscale;
 
-                       surface->lightmaptexture = lightmaptexture;
-                       surface->deluxemaptexture = deluxemaptexture;
-                       surface->lightmapinfo->lightmaporigin[0] = lightmapx;
-                       surface->lightmapinfo->lightmaporigin[1] = lightmapy;
+                               smax = surface->lightmapinfo->extents[0] >> 4;
+                               tmax = surface->lightmapinfo->extents[1] >> 4;
+                               ssize = (surface->lightmapinfo->extents[0] >> 4) + 1;
+                               tsize = (surface->lightmapinfo->extents[1] >> 4) + 1;
 
-                       ubase = lightmapx * lightmaptexcoordscale;
-                       vbase = lightmapy * lightmaptexcoordscale;
-                       uscale = lightmaptexcoordscale;
-                       vscale = lightmaptexcoordscale;
+                               // stainmap for permanent marks on walls
+                               surface->lightmapinfo->stainsamples = (unsigned char *)Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
+                               // clear to white
+                               memset(surface->lightmapinfo->stainsamples, 255, ssize * tsize * 3);
 
-                       for (i = 0;i < surface->num_vertices;i++)
-                       {
-                               u = ((DotProduct(((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3), surface->lightmapinfo->texinfo->vecs[0]) + surface->lightmapinfo->texinfo->vecs[0][3]) + 8 - surface->lightmapinfo->texturemins[0]) * (1.0 / 16.0);
-                               v = ((DotProduct(((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3), surface->lightmapinfo->texinfo->vecs[1]) + surface->lightmapinfo->texinfo->vecs[1][3]) + 8 - surface->lightmapinfo->texturemins[1]) * (1.0 / 16.0);
-                               (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 0] = u * uscale + ubase;
-                               (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 1] = v * vscale + vbase;
-                               // LordHavoc: calc lightmap data offset for vertex lighting to use
-                               iu = (int) u;
-                               iv = (int) v;
-                               (loadmodel->surfmesh.data_lightmapoffsets + surface->num_firstvertex)[i] = (bound(0, iv, tmax) * ssize + bound(0, iu, smax)) * 3;
+                               // find a place for this lightmap
+                               if (!lightmaptexture || !Mod_Q1BSP_AllocLightmapBlock(lightmap_lineused, lightmapsize, lightmapsize, ssize, tsize, &lightmapx, &lightmapy))
+                               {
+                                       // allocate a texture pool if we need it
+                                       if (loadmodel->texturepool == NULL)
+                                               loadmodel->texturepool = R_AllocTexturePool();
+                                       // could not find room, make a new lightmap
+                                       lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%i", lightmapnumber), lightmapsize, lightmapsize, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
+                                       if (loadmodel->brushq1.nmaplightdata)
+                                               deluxemaptexture = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%i", lightmapnumber), lightmapsize, lightmapsize, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
+                                       lightmapnumber++;
+                                       memset(lightmap_lineused, 0, sizeof(lightmap_lineused));
+                                       Mod_Q1BSP_AllocLightmapBlock(lightmap_lineused, lightmapsize, lightmapsize, ssize, tsize, &lightmapx, &lightmapy);
+                               }
+
+                               surface->lightmaptexture = lightmaptexture;
+                               surface->deluxemaptexture = deluxemaptexture;
+                               surface->lightmapinfo->lightmaporigin[0] = lightmapx;
+                               surface->lightmapinfo->lightmaporigin[1] = lightmapy;
+
+                               uscale = 1.0f / (float)lightmapsize;
+                               vscale = 1.0f / (float)lightmapsize;
+                               ubase = lightmapx * uscale;
+                               vbase = lightmapy * vscale;
+
+                               for (i = 0;i < surface->num_vertices;i++)
+                               {
+                                       u = ((DotProduct(((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3), surface->lightmapinfo->texinfo->vecs[0]) + surface->lightmapinfo->texinfo->vecs[0][3]) + 8 - surface->lightmapinfo->texturemins[0]) * (1.0 / 16.0);
+                                       v = ((DotProduct(((loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex) + i * 3), surface->lightmapinfo->texinfo->vecs[1]) + surface->lightmapinfo->texinfo->vecs[1][3]) + 8 - surface->lightmapinfo->texturemins[1]) * (1.0 / 16.0);
+                                       (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 0] = u * uscale + ubase;
+                                       (loadmodel->surfmesh.data_texcoordlightmap2f + 2 * surface->num_firstvertex)[i * 2 + 1] = v * vscale + vbase;
+                                       // LordHavoc: calc lightmap data offset for vertex lighting to use
+                                       iu = (int) u;
+                                       iv = (int) v;
+                                       (loadmodel->surfmesh.data_lightmapoffsets + surface->num_firstvertex)[i] = (bound(0, iv, tmax) * ssize + bound(0, iu, smax)) * 3;
+                               }
                        }
                }
        }
@@ -2432,16 +2499,40 @@ static void Mod_Q1BSP_LoadNodes(lump_t *l)
                p = LittleLong(in->planenum);
                out->plane = loadmodel->brush.data_planes + p;
 
-               out->firstsurface = LittleShort(in->firstface);
-               out->numsurfaces = LittleShort(in->numfaces);
+               out->firstsurface = (unsigned short)LittleShort(in->firstface);
+               out->numsurfaces = (unsigned short)LittleShort(in->numfaces);
 
                for (j=0 ; j<2 ; j++)
                {
-                       p = LittleShort(in->children[j]);
-                       if (p >= 0)
-                               out->children[j] = loadmodel->brush.data_nodes + p;
+                       // LordHavoc: this code supports broken bsp files produced by
+                       // arguire qbsp which can produce more than 32768 nodes, any value
+                       // below count is assumed to be a node number, any other value is
+                       // assumed to be a leaf number
+                       p = (unsigned short)LittleShort(in->children[j]);
+                       if (p < count)
+                       {
+                               if (p < loadmodel->brush.num_nodes)
+                                       out->children[j] = loadmodel->brush.data_nodes + p;
+                               else
+                               {
+                                       Con_Printf("Mod_Q1BSP_LoadNodes: invalid node index %i (file has only %i nodes)\n", p, loadmodel->brush.num_nodes);
+                                       // map it to the solid leaf
+                                       out->children[j] = (mnode_t *)loadmodel->brush.data_leafs;
+                               }
+                       }
                        else
-                               out->children[j] = (mnode_t *)(loadmodel->brush.data_leafs + (-1 - p));
+                       {
+                               // note this uses 65535 intentionally, -1 is leaf 0
+                               p = 65535 - p;
+                               if (p < loadmodel->brush.num_leafs)
+                                       out->children[j] = (mnode_t *)(loadmodel->brush.data_leafs + p);
+                               else
+                               {
+                                       Con_Printf("Mod_Q1BSP_LoadNodes: invalid leaf index %i (file has only %i leafs)\n", p, loadmodel->brush.num_leafs);
+                                       // map it to the solid leaf
+                                       out->children[j] = (mnode_t *)loadmodel->brush.data_leafs;
+                               }
+                       }
                }
        }
 
@@ -2480,9 +2571,9 @@ static void Mod_Q1BSP_LoadLeafs(lump_t *l)
 
                out->contents = LittleLong(in->contents);
 
-               out->firstleafsurface = loadmodel->brush.data_leafsurfaces + LittleShort(in->firstmarksurface);
-               out->numleafsurfaces = LittleShort(in->nummarksurfaces);
-               if (out->firstleafsurface < 0 || LittleShort(in->firstmarksurface) + out->numleafsurfaces > loadmodel->brush.num_leafsurfaces)
+               out->firstleafsurface = loadmodel->brush.data_leafsurfaces + (unsigned short)LittleShort(in->firstmarksurface);
+               out->numleafsurfaces = (unsigned short)LittleShort(in->nummarksurfaces);
+               if (out->firstleafsurface < 0 || (unsigned short)LittleShort(in->firstmarksurface) + out->numleafsurfaces > loadmodel->brush.num_leafsurfaces)
                {
                        Con_Printf("Mod_Q1BSP_LoadLeafs: invalid leafsurface range %i:%i outside range %i:%i\n", (int)(out->firstleafsurface - loadmodel->brush.data_leafsurfaces), (int)(out->firstleafsurface + out->numleafsurfaces - loadmodel->brush.data_leafsurfaces), 0, loadmodel->brush.num_leafsurfaces);
                        out->firstleafsurface = NULL;
@@ -2532,7 +2623,8 @@ qboolean Mod_Q1BSP_CheckWaterAlphaSupport(void)
 
 static void Mod_Q1BSP_LoadClipnodes(lump_t *l, hullinfo_t *hullinfo)
 {
-       dclipnode_t *in, *out;
+       dclipnode_t *in;
+       mclipnode_t *out;
        int                     i, count;
        hull_t          *hull;
 
@@ -2540,7 +2632,7 @@ static void Mod_Q1BSP_LoadClipnodes(lump_t *l, hullinfo_t *hullinfo)
        if (l->filelen % sizeof(*in))
                Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
        count = l->filelen / sizeof(*in);
-       out = (dclipnode_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
+       out = (mclipnode_t *)Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
 
        loadmodel->brushq1.clipnodes = out;
        loadmodel->brushq1.numclipnodes = count;
@@ -2564,12 +2656,15 @@ static void Mod_Q1BSP_LoadClipnodes(lump_t *l, hullinfo_t *hullinfo)
        for (i=0 ; i<count ; i++, out++, in++)
        {
                out->planenum = LittleLong(in->planenum);
-               out->children[0] = LittleShort(in->children[0]);
-               out->children[1] = LittleShort(in->children[1]);
+               // LordHavoc: this code supports arguire qbsp's broken clipnodes indices (more than 32768 clipnodes), values above count are assumed to be contents values
+               out->children[0] = (unsigned short)LittleShort(in->children[0]);
+               out->children[1] = (unsigned short)LittleShort(in->children[1]);
+               if (out->children[0] >= count)
+                       out->children[0] -= 65536;
+               if (out->children[1] >= count)
+                       out->children[1] -= 65536;
                if (out->planenum < 0 || out->planenum >= loadmodel->brush.num_planes)
                        Host_Error("Corrupt clipping hull(out of range planenum)");
-               if (out->children[0] >= count || out->children[1] >= count)
-                       Host_Error("Corrupt clipping hull(out of range child)");
        }
 }
 
@@ -2577,14 +2672,14 @@ static void Mod_Q1BSP_LoadClipnodes(lump_t *l, hullinfo_t *hullinfo)
 static void Mod_Q1BSP_MakeHull0(void)
 {
        mnode_t         *in;
-       dclipnode_t *out;
+       mclipnode_t *out;
        int                     i;
        hull_t          *hull;
 
        hull = &loadmodel->brushq1.hulls[0];
 
        in = loadmodel->brush.data_nodes;
-       out = (dclipnode_t *)Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_nodes * sizeof(dclipnode_t));
+       out = (mclipnode_t *)Mem_Alloc(loadmodel->mempool, loadmodel->brush.num_nodes * sizeof(*out));
 
        hull->clipnodes = out;
        hull->firstclipnode = 0;
@@ -2612,7 +2707,7 @@ static void Mod_Q1BSP_LoadLeaffaces(lump_t *l)
 
        for (i = 0;i < loadmodel->brush.num_leafsurfaces;i++)
        {
-               j = (unsigned) LittleShort(in[i]);
+               j = (unsigned short) LittleShort(in[i]);
                if (j >= loadmodel->num_surfaces)
                        Host_Error("Mod_Q1BSP_LoadLeaffaces: bad surface number");
                loadmodel->brush.data_leafsurfaces[i] = j;
@@ -2673,12 +2768,12 @@ static void Mod_Q1BSP_LoadMapBrushes(void)
        if (!maptext)
                return;
        text = maptext;
-       if (!COM_ParseToken_Simple(&data, false))
+       if (!COM_ParseToken_Simple(&data, false, false))
                return; // error
        submodel = 0;
        for (;;)
        {
-               if (!COM_ParseToken_Simple(&data, false))
+               if (!COM_ParseToken_Simple(&data, false, false))
                        break;
                if (com_token[0] != '{')
                        return; // error
@@ -2689,7 +2784,7 @@ static void Mod_Q1BSP_LoadMapBrushes(void)
                brushes = Mem_Alloc(loadmodel->mempool, maxbrushes * sizeof(mbrush_t));
                for (;;)
                {
-                       if (!COM_ParseToken_Simple(&data, false))
+                       if (!COM_ParseToken_Simple(&data, false, false))
                                return; // error
                        if (com_token[0] == '}')
                                break; // end of entity
@@ -2713,7 +2808,7 @@ static void Mod_Q1BSP_LoadMapBrushes(void)
                                }
                                for (;;)
                                {
-                                       if (!COM_ParseToken_Simple(&data, false))
+                                       if (!COM_ParseToken_Simple(&data, false, false))
                                                return; // error
                                        if (com_token[0] == '}')
                                                break; // end of brush
@@ -2722,25 +2817,25 @@ static void Mod_Q1BSP_LoadMapBrushes(void)
                                        // FIXME: support hl .map format
                                        for (pointnum = 0;pointnum < 3;pointnum++)
                                        {
-                                               COM_ParseToken_Simple(&data, false);
+                                               COM_ParseToken_Simple(&data, false, false);
                                                for (componentnum = 0;componentnum < 3;componentnum++)
                                                {
-                                                       COM_ParseToken_Simple(&data, false);
+                                                       COM_ParseToken_Simple(&data, false, false);
                                                        point[pointnum][componentnum] = atof(com_token);
                                                }
-                                               COM_ParseToken_Simple(&data, false);
+                                               COM_ParseToken_Simple(&data, false, false);
                                        }
-                                       COM_ParseToken_Simple(&data, false);
+                                       COM_ParseToken_Simple(&data, false, false);
                                        strlcpy(facetexture, com_token, sizeof(facetexture));
-                                       COM_ParseToken_Simple(&data, false);
+                                       COM_ParseToken_Simple(&data, false, false);
                                        //scroll_s = atof(com_token);
-                                       COM_ParseToken_Simple(&data, false);
+                                       COM_ParseToken_Simple(&data, false, false);
                                        //scroll_t = atof(com_token);
-                                       COM_ParseToken_Simple(&data, false);
+                                       COM_ParseToken_Simple(&data, false, false);
                                        //rotate = atof(com_token);
-                                       COM_ParseToken_Simple(&data, false);
+                                       COM_ParseToken_Simple(&data, false, false);
                                        //scale_s = atof(com_token);
-                                       COM_ParseToken_Simple(&data, false);
+                                       COM_ParseToken_Simple(&data, false, false);
                                        //scale_t = atof(com_token);
                                        TriangleNormal(point[0], point[1], point[2], planenormal);
                                        VectorNormalizeDouble(planenormal);
@@ -3237,7 +3332,7 @@ static void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org,
 
 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
 //of the given point.
-static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, unsigned char *pvsbuffer, int pvsbufferlength)
+static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, unsigned char *pvsbuffer, int pvsbufferlength, qboolean merge)
 {
        int bytes = model->brush.num_pvsclusterbytes;
        bytes = min(bytes, pvsbufferlength);
@@ -3246,7 +3341,8 @@ static int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, unsi
                memset(pvsbuffer, 0xFF, bytes);
                return bytes;
        }
-       memset(pvsbuffer, 0, bytes);
+       if (!merge)
+               memset(pvsbuffer, 0, bytes);
        Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, model->brush.data_nodes);
        return bytes;
 }
@@ -3305,6 +3401,8 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer, void *bufferend)
        dheader_t _header;
        hullinfo_t hullinfo;
 
+       mod->modeldatatypestring = "Q1BSP";
+
        mod->type = mod_brushq1;
 
        if (!memcmp (buffer, "MCBSPpad", 8))
@@ -3323,8 +3421,10 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer, void *bufferend)
                        Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i)", mod->name, i, MCBSPVERSION);
 
        // read hull info
-               hullinfo.numhulls = LittleLong(*(int*)index); index += 4;
+               hullinfo.numhulls = SB_ReadInt (&index);
                hullinfo.filehulls = hullinfo.numhulls;
+               mod->brushq1.numhulls = hullinfo.numhulls;
+
                VectorClear (hullinfo.hullsizes[0][0]);
                VectorClear (hullinfo.hullsizes[0][1]);
                for (i = 1; i < hullinfo.numhulls; i++)
@@ -3362,8 +3462,11 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer, void *bufferend)
                VectorClear (hullinfo.hullsizes[0][1]);
                if (mod->brush.ishlbsp)
                {
+                       mod->modeldatatypestring = "HLBSP";
+
                        hullinfo.numhulls = 4;
                        hullinfo.filehulls = 4;
+                       mod->brushq1.numhulls = 4;
                        VectorSet (hullinfo.hullsizes[1][0], -16, -16, -36);
                        VectorSet (hullinfo.hullsizes[1][1], 16, 16, 36);
                        VectorSet (hullinfo.hullsizes[2][0], -32, -32, -32);
@@ -3375,6 +3478,7 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer, void *bufferend)
                {
                        hullinfo.numhulls = 3;
                        hullinfo.filehulls = 4;
+                       mod->brushq1.numhulls = 3;
                        VectorSet (hullinfo.hullsizes[1][0], -16, -16, -24);
                        VectorSet (hullinfo.hullsizes[1][1], 16, 16, 32);
                        VectorSet (hullinfo.hullsizes[2][0], -32, -32, -24);
@@ -3548,10 +3652,12 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer, void *bufferend)
                for (j = 0;j < mod->nummodelsurfaces;j++)
                        mod->surfacelist[j] = mod->firstmodelsurface + j;
 
-               // this gets altered below if sky is used
+               // this gets altered below if sky or water is used
                mod->DrawSky = NULL;
+               mod->DrawAddWaterPlanes = NULL;
                mod->Draw = R_Q1BSP_Draw;
                mod->DrawDepth = R_Q1BSP_DrawDepth;
+               mod->DrawDebug = R_Q1BSP_DrawDebug;
                mod->GetLightInfo = R_Q1BSP_GetLightInfo;
                mod->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
                mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
@@ -3581,6 +3687,8 @@ void Mod_Q1BSP_Load(model_t *mod, void *buffer, void *bufferend)
                                // we only need to have a drawsky function if it is used(usually only on world model)
                                if (surface->texture->basematerialflags & MATERIALFLAG_SKY)
                                        mod->DrawSky = R_Q1BSP_DrawSky;
+                               if (surface->texture->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION))
+                                       mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
                                // calculate bounding shapes
                                for (k = 0, vec = (loadmodel->surfmesh.data_vertex3f + 3 * surface->num_firstvertex);k < surface->num_vertices;k++, vec += 3)
                                {
@@ -4009,6 +4117,8 @@ void static Mod_Q2BSP_Load(model_t *mod, void *buffer, void *bufferend)
 
        Host_Error("Mod_Q2BSP_Load: not yet implemented");
 
+       mod->modeldatatypestring = "Q2BSP";
+
        mod->type = mod_brushq2;
 
        header = (q2dheader_t *)buffer;
@@ -4083,11 +4193,11 @@ static void Mod_Q3BSP_LoadEntities(lump_t *l)
        memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
        data = loadmodel->brush.entities;
        // some Q3 maps override the lightgrid_cellsize with a worldspawn key
-       if (data && COM_ParseToken_Simple(&data, false) && com_token[0] == '{')
+       if (data && COM_ParseToken_Simple(&data, false, false) && com_token[0] == '{')
        {
                while (1)
                {
-                       if (!COM_ParseToken_Simple(&data, false))
+                       if (!COM_ParseToken_Simple(&data, false, false))
                                break; // error
                        if (com_token[0] == '}')
                                break; // end of worldspawn
@@ -4097,7 +4207,7 @@ static void Mod_Q3BSP_LoadEntities(lump_t *l)
                                strlcpy(key, com_token, sizeof(key));
                        while (key[strlen(key)-1] == ' ') // remove trailing spaces
                                key[strlen(key)-1] = 0;
-                       if (!COM_ParseToken_Simple(&data, false))
+                       if (!COM_ParseToken_Simple(&data, false, false))
                                break; // error
                        strlcpy(value, com_token, sizeof(value));
                        if (!strcmp("gridsize", key))
@@ -4137,7 +4247,7 @@ static void Mod_Q3BSP_LoadTextures(lump_t *l)
 
        c = 0;
        for (i = 0;i < count;i++, in++, out++)
-               if (Mod_LoadTextureFromQ3Shader(out, out->name, false, true, false))
+               if (!Mod_LoadTextureFromQ3Shader(out, out->name, false, true, false))
                        c++;
        if (c)
                Con_DPrintf("%s: %i textures missing shaders\n", loadmodel->name, c);
@@ -4447,9 +4557,9 @@ static void Mod_Q3BSP_LoadLightmaps(lump_t *l, lump_t *faceslump)
                                for (mergeheight = 1;mergewidth*mergeheight < j && mergeheight < (1 << power);mergeheight *= 2)
                                        ;
                                Con_DPrintf("lightmap merge texture #%i is %ix%i (%i of %i used)\n", lightmapindex, mergewidth*128, mergeheight*128, min(j, mergewidth*mergeheight), mergewidth*mergeheight);
-                               loadmodel->brushq3.data_lightmaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", lightmapindex), mergewidth * 128, mergeheight * 128, NULL, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
+                               loadmodel->brushq3.data_lightmaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", lightmapindex), mergewidth * 128, mergeheight * 128, NULL, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE | (gl_texturecompression_q3bsplightmaps.integer ? TEXF_COMPRESS : 0), NULL);
                                if (loadmodel->brushq3.data_deluxemaps)
-                                       loadmodel->brushq3.data_deluxemaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%04i", lightmapindex), mergewidth * 128, mergeheight * 128, NULL, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
+                                       loadmodel->brushq3.data_deluxemaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%04i", lightmapindex), mergewidth * 128, mergeheight * 128, NULL, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE | (gl_texturecompression_q3bspdeluxemaps.integer ? TEXF_COMPRESS : 0), NULL);
                        }
                        mergewidth = R_TextureWidth(loadmodel->brushq3.data_lightmaps[lightmapindex]) / 128;
                        mergeheight = R_TextureHeight(loadmodel->brushq3.data_lightmaps[lightmapindex]) / 128;
@@ -4467,9 +4577,9 @@ static void Mod_Q3BSP_LoadLightmaps(lump_t *l, lump_t *faceslump)
                        // figure out which merged lightmap texture this fits into
                        int lightmapindex = i >> loadmodel->brushq3.deluxemapping;
                        if (loadmodel->brushq3.deluxemapping && (i & 1))
-                               loadmodel->brushq3.data_deluxemaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%04i", lightmapindex), 128, 128, in[i].rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
+                               loadmodel->brushq3.data_deluxemaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("deluxemap%04i", lightmapindex), 128, 128, in[i].rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE | (gl_texturecompression_q3bspdeluxemaps.integer ? TEXF_COMPRESS : 0), NULL);
                        else
-                               loadmodel->brushq3.data_lightmaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", lightmapindex), 128, 128, in[i].rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
+                               loadmodel->brushq3.data_lightmaps[lightmapindex] = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", lightmapindex), 128, 128, in[i].rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE | (gl_texturecompression_q3bsplightmaps.integer ? TEXF_COMPRESS : 0), NULL);
                }
        }
 }
@@ -5501,6 +5611,8 @@ void Mod_Q3BSP_Load(model_t *mod, void *buffer, void *bufferend)
        float corner[3], yawradius, modelradius;
        msurface_t *surface;
 
+       mod->modeldatatypestring = "Q3BSP";
+
        mod->type = mod_brushq3;
        mod->numframes = 2; // although alternate textures are not supported it is annoying to complain about no such frame 1
        mod->numskins = 1;
@@ -5534,10 +5646,12 @@ void Mod_Q3BSP_Load(model_t *mod, void *buffer, void *bufferend)
        mod->brush.PointInLeaf = Mod_Q1BSP_PointInLeaf;
        mod->Draw = R_Q1BSP_Draw;
        mod->DrawDepth = R_Q1BSP_DrawDepth;
+       mod->DrawDebug = R_Q1BSP_DrawDebug;
        mod->GetLightInfo = R_Q1BSP_GetLightInfo;
        mod->CompileShadowVolume = R_Q1BSP_CompileShadowVolume;
        mod->DrawShadowVolume = R_Q1BSP_DrawShadowVolume;
        mod->DrawLight = R_Q1BSP_DrawLight;
+       mod->DrawAddWaterPlanes = NULL;
 
        mod_base = (unsigned char *)header;
 
@@ -5669,12 +5783,20 @@ void Mod_Q3BSP_Load(model_t *mod, void *buffer, void *bufferend)
                mod->radius2 = modelradius * modelradius;
 
                for (j = 0;j < mod->nummodelsurfaces;j++)
-                       if (mod->data_surfaces[j + mod->firstmodelsurface].texture->surfaceflags & Q3SURFACEFLAG_SKY)
+                       if (mod->data_surfaces[j + mod->firstmodelsurface].texture->basematerialflags & MATERIALFLAG_SKY)
                                break;
                if (j < mod->nummodelsurfaces)
                        mod->DrawSky = R_Q1BSP_DrawSky;
                else
                        mod->DrawSky = NULL;
+
+               for (j = 0;j < mod->nummodelsurfaces;j++)
+                       if (mod->data_surfaces[j + mod->firstmodelsurface].texture->basematerialflags & (MATERIALFLAG_WATERSHADER | MATERIALFLAG_REFRACTION | MATERIALFLAG_REFLECTION))
+                               break;
+               if (j < mod->nummodelsurfaces)
+                       mod->DrawAddWaterPlanes = R_Q1BSP_DrawAddWaterPlanes;
+               else
+                       mod->DrawAddWaterPlanes = NULL;
        }
 }