]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - model_brush.c
Fix alpha transparent textures (used by HLBSP maps) in HL/Q1 BSP maps
[xonotic/darkplaces.git] / model_brush.c
index 2aea6f858bcacc886f9d3382eef556cc056c0a5e..4512c541c487960e19c479e3130b71dbac44d4e0 100644 (file)
@@ -43,6 +43,7 @@ cvar_t mod_q3bsp_curves_collisions = {0, "mod_q3bsp_curves_collisions", "1", "en
 cvar_t mod_q3bsp_optimizedtraceline = {0, "mod_q3bsp_optimizedtraceline", "1", "whether to use optimized traceline code for line traces (as opposed to tracebox code)"};
 cvar_t mod_q3bsp_debugtracebrush = {0, "mod_q3bsp_debugtracebrush", "0", "selects different tracebrush bsp recursion algorithms (for debugging purposes only)"};
 cvar_t mod_q3bsp_lightmapmergepower = {CVAR_SAVE, "mod_q3bsp_lightmapmergepower", "4", "merges the quake3 128x128 lightmap textures into larger lightmap group textures to speed up rendering, 1 = 256x256, 2 = 512x512, 3 = 1024x1024, 4 = 2048x2048, 5 = 4096x4096, ..."};
+cvar_t mod_q3bsp_nolightmaps = {CVAR_SAVE, "mod_q3bsp_nolightmaps", "0", "do not load lightmaps in Q3BSP maps (to save video RAM, but be warned: it looks ugly)"};
 
 static texture_t mod_q1bsp_texture_solid;
 static texture_t mod_q1bsp_texture_sky;
@@ -69,6 +70,7 @@ void Mod_BrushInit(void)
        Cvar_RegisterVariable(&mod_q3bsp_optimizedtraceline);
        Cvar_RegisterVariable(&mod_q3bsp_debugtracebrush);
        Cvar_RegisterVariable(&mod_q3bsp_lightmapmergepower);
+       Cvar_RegisterVariable(&mod_q3bsp_nolightmaps);
 
        memset(&mod_q1bsp_texture_solid, 0, sizeof(mod_q1bsp_texture_solid));
        strlcpy(mod_q1bsp_texture_solid.name, "solid" , sizeof(mod_q1bsp_texture_solid.name));
@@ -1500,9 +1502,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_PRECACHE | (r_picmipworld.integer ? TEXF_PICMIP : 0) | TEXF_COMPRESS, false);
+                               skinframe = R_SkinFrame_LoadExternal(gamemode == GAME_TENEBRAE ? tx->name : va("textures/%s/%s", mapname, tx->name), TEXF_ALPHA | TEXF_MIPMAP | 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_PRECACHE | (r_picmipworld.integer ? TEXF_PICMIP : 0) | TEXF_COMPRESS, false);
+                                       skinframe = R_SkinFrame_LoadExternal(gamemode == GAME_TENEBRAE ? tx->name : va("textures/%s", tx->name), TEXF_ALPHA | TEXF_MIPMAP | 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
@@ -1519,13 +1521,13 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l)
                                                {
                                                        tx->width = image_width;
                                                        tx->height = image_height;
-                                                       skinframe = R_SkinFrame_LoadInternalBGRA(tx->name, TEXF_MIPMAP | TEXF_PRECACHE | (r_picmipworld.integer ? TEXF_PICMIP : 0), pixels, image_width, image_height);
+                                                       skinframe = R_SkinFrame_LoadInternalBGRA(tx->name, TEXF_ALPHA | TEXF_MIPMAP | TEXF_PRECACHE | (r_picmipworld.integer ? TEXF_PICMIP : 0), pixels, image_width, image_height);
                                                }
                                                if (freepixels)
                                                        Mem_Free(freepixels);
                                        }
                                        else if (mtdata) // texture included
-                                               skinframe = R_SkinFrame_LoadInternalQuake(tx->name, TEXF_MIPMAP | TEXF_PRECACHE | (r_picmipworld.integer ? TEXF_PICMIP : 0), false, r_fullbrights.integer, mtdata, tx->width, tx->height);
+                                               skinframe = R_SkinFrame_LoadInternalQuake(tx->name, TEXF_ALPHA | TEXF_MIPMAP | TEXF_PRECACHE | (r_picmipworld.integer ? TEXF_PICMIP : 0), false, r_fullbrights.integer, mtdata, tx->width, tx->height);
                                }
                                // if skinframe is still NULL the "missing" texture will be used
                                if (skinframe)
@@ -4477,10 +4479,33 @@ static void Mod_Q3BSP_LoadLightmaps(lump_t *l, lump_t *faceslump)
        if (cls.state == ca_dedicated)
                return;
 
-       FS_StripExtension(loadmodel->name, mapname, sizeof(mapname));
-       inpixels[0] = loadimagepixelsbgra(va("%s/lm_%04d", mapname, 0), false, false);
-       if(inpixels[0])
+       if(mod_q3bsp_nolightmaps.integer)
        {
+               return;
+       }
+       else if(l->filelen)
+       {
+               // prefer internal LMs for compatibility (a BSP contains no info on whether external LMs exist)
+               if (developer_loading.integer)
+                       Con_Printf("Using internal lightmaps\n");
+               input_pointer = (q3dlightmap_t *)(mod_base + l->fileofs);
+               if (l->filelen % sizeof(*input_pointer))
+                       Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
+               count = l->filelen / sizeof(*input_pointer);
+               for(i = 0; i < count; ++i)
+                       inpixels[i] = input_pointer[i].rgb;
+       }
+       else
+       {
+               // no internal lightmaps
+               // try external lightmaps
+               if (developer_loading.integer)
+                       Con_Printf("Using external lightmaps\n");
+               FS_StripExtension(loadmodel->name, mapname, sizeof(mapname));
+               inpixels[0] = loadimagepixelsbgra(va("%s/lm_%04d", mapname, 0), false, false);
+               if(!inpixels[0])
+                       return;
+
                // using EXTERNAL lightmaps instead
                if(image_width != (int) CeilPowerOf2(image_width) || image_width != image_height)
                {
@@ -4508,23 +4533,10 @@ static void Mod_Q3BSP_LoadLightmaps(lump_t *l, lump_t *faceslump)
                        }
                }
        }
-       else
-       {
-               if (!l->filelen)
-               {
-                       return;
-               }
-               input_pointer = (q3dlightmap_t *)(mod_base + l->fileofs);
-               if (l->filelen % sizeof(*input_pointer))
-                       Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
-               count = l->filelen / sizeof(*input_pointer);
-               loadmodel->brushq3.num_originallightmaps = count;
-               for(i = 0; i < count; ++i)
-                       inpixels[i] = input_pointer[i].rgb;
-       }
 
-       convertedpixels = Mem_Alloc(tempmempool, size*size*4); // TODO free this
+       convertedpixels = Mem_Alloc(tempmempool, size*size*4);
        loadmodel->brushq3.lightmapsize = size;
+       loadmodel->brushq3.num_originallightmaps = count;
 
        // now check the surfaces to see if any of them index an odd numbered
        // lightmap, if so this is not a deluxemapped bsp file
@@ -4662,6 +4674,7 @@ static void Mod_Q3BSP_LoadLightmaps(lump_t *l, lump_t *faceslump)
                }
        }
 
+       Mem_Free(convertedpixels);
        if(external)
        {
                for(i = 0; i < count; ++i)
@@ -4742,7 +4755,8 @@ static void Mod_Q3BSP_LoadFaces(lump_t *l)
                                n = -1;
                        else if (n >= loadmodel->brushq3.num_originallightmaps)
                        {
-                               Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_originallightmaps);
+                               if(loadmodel->brushq3.num_originallightmaps != 0)
+                                       Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_originallightmaps);
                                n = -1;
                        }
                        else
@@ -5740,6 +5754,8 @@ void Mod_Q3BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
        mod->numskins = 1;
 
        header = (q3dheader_t *)buffer;
+       if((char *) bufferend < (char *) buffer + sizeof(q3dheader_t))
+               Host_Error("Mod_Q3BSP_Load: %s is smaller than its header", mod->name);
 
        i = LittleLong(header->version);
        if (i != Q3BSPVERSION && i != Q3BSPVERSION_IG && i != Q3BSPVERSION_LIVE)
@@ -5780,14 +5796,25 @@ void Mod_Q3BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
        lumps = (header->version == Q3BSPVERSION_LIVE) ? Q3HEADER_LUMPS_LIVE : Q3HEADER_LUMPS;
        for (i = 0;i < lumps;i++)
        {
-               header->lumps[i].fileofs = LittleLong(header->lumps[i].fileofs);
-               header->lumps[i].filelen = LittleLong(header->lumps[i].filelen);
-       }
+               j = (header->lumps[i].fileofs = LittleLong(header->lumps[i].fileofs));
+               if((char *) bufferend < (char *) buffer + j)
+                       Host_Error("Mod_Q3BSP_Load: %s has a lump that starts outside the file!", mod->name);
+               j += (header->lumps[i].filelen = LittleLong(header->lumps[i].filelen));
+               if((char *) bufferend < (char *) buffer + j)
+                       Host_Error("Mod_Q3BSP_Load: %s has a lump that ends outside the file!", mod->name);
+       }
+       /*
+        * NO, do NOT clear them!
+        * they contain actual data referenced by other stuff.
+        * Instead, before using the advertisements lump, check header->versio
+        * again!
+        * Sorry, but otherwise it breaks memory of the first lump.
        for (i = lumps;i < Q3HEADER_LUMPS_MAX;i++)
        {
                header->lumps[i].fileofs = 0;
                header->lumps[i].filelen = 0;
        }
+       */
 
        mod->brush.qw_md4sum = 0;
        mod->brush.qw_md4sum2 = 0;
@@ -5831,17 +5858,20 @@ void Mod_Q3BSP_Load(dp_model_t *mod, void *buffer, void *bufferend)
 
        // make a single combined shadow mesh to allow optimized shadow volume creation
        numshadowmeshtriangles = 0;
-       for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
+       if (cls.state != ca_dedicated)
        {
-               surface->num_firstshadowmeshtriangle = numshadowmeshtriangles;
-               numshadowmeshtriangles += surface->num_triangles;
+               for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
+               {
+                       surface->num_firstshadowmeshtriangle = numshadowmeshtriangles;
+                       numshadowmeshtriangles += surface->num_triangles;
+               }
+               loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
+               for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
+                       if (surface->num_triangles > 0)
+                               Mod_ShadowMesh_AddMesh(loadmodel->mempool, loadmodel->brush.shadowmesh, NULL, NULL, NULL, loadmodel->surfmesh.data_vertex3f, NULL, NULL, NULL, NULL, surface->num_triangles, (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
+               loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true, false);
+               Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
        }
-       loadmodel->brush.shadowmesh = Mod_ShadowMesh_Begin(loadmodel->mempool, numshadowmeshtriangles * 3, numshadowmeshtriangles, NULL, NULL, NULL, false, false, true);
-       for (j = 0, surface = loadmodel->data_surfaces;j < loadmodel->num_surfaces;j++, surface++)
-               if (surface->num_triangles > 0)
-                       Mod_ShadowMesh_AddMesh(loadmodel->mempool, loadmodel->brush.shadowmesh, NULL, NULL, NULL, loadmodel->surfmesh.data_vertex3f, NULL, NULL, NULL, NULL, surface->num_triangles, (loadmodel->surfmesh.data_element3i + 3 * surface->num_firsttriangle));
-       loadmodel->brush.shadowmesh = Mod_ShadowMesh_Finish(loadmodel->mempool, loadmodel->brush.shadowmesh, false, true, false);
-       Mod_BuildTriangleNeighbors(loadmodel->brush.shadowmesh->neighbor3i, loadmodel->brush.shadowmesh->element3i, loadmodel->brush.shadowmesh->numtriangles);
 
        loadmodel->brush.num_leafs = 0;
        Mod_Q3BSP_RecursiveFindNumLeafs(loadmodel->brush.data_nodes);