replaced Mod_LoadSkinFrame functions with R_SkinFrame system, this
authorhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Tue, 24 Apr 2007 13:19:07 +0000 (13:19 +0000)
committerhavoc <havoc@d7cf8633-e32d-0410-b094-e92efae38249>
Tue, 24 Apr 2007 13:19:07 +0000 (13:19 +0000)
allows multiple models to share textures in a given level, as well as
models using the same texture multiple times (often the case in md3
where meshes have a vertex limit, and q3bsp where texture structs
describe surface properties and multiple may exist referring to the same
texture set)
this reduced Nexuiz video memory usage by over 150MB

git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@7168 d7cf8633-e32d-0410-b094-e92efae38249

gl_rmain.c
gl_rsurf.c
model_alias.c
model_brush.c
model_shared.c
model_shared.h
model_sprite.c
r_sprites.c
render.h

index b34880d..b329451 100644 (file)
@@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #include "quakedef.h"
 #include "r_shadow.h"
 #include "polygon.h"
+#include "image.h"
 
 mempool_t *r_main_mempool;
 rtexturepool_t *r_main_texturepool;
@@ -1034,6 +1035,330 @@ void R_SwitchSurfaceShader(int permutation)
        }
 }
 
+#define SKINFRAME_HASH 1024
+
+struct
+{
+       int loadsequence; // incremented each level change
+       memexpandablearray_t array;
+       skinframe_t *hash[SKINFRAME_HASH];
+}
+r_skinframe;
+
+void R_SkinFrame_PrepareForPurge(void)
+{
+       r_skinframe.loadsequence++;
+       // wrap it without hitting zero
+       if (r_skinframe.loadsequence >= 200)
+               r_skinframe.loadsequence = 1;
+}
+
+void R_SkinFrame_MarkUsed(skinframe_t *skinframe)
+{
+       // mark the skinframe as used for the purging code
+       skinframe->loadsequence = r_skinframe.loadsequence;
+}
+
+void R_SkinFrame_Purge(void)
+{
+       int i;
+       skinframe_t *s;
+       for (i = 0;i < SKINFRAME_HASH;i++)
+       {
+               for (s = r_skinframe.hash[i];s;s = s->next)
+               {
+                       if (s->loadsequence && s->loadsequence != r_skinframe.loadsequence)
+                       {
+                               if (s->base == r_texture_notexture)     s->base   = NULL;
+                               if (s->nmap == r_texture_blanknormalmap)s->nmap   = NULL;
+                               if (s->merged == s->base)               s->merged = NULL;
+                               if (s->stain ) R_FreeTexture(s->stain );s->stain  = NULL;
+                               if (s->merged) R_FreeTexture(s->merged);s->merged = NULL;
+                               if (s->base  ) R_FreeTexture(s->base  );s->base   = NULL;
+                               if (s->pants ) R_FreeTexture(s->pants );s->pants  = NULL;
+                               if (s->shirt ) R_FreeTexture(s->shirt );s->shirt  = NULL;
+                               if (s->nmap  ) R_FreeTexture(s->nmap  );s->nmap   = NULL;
+                               if (s->gloss ) R_FreeTexture(s->gloss );s->gloss  = NULL;
+                               if (s->glow  ) R_FreeTexture(s->glow  );s->glow   = NULL;
+                               if (s->fog   ) R_FreeTexture(s->fog   );s->fog    = NULL;
+                               s->loadsequence = 0;
+                       }
+               }
+       }
+}
+
+skinframe_t *R_SkinFrame_Find(const char *name, int textureflags, int comparewidth, int compareheight, int comparecrc, qboolean add)
+{
+       skinframe_t *item;
+       int hashindex;
+       char basename[MAX_QPATH];
+
+       Image_StripImageExtension(name, basename, sizeof(basename));
+
+       hashindex = CRC_Block((unsigned char *)basename, strlen(basename)) & (SKINFRAME_HASH - 1);
+       for (item = r_skinframe.hash[hashindex];item;item = item->next)
+               if (!strcmp(item->basename, basename) && item->textureflags == textureflags && item->comparewidth == comparewidth && item->compareheight == compareheight && item->comparecrc == comparecrc)
+                       break;
+       if (!item)
+       {
+               if (!add)
+                       return NULL;
+               item = (skinframe_t *)Mem_ExpandableArray_AllocRecord(&r_skinframe.array);
+               memset(item, 0, sizeof(*item));
+               strlcpy(item->basename, basename, sizeof(item->basename));
+               item->textureflags = textureflags;
+               item->comparewidth = comparewidth;
+               item->compareheight = compareheight;
+               item->comparecrc = comparecrc;
+               item->next = r_skinframe.hash[hashindex];
+               r_skinframe.hash[hashindex] = item;
+       }
+       R_SkinFrame_MarkUsed(item);
+       return item;
+}
+
+skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags)
+{
+       // FIXME: it should be possible to disable loading various layers using
+       // cvars, to prevent wasted loading time and memory usage if the user does
+       // not want them
+       qboolean loadnormalmap = true;
+       qboolean loadgloss = true;
+       qboolean loadpantsandshirt = true;
+       qboolean loadglow = true;
+       int j;
+       unsigned char *pixels;
+       unsigned char *bumppixels;
+       unsigned char *basepixels = NULL;
+       int basepixels_width;
+       int basepixels_height;
+       skinframe_t *skinframe;
+
+       if (cls.state == ca_dedicated)
+               return NULL;
+
+       // return an existing skinframe if already loaded
+       // if loading of the first image fails, don't make a new skinframe as it
+       // would cause all future lookups of this to be missing
+       skinframe = R_SkinFrame_Find(name, textureflags, 0, 0, 0, false);
+       if (skinframe && skinframe->base)
+               return skinframe;
+
+       basepixels = loadimagepixels(name, false, 0, 0);
+       if (basepixels == NULL)
+               return NULL;
+
+       // we've got some pixels to store, so really allocate this new texture now
+       if (!skinframe)
+               skinframe = R_SkinFrame_Find(name, textureflags, 0, 0, 0, true);
+       skinframe->stain = NULL;
+       skinframe->merged = NULL;
+       skinframe->base = r_texture_notexture;
+       skinframe->pants = NULL;
+       skinframe->shirt = NULL;
+       skinframe->nmap = r_texture_blanknormalmap;
+       skinframe->gloss = NULL;
+       skinframe->glow = NULL;
+       skinframe->fog = NULL;
+
+       basepixels_width = image_width;
+       basepixels_height = image_height;
+       skinframe->base = R_LoadTexture2D (r_main_texturepool, skinframe->basename, basepixels_width, basepixels_height, basepixels, TEXTYPE_RGBA, skinframe->textureflags, NULL);
+
+       if (textureflags & TEXF_ALPHA)
+       {
+               for (j = 3;j < basepixels_width * basepixels_height * 4;j += 4)
+                       if (basepixels[j] < 255)
+                               break;
+               if (j < basepixels_width * basepixels_height * 4)
+               {
+                       // has transparent pixels
+                       pixels = (unsigned char *)Mem_Alloc(tempmempool, image_width * image_height * 4);
+                       for (j = 0;j < image_width * image_height * 4;j += 4)
+                       {
+                               pixels[j+0] = 255;
+                               pixels[j+1] = 255;
+                               pixels[j+2] = 255;
+                               pixels[j+3] = basepixels[j+3];
+                       }
+                       skinframe->fog = R_LoadTexture2D (r_main_texturepool, va("%s_mask", skinframe->basename), image_width, image_height, pixels, TEXTYPE_RGBA, skinframe->textureflags, NULL);
+                       Mem_Free(pixels);
+               }
+       }
+
+       // _norm is the name used by tenebrae and has been adopted as standard
+       if (loadnormalmap)
+       {
+               if ((pixels = loadimagepixels(va("%s_norm", skinframe->basename), false, 0, 0)) != NULL)
+               {
+                       skinframe->nmap = R_LoadTexture2D (r_main_texturepool, va("%s_nmap", skinframe->basename), image_width, image_height, pixels, TEXTYPE_RGBA, skinframe->textureflags, NULL);
+                       Mem_Free(pixels);
+                       pixels = NULL;
+               }
+               else if (r_shadow_bumpscale_bumpmap.value > 0 && (bumppixels = loadimagepixels(va("%s_bump", skinframe->basename), false, 0, 0)) != NULL)
+               {
+                       pixels = (unsigned char *)Mem_Alloc(tempmempool, image_width * image_height * 4);
+                       Image_HeightmapToNormalmap(bumppixels, pixels, image_width, image_height, false, r_shadow_bumpscale_bumpmap.value);
+                       skinframe->nmap = R_LoadTexture2D (r_main_texturepool, va("%s_nmap", skinframe->basename), image_width, image_height, pixels, TEXTYPE_RGBA, skinframe->textureflags, NULL);
+                       Mem_Free(pixels);
+                       Mem_Free(bumppixels);
+               }
+               else if (r_shadow_bumpscale_basetexture.value > 0)
+               {
+                       pixels = (unsigned char *)Mem_Alloc(tempmempool, basepixels_width * basepixels_height * 4);
+                       Image_HeightmapToNormalmap(basepixels, pixels, basepixels_width, basepixels_height, false, r_shadow_bumpscale_basetexture.value);
+                       skinframe->nmap = R_LoadTexture2D (r_main_texturepool, va("%s_nmap", skinframe->basename), basepixels_width, basepixels_height, pixels, TEXTYPE_RGBA, skinframe->textureflags, NULL);
+                       Mem_Free(pixels);
+               }
+       }
+       // _luma is supported for tenebrae compatibility
+       // (I think it's a very stupid name, but oh well)
+       // _glow is the preferred name
+       if (loadglow          && ((pixels = loadimagepixels(va("%s_glow", skinframe->basename), false, 0, 0)) != NULL || (pixels = loadimagepixels(va("%s_luma", skinframe->basename), false, 0, 0)) != NULL)) {skinframe->glow = R_LoadTexture2D (r_main_texturepool, va("%s_glow", skinframe->basename), image_width, image_height, pixels, TEXTYPE_RGBA, skinframe->textureflags, NULL);Mem_Free(pixels);pixels = NULL;}
+       if (loadgloss         && (pixels = loadimagepixels(va("%s_gloss", skinframe->basename), false, 0, 0)) != NULL) {skinframe->gloss = R_LoadTexture2D (r_main_texturepool, va("%s_gloss", skinframe->basename), image_width, image_height, pixels, TEXTYPE_RGBA, skinframe->textureflags, NULL);Mem_Free(pixels);pixels = NULL;}
+       if (loadpantsandshirt && (pixels = loadimagepixels(va("%s_pants", skinframe->basename), false, 0, 0)) != NULL) {skinframe->pants = R_LoadTexture2D (r_main_texturepool, va("%s_pants", skinframe->basename), image_width, image_height, pixels, TEXTYPE_RGBA, skinframe->textureflags, NULL);Mem_Free(pixels);pixels = NULL;}
+       if (loadpantsandshirt && (pixels = loadimagepixels(va("%s_shirt", skinframe->basename), false, 0, 0)) != NULL) {skinframe->shirt = R_LoadTexture2D (r_main_texturepool, va("%s_shirt", skinframe->basename), image_width, image_height, pixels, TEXTYPE_RGBA, skinframe->textureflags, NULL);Mem_Free(pixels);pixels = NULL;}
+
+       if (basepixels)
+               Mem_Free(basepixels);
+
+       return skinframe;
+}
+
+static rtexture_t *R_SkinFrame_TextureForSkinLayer(const unsigned char *in, int width, int height, const char *name, const unsigned int *palette, int textureflags, qboolean force)
+{
+       int i;
+       if (!force)
+       {
+               for (i = 0;i < width*height;i++)
+                       if (((unsigned char *)&palette[in[i]])[3] > 0)
+                               break;
+               if (i == width*height)
+                       return NULL;
+       }
+       return R_LoadTexture2D (r_main_texturepool, name, width, height, in, TEXTYPE_PALETTE, textureflags, palette);
+}
+
+skinframe_t *R_SkinFrame_LoadInternal(const char *name, int textureflags, int loadpantsandshirt, int loadglowtexture, const unsigned char *skindata, int width, int height, int bitsperpixel, const unsigned int *palette, const unsigned int *alphapalette)
+{
+       int i;
+       unsigned char *temp1, *temp2;
+       skinframe_t *skinframe;
+
+       if (cls.state == ca_dedicated)
+               return NULL;
+
+       // if already loaded just return it, otherwise make a new skinframe
+       skinframe = R_SkinFrame_Find(name, textureflags, width, height, skindata ? CRC_Block(skindata, width*height*bitsperpixel/8) : 0, true);
+       if (skinframe && skinframe->base)
+               return skinframe;
+
+       skinframe->stain = NULL;
+       skinframe->merged = NULL;
+       skinframe->base = r_texture_notexture;
+       skinframe->pants = NULL;
+       skinframe->shirt = NULL;
+       skinframe->nmap = r_texture_blanknormalmap;
+       skinframe->gloss = NULL;
+       skinframe->glow = NULL;
+       skinframe->fog = NULL;
+
+       // if no data was provided, then clearly the caller wanted to get a blank skinframe
+       if (!skindata)
+               return NULL;
+
+       if (bitsperpixel == 32)
+       {
+               if (r_shadow_bumpscale_basetexture.value > 0)
+               {
+                       temp1 = (unsigned char *)Mem_Alloc(tempmempool, width * height * 8);
+                       temp2 = temp1 + width * height * 4;
+                       Image_HeightmapToNormalmap(skindata, temp2, width, height, false, r_shadow_bumpscale_basetexture.value);
+                       skinframe->nmap = R_LoadTexture2D(r_main_texturepool, va("%s_nmap", skinframe->basename), width, height, temp2, TEXTYPE_RGBA, textureflags | TEXF_ALPHA, NULL);
+                       Mem_Free(temp1);
+               }
+               skinframe->base = skinframe->merged = R_LoadTexture2D(r_main_texturepool, skinframe->basename, width, height, skindata, TEXTYPE_RGBA, textureflags, NULL);
+               if (textureflags & TEXF_ALPHA)
+               {
+                       for (i = 3;i < width * height * 4;i += 4)
+                               if (skindata[i] < 255)
+                                       break;
+                       if (i < width * height * 4)
+                       {
+                               unsigned char *fogpixels = (unsigned char *)Mem_Alloc(tempmempool, width * height * 4);
+                               memcpy(fogpixels, skindata, width * height * 4);
+                               for (i = 0;i < width * height * 4;i += 4)
+                                       fogpixels[i] = fogpixels[i+1] = fogpixels[i+2] = 255;
+                               skinframe->fog = R_LoadTexture2D(r_main_texturepool, va("%s_fog", skinframe->basename), width, height, fogpixels, TEXTYPE_RGBA, textureflags, NULL);
+                               Mem_Free(fogpixels);
+                       }
+               }
+       }
+       else if (bitsperpixel == 8)
+       {
+               if (r_shadow_bumpscale_basetexture.value > 0)
+               {
+                       temp1 = (unsigned char *)Mem_Alloc(tempmempool, width * height * 8);
+                       temp2 = temp1 + width * height * 4;
+                       if (bitsperpixel == 32)
+                               Image_HeightmapToNormalmap(skindata, temp2, width, height, false, r_shadow_bumpscale_basetexture.value);
+                       else
+                       {
+                               // use either a custom palette or the quake palette
+                               Image_Copy8bitRGBA(skindata, temp1, width * height, palette ? palette : palette_complete);
+                               Image_HeightmapToNormalmap(temp1, temp2, width, height, false, r_shadow_bumpscale_basetexture.value);
+                       }
+                       skinframe->nmap = R_LoadTexture2D(r_main_texturepool, va("%s_nmap", skinframe->basename), width, height, temp2, TEXTYPE_RGBA, textureflags | TEXF_ALPHA, NULL);
+                       Mem_Free(temp1);
+               }
+               // use either a custom palette, or the quake palette
+               skinframe->base = skinframe->merged = R_SkinFrame_TextureForSkinLayer(skindata, width, height, va("%s_merged", skinframe->basename), palette ? palette : (loadglowtexture ? palette_nofullbrights : ((textureflags & TEXF_ALPHA) ? palette_transparent : palette_complete)), textureflags, true); // all
+               if (!palette && loadglowtexture)
+                       skinframe->glow = R_SkinFrame_TextureForSkinLayer(skindata, width, height, va("%s_glow", skinframe->basename), palette_onlyfullbrights, textureflags, false); // glow
+               if (!palette && loadpantsandshirt)
+               {
+                       skinframe->pants = R_SkinFrame_TextureForSkinLayer(skindata, width, height, va("%s_pants", skinframe->basename), palette_pantsaswhite, textureflags, false); // pants
+                       skinframe->shirt = R_SkinFrame_TextureForSkinLayer(skindata, width, height, va("%s_shirt", skinframe->basename), palette_shirtaswhite, textureflags, false); // shirt
+               }
+               if (skinframe->pants || skinframe->shirt)
+                       skinframe->base = R_SkinFrame_TextureForSkinLayer(skindata, width, height, va("%s_nospecial", skinframe->basename),loadglowtexture ? palette_nocolormapnofullbrights : palette_nocolormap, textureflags, false); // no special colors
+               if (textureflags & TEXF_ALPHA)
+               {
+                       // if not using a custom alphapalette, use the quake one
+                       if (!alphapalette)
+                               alphapalette = palette_alpha;
+                       for (i = 0;i < width * height;i++)
+                               if (((unsigned char *)alphapalette)[skindata[i]*4+3] < 255)
+                                       break;
+                       if (i < width * height)
+                               skinframe->fog = R_SkinFrame_TextureForSkinLayer(skindata, width, height, va("%s_fog", skinframe->basename), alphapalette, textureflags, true); // fog mask
+               }
+       }
+
+       return skinframe;
+}
+
+skinframe_t *R_SkinFrame_LoadMissing(void)
+{
+       skinframe_t *skinframe;
+
+       if (cls.state == ca_dedicated)
+               return NULL;
+
+       skinframe = R_SkinFrame_Find("missing", TEXF_PRECACHE, 0, 0, 0, true);
+       skinframe->stain = NULL;
+       skinframe->merged = NULL;
+       skinframe->base = r_texture_notexture;
+       skinframe->pants = NULL;
+       skinframe->shirt = NULL;
+       skinframe->nmap = r_texture_blanknormalmap;
+       skinframe->gloss = NULL;
+       skinframe->glow = NULL;
+       skinframe->fog = NULL;
+
+       return skinframe;
+}
+
 void gl_main_start(void)
 {
        int x;
@@ -1048,6 +1373,11 @@ void gl_main_start(void)
                r_refdef.fogmasktable[x] = bound(0, alpha, 1);
        }
 
+       // set up r_skinframe loading system for textures
+       memset(&r_skinframe, 0, sizeof(r_skinframe));
+       r_skinframe.loadsequence = 1;
+       Mem_ExpandableArray_NewArray(&r_skinframe.array, r_main_mempool, sizeof(skinframe_t), 256);
+
        r_main_texturepool = R_AllocTexturePool();
        R_BuildBlankTextures();
        R_BuildNoTexture();
@@ -1064,6 +1394,10 @@ void gl_main_start(void)
 
 void gl_main_shutdown(void)
 {
+       // clear out the r_skinframe state
+       Mem_ExpandableArray_FreeArray(&r_skinframe.array);
+       memset(&r_skinframe, 0, sizeof(r_skinframe));
+
        if (r_svbsp.nodes)
                Mem_Free(r_svbsp.nodes);
        memset(&r_svbsp, 0, sizeof (r_svbsp));
@@ -2771,9 +3105,9 @@ void R_UpdateTextureInfo(const entity_render_t *ent, texture_t *t)
 
        // pick a new currentskinframe if the material is animated
        if (t->numskinframes >= 2)
-               t->currentskinframe = t->skinframes + ((int)(t->skinframerate * (cl.time - ent->frame2time)) % t->numskinframes);
+               t->currentskinframe = t->skinframes[(int)(t->skinframerate * (cl.time - ent->frame2time)) % t->numskinframes];
        if (t->backgroundnumskinframes >= 2)
-               t->backgroundcurrentskinframe = t->backgroundskinframes + ((int)(t->backgroundskinframerate * (cl.time - ent->frame2time)) % t->backgroundnumskinframes);
+               t->backgroundcurrentskinframe = t->backgroundskinframes[(int)(t->backgroundskinframerate * (cl.time - ent->frame2time)) % t->backgroundnumskinframes];
 
        t->currentmaterialflags = t->basematerialflags;
        t->currentalpha = ent->alpha;
index 2448ded..c245edb 100644 (file)
@@ -1111,6 +1111,7 @@ void R_ReplaceWorldTexture (void)
        texture_t       *t;
        int                     i;
        const char      *r, *newt;
+       skinframe_t *skinframe;
        m = r_refdef.worldmodel;
 
        if(Cmd_Argc() < 2)
@@ -1132,15 +1133,15 @@ void R_ReplaceWorldTexture (void)
        {
                if(t->width && !strcasecmp(t->name, r))
                {
-                       if(Mod_LoadSkinFrame(&t->skinframes[0], (char*)newt, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, r_fullbrights.integer))
+                       if ((skinframe = R_SkinFrame_LoadExternal((char*)newt, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP)))
                        {
+                               t->skinframes[0] = skinframe;
                                Con_Printf("%s replaced with %s\n", r, newt);
                                return;
                        }
                        else
                        {
                                Con_Printf("%s was not found\n", newt);
-                               Mod_LoadSkinFrame(&t->skinframes[0], (char*)r, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, r_fullbrights.integer);//back to default
                                return;
                        }
                }
index b4e723f..b0f0af3 100644 (file)
@@ -674,18 +674,14 @@ static void Mod_MDL_LoadFrames (unsigned char* datapointer, int inverts, int *ve
 
 static void Mod_BuildAliasSkinFromSkinFrame(texture_t *texture, skinframe_t *skinframe)
 {
+       // hack
+       if (!skinframe)
+               skinframe = R_SkinFrame_LoadMissing();
        texture->currentframe = texture;
        texture->numskinframes = 1;
        texture->skinframerate = 1;
-       texture->currentskinframe = texture->skinframes + 0;
-       if (skinframe)
-               texture->skinframes[0] = *skinframe;
-       else
-       {
-               // hack
-               memset(texture->skinframes, 0, sizeof(texture->skinframes));
-               texture->skinframes[0].base = r_texture_notexture;
-       }
+       texture->skinframes[0] = skinframe;
+       texture->currentskinframe = skinframe;
 
        texture->basematerialflags = MATERIALFLAG_WALL;
        if (texture->currentskinframe->fog)
@@ -697,7 +693,7 @@ static void Mod_BuildAliasSkinsFromSkinFiles(texture_t *skin, skinfile_t *skinfi
 {
        int i;
        skinfileitem_t *skinfileitem;
-       skinframe_t tempskinframe;
+       skinframe_t *tempskinframe;
        if (skinfile)
        {
                // the skin += loadmodel->num_surfaces part of this is because data_textures on alias models is arranged as [numskins][numsurfaces]
@@ -713,10 +709,11 @@ static void Mod_BuildAliasSkinsFromSkinFiles(texture_t *skin, skinfile_t *skinfi
                                // leave the skin unitialized (nodraw) if the replacement is "common/nodraw" or "textures/common/nodraw"
                                if (!strcmp(skinfileitem->name, meshname) && strcmp(skinfileitem->replacement, "common/nodraw") && strcmp(skinfileitem->replacement, "textures/common/nodraw"))
                                {
-                                       if (!Mod_LoadSkinFrame(&tempskinframe, skinfileitem->replacement, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, true, true))
+                                       tempskinframe = R_SkinFrame_LoadExternal(skinfileitem->replacement, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP);
+                                       if (!tempskinframe)
                                                if (cls.state != ca_dedicated)
                                                        Con_DPrintf("mesh \"%s\": failed to load skin #%i \"%s\"\n", meshname, i, skinfileitem->replacement);
-                                       Mod_BuildAliasSkinFromSkinFrame(skin, &tempskinframe);
+                                       Mod_BuildAliasSkinFromSkinFrame(skin, tempskinframe);
                                        break;
                                }
                        }
@@ -724,10 +721,11 @@ static void Mod_BuildAliasSkinsFromSkinFiles(texture_t *skin, skinfile_t *skinfi
        }
        else
        {
-               if (!Mod_LoadSkinFrame(&tempskinframe, shadername, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, true, true))
+               tempskinframe = R_SkinFrame_LoadExternal(shadername, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP);
+               if (!tempskinframe)
                        if (cls.state != ca_dedicated)
                                Con_Printf("Can't find texture \"%s\" for mesh \"%s\", using grey checkerboard\n", shadername, meshname);
-               Mod_BuildAliasSkinFromSkinFrame(skin, &tempskinframe);
+               Mod_BuildAliasSkinFromSkinFrame(skin, tempskinframe);
        }
 }
 
@@ -749,7 +747,7 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
        daliasgroup_t *pinframegroup;
        unsigned char *datapointer, *startframes, *startskins;
        char name[MAX_QPATH];
-       skinframe_t tempskinframe;
+       skinframe_t *tempskinframe;
        animscene_t *tempskinscenes;
        texture_t *tempaliasskins;
        float *vertst;
@@ -992,16 +990,17 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
                                        sprintf (name, "%s_%i_%i", loadmodel->name, i, j);
                                else
                                        sprintf (name, "%s_%i", loadmodel->name, i);
-                               if (!Mod_LoadSkinFrame(&tempskinframe, name, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PICMIP, true, true))
-                                       Mod_LoadSkinFrame_Internal(&tempskinframe, name, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_PICMIP, true, r_fullbrights.integer, (unsigned char *)datapointer, skinwidth, skinheight, 8, NULL, NULL);
-                               Mod_BuildAliasSkinFromSkinFrame(loadmodel->data_textures + totalskins * loadmodel->num_surfaces, &tempskinframe);
+                               tempskinframe = R_SkinFrame_LoadExternal(name, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PICMIP);
+                               if (!tempskinframe)
+                                       tempskinframe = R_SkinFrame_LoadInternal(name, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_PICMIP, true, r_fullbrights.integer, (unsigned char *)datapointer, skinwidth, skinheight, 8, NULL, NULL);
+                               Mod_BuildAliasSkinFromSkinFrame(loadmodel->data_textures + totalskins * loadmodel->num_surfaces, tempskinframe);
                                datapointer += skinwidth * skinheight;
                                totalskins++;
                        }
                }
                // check for skins that don't exist in the model, but do exist as external images
                // (this was added because yummyluv kept pestering me about support for it)
-               while (Mod_LoadSkinFrame(&tempskinframe, va("%s_%i", loadmodel->name, loadmodel->numskins), (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PICMIP, true, true))
+               while ((tempskinframe = R_SkinFrame_LoadExternal(va("%s_%i", loadmodel->name, loadmodel->numskins), (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PICMIP)))
                {
                        // expand the arrays to make room
                        tempskinscenes = loadmodel->skinscenes;
@@ -1015,7 +1014,7 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
                        Mem_Free(tempaliasskins);
 
                        // store the info about the new skin
-                       Mod_BuildAliasSkinFromSkinFrame(loadmodel->data_textures + totalskins * loadmodel->num_surfaces, &tempskinframe);
+                       Mod_BuildAliasSkinFromSkinFrame(loadmodel->data_textures + totalskins * loadmodel->num_surfaces, tempskinframe);
                        strlcpy(loadmodel->skinscenes[loadmodel->numskins].name, name, sizeof(loadmodel->skinscenes[loadmodel->numskins].name));
                        loadmodel->skinscenes[loadmodel->numskins].firstframe = totalskins;
                        loadmodel->skinscenes[loadmodel->numskins].framecount = 1;
@@ -1029,10 +1028,7 @@ void Mod_IDP0_Load(model_t *mod, void *buffer, void *bufferend)
                        // fix up the pointers since they are pointing at the old textures array
                        // FIXME: this is a hack!
                        for (j = 0;j < loadmodel->numskins * loadmodel->num_surfaces;j++)
-                       {
                                loadmodel->data_textures[j].currentframe = &loadmodel->data_textures[j];
-                               loadmodel->data_textures[j].currentskinframe = &loadmodel->data_textures[j].skinframes[0];
-                       }
                }
        }
 
@@ -1065,7 +1061,7 @@ void Mod_IDP2_Load(model_t *mod, void *buffer, void *bufferend)
                unsigned short st;
        }
        *hash, **md2verthash, *md2verthashdata;
-       skinframe_t tempskinframe;
+       skinframe_t *tempskinframe;
        skinfile_t *skinfiles;
 
        pinmodel = (md2_t *)buffer;
@@ -1147,9 +1143,10 @@ void Mod_IDP2_Load(model_t *mod, void *buffer, void *bufferend)
                loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_surfaces * loadmodel->numskins * sizeof(texture_t));
                for (i = 0;i < loadmodel->numskins;i++, inskin += MD2_SKINNAME)
                {
-                       if (!Mod_LoadSkinFrame(&tempskinframe, inskin, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, true, true))
+                       tempskinframe = R_SkinFrame_LoadExternal(inskin, (r_mipskins.integer ? TEXF_MIPMAP : 0) | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP);
+                       if (!tempskinframe)
                                Con_Printf("%s is missing skin \"%s\"\n", loadmodel->name, inskin);
-                       Mod_BuildAliasSkinFromSkinFrame(loadmodel->data_textures + i * loadmodel->num_surfaces, &tempskinframe);
+                       Mod_BuildAliasSkinFromSkinFrame(loadmodel->data_textures + i * loadmodel->num_surfaces, tempskinframe);
                }
        }
        else
index d3ce374..53191ef 100644 (file)
@@ -1252,6 +1252,10 @@ void R_Q1BSP_LoadSplitSky (unsigned char *src, int width, int height, int bytesp
        int i, j;
        unsigned solidpixels[128*128], alphapixels[128*128];
 
+       // allocate a texture pool if we need it
+       if (loadmodel->texturepool == NULL && cls.state != ca_dedicated)
+               loadmodel->texturepool = R_AllocTexturePool();
+
        // if sky isn't the right size, just use it as a solid layer
        if (width != 256 || height != 128)
        {
@@ -1314,6 +1318,7 @@ void R_Q1BSP_LoadSplitSky (unsigned char *src, int width, int height, int bytesp
 static void Mod_Q1BSP_LoadTextures(lump_t *l)
 {
        int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
+       skinframe_t *skinframe;
        miptex_t *dmiptex;
        texture_t *tx, *tx2, *anims[10], *altanims[10];
        dmiptexlump_t *m;
@@ -1339,6 +1344,7 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l)
        loadmodel->data_textures = (texture_t *)Mem_Alloc(loadmodel->mempool, loadmodel->num_textures * sizeof(texture_t));
 
        // fill out all slots with notexture
+       skinframe = R_SkinFrame_LoadMissing();
        for (i = 0, tx = loadmodel->data_textures;i < loadmodel->num_textures;i++, tx++)
        {
                strlcpy(tx->name, "NO TEXTURE FOUND", sizeof(tx->name));
@@ -1346,9 +1352,8 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l)
                tx->height = 16;
                tx->numskinframes = 1;
                tx->skinframerate = 1;
-               tx->currentskinframe = tx->skinframes;
-               tx->skinframes[0].base = r_texture_notexture;
-               tx->backgroundcurrentskinframe = tx->backgroundskinframes;
+               tx->skinframes[0] = skinframe;
+               tx->currentskinframe = tx->skinframes[0];
                tx->basematerialflags = 0;
                if (i == loadmodel->num_textures - 1)
                {
@@ -1441,8 +1446,10 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l)
                        }
                        else
                        {
-                               if (!Mod_LoadSkinFrame(&tx->skinframes[0], gamemode == GAME_TENEBRAE ? tx->name : va("textures/%s/%s", mapname, tx->name), TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, true)
-                                && !Mod_LoadSkinFrame(&tx->skinframes[0], gamemode == GAME_TENEBRAE ? tx->name : va("textures/%s", tx->name), TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, true))
+                               skinframe = R_SkinFrame_LoadExternal(gamemode == GAME_TENEBRAE ? tx->name : va("textures/%s/%s", mapname, tx->name), TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP);
+                               if (!skinframe)
+                                       skinframe = R_SkinFrame_LoadExternal(gamemode == GAME_TENEBRAE ? tx->name : va("textures/%s", tx->name), TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP);
+                               if (!skinframe)
                                {
                                        // did not find external texture, load it from the bsp or wad3
                                        if (loadmodel->brush.ishlbsp)
@@ -1458,21 +1465,17 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l)
                                                {
                                                        tx->width = image_width;
                                                        tx->height = image_height;
-                                                       Mod_LoadSkinFrame_Internal(&tx->skinframes[0], tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, false, pixels, image_width, image_height, 32, NULL, NULL);
+                                                       skinframe = R_SkinFrame_LoadInternal(tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, false, pixels, image_width, image_height, 32, NULL, NULL);
                                                }
                                                if (freepixels)
                                                        Mem_Free(freepixels);
                                        }
                                        else if (mtdata) // texture included
-                                               Mod_LoadSkinFrame_Internal(&tx->skinframes[0], tx->name, TEXF_MIPMAP | TEXF_PRECACHE | TEXF_PICMIP, false, r_fullbrights.integer, mtdata, tx->width, tx->height, 8, NULL, NULL);
+                                               skinframe = R_SkinFrame_LoadInternal(tx->name, TEXF_MIPMAP | TEXF_PRECACHE | TEXF_PICMIP, false, r_fullbrights.integer, mtdata, tx->width, tx->height, 8, NULL, NULL);
                                }
-                       }
-                       if (tx->skinframes[0].base == NULL)
-                       {
-                               // no texture found
-                               tx->width = 16;
-                               tx->height = 16;
-                               tx->skinframes[0].base = r_texture_notexture;
+                               // if skinframe is still NULL the "missing" texture will be used
+                               if (skinframe)
+                                       tx->skinframes[0] = skinframe;
                        }
                }
 
@@ -1513,11 +1516,12 @@ static void Mod_Q1BSP_LoadTextures(lump_t *l)
                        tx->surfaceflags = mod_q1bsp_texture_solid.surfaceflags;
                        tx->basematerialflags |= MATERIALFLAG_WALL;
                }
-               if (tx->skinframes[0].fog)
+               if (tx->skinframes[0]->fog)
                        tx->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
 
                // start out with no animation
                tx->currentframe = tx;
+               tx->currentskinframe = tx->skinframes[0];
        }
 
        // sequence the animations
@@ -2299,6 +2303,9 @@ static void Mod_Q1BSP_LoadFaces(lump_t *l)
                        // 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 && 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)
@@ -4537,7 +4544,7 @@ Q3 shader blendfuncs actually used in the game (* = supported by DP)
                                out->numskinframes = shader->primarylayer->numframes;
                                out->skinframerate = shader->primarylayer->framerate;
                                for (j = 0;j < shader->primarylayer->numframes;j++)
-                                       if (!Mod_LoadSkinFrame(&out->skinframes[j], shader->primarylayer->texturename[j], ((shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS) ? 0 : TEXF_MIPMAP) | TEXF_ALPHA | TEXF_PRECACHE | (shader->textureflags & Q3TEXTUREFLAG_NOPICMIP ? 0 : TEXF_PICMIP) | (shader->primarylayer->clampmap ? TEXF_CLAMP : 0), false, true))
+                                       if (!(out->skinframes[j] = R_SkinFrame_LoadExternal(shader->primarylayer->texturename[j], ((shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS) ? 0 : TEXF_MIPMAP) | TEXF_ALPHA | TEXF_PRECACHE | (shader->textureflags & Q3TEXTUREFLAG_NOPICMIP ? 0 : TEXF_PICMIP) | (shader->primarylayer->clampmap ? TEXF_CLAMP : 0))))
                                                Con_DPrintf("%s: could not load texture \"%s\" (frame %i) for shader \"%s\"\n", loadmodel->name, shader->primarylayer->texturename[j], j, out->name);
                        }
                        if (shader->backgroundlayer && cls.state != ca_dedicated)
@@ -4546,8 +4553,13 @@ Q3 shader blendfuncs actually used in the game (* = supported by DP)
                                out->backgroundnumskinframes = shader->backgroundlayer->numframes;
                                out->backgroundskinframerate = shader->backgroundlayer->framerate;
                                for (j = 0;j < shader->backgroundlayer->numframes;j++)
-                                       if (!Mod_LoadSkinFrame(&out->backgroundskinframes[j], shader->backgroundlayer->texturename[j], ((shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS) ? 0 : TEXF_MIPMAP) | TEXF_ALPHA | TEXF_PRECACHE | (shader->textureflags & Q3TEXTUREFLAG_NOPICMIP ? 0 : TEXF_PICMIP) | (shader->backgroundlayer->clampmap ? TEXF_CLAMP : 0), false, true))
+                               {
+                                       if (!(out->backgroundskinframes[j] = R_SkinFrame_LoadExternal(shader->backgroundlayer->texturename[j], ((shader->surfaceparms & Q3SURFACEPARM_NOMIPMAPS) ? 0 : TEXF_MIPMAP) | TEXF_ALPHA | TEXF_PRECACHE | (shader->textureflags & Q3TEXTUREFLAG_NOPICMIP ? 0 : TEXF_PICMIP) | (shader->backgroundlayer->clampmap ? TEXF_CLAMP : 0))))
+                                       {
                                                Con_DPrintf("%s: could not load texture \"%s\" (frame %i) for shader \"%s\"\n", loadmodel->name, shader->backgroundlayer->texturename[j], j, out->name);
+                                               out->backgroundskinframes[j] = R_SkinFrame_LoadMissing();
+                                       }
+                               }
                        }
                }
                else if (!strcmp(out->name, "noshader"))
@@ -4572,13 +4584,15 @@ Q3 shader blendfuncs actually used in the game (* = supported by DP)
                        //if (R_TextureHasAlpha(out->skinframes[0].base))
                        //      out->surfaceparms |= Q3SURFACEPARM_TRANS;
                        if (cls.state != ca_dedicated)
-                               if (!Mod_LoadSkinFrame(&out->skinframes[0], out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP, false, true))
+                               if (!(out->skinframes[0] = R_SkinFrame_LoadExternal(out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE | TEXF_PICMIP)))
                                        Con_DPrintf("%s: could not load texture for missing shader \"%s\"\n", loadmodel->name, out->name);
                }
                // init the animation variables
                out->currentframe = out;
-               out->currentskinframe = &out->skinframes[0];
-               out->backgroundcurrentskinframe = &out->backgroundskinframes[0];
+               if (!out->skinframes[0])
+                       out->skinframes[0] = R_SkinFrame_LoadMissing();
+               out->currentskinframe = out->skinframes[0];
+               out->backgroundcurrentskinframe = out->backgroundskinframes[0];
        }
        if (c)
                Con_DPrintf("%s: %i textures missing shaders\n", loadmodel->name, c);
@@ -4860,6 +4874,10 @@ static void Mod_Q3BSP_LoadLightmaps(lump_t *l, lump_t *faceslump)
        if (loadmodel->brushq3.deluxemapping)
                loadmodel->brushq3.data_deluxemaps = (rtexture_t **)Mem_Alloc(loadmodel->mempool, loadmodel->brushq3.num_mergedlightmaps * sizeof(rtexture_t *));
 
+       // allocate a texture pool if we need it
+       if (loadmodel->texturepool == NULL && cls.state != ca_dedicated)
+               loadmodel->texturepool = R_AllocTexturePool();
+
        j = 128 << loadmodel->brushq3.num_lightmapmergepower;
        if (loadmodel->brushq3.data_lightmaps)
                for (i = 0;i < loadmodel->brushq3.num_mergedlightmaps;i++)
index a796f3f..7929878 100644 (file)
@@ -67,7 +67,27 @@ static void mod_shutdown(void)
 static void mod_newmap(void)
 {
        msurface_t *surface;
-       int i, surfacenum, ssize, tsize;
+       int i, j, k, numtextures, surfacenum, ssize, tsize;
+
+       R_SkinFrame_PrepareForPurge();
+       for (i = 0;i < mod_numknown;i++)
+       {
+               if (mod_known[i].mempool && mod_known[i].data_textures)
+               {
+                       numtextures = mod_known[i].num_textures;
+                       // models can have multiple sets of textures
+                       if (mod_known[i].numskins > 1)
+                               numtextures *= mod_known[i].numskins;
+                       for (j = 0;j < numtextures;j++)
+                       {
+                               for (k = 0;k < mod_known[i].data_textures[j].numskinframes;k++)
+                                       R_SkinFrame_MarkUsed(mod_known[i].data_textures[j].skinframes[k]);
+                               for (k = 0;k < mod_known[i].data_textures[j].backgroundnumskinframes;k++)
+                                       R_SkinFrame_MarkUsed(mod_known[i].data_textures[j].backgroundskinframes[k]);
+                       }
+               }
+       }
+       R_SkinFrame_Purge();
 
        if (!cl_stainmaps_clearonload.integer)
                return;
@@ -218,9 +238,6 @@ model_t *Mod_LoadModel(model_t *mod, qboolean crash, qboolean checkdisk, qboolea
 
                // all models use memory, so allocate a memory pool
                mod->mempool = Mem_AllocPool(mod->name, 0, NULL);
-               // all models load textures, so allocate a texture pool
-               if (cls.state != ca_dedicated)
-                       mod->texturepool = R_AllocTexturePool();
 
                num = LittleLong(*((int *)buf));
                // call the apropriate loader
@@ -1031,192 +1048,6 @@ void Mod_ShadowMesh_Free(shadowmesh_t *mesh)
        }
 }
 
-static rtexture_t *GL_TextureForSkinLayer(const unsigned char *in, int width, int height, const char *name, const unsigned int *palette, int textureflags, qboolean force)
-{
-       int i;
-       if (!force)
-       {
-               for (i = 0;i < width*height;i++)
-                       if (((unsigned char *)&palette[in[i]])[3] > 0)
-                               break;
-               if (i == width*height)
-                       return NULL;
-       }
-       return R_LoadTexture2D (loadmodel->texturepool, name, width, height, in, TEXTYPE_PALETTE, textureflags, palette);
-}
-
-int Mod_LoadSkinFrame(skinframe_t *skinframe, const char *basename, int textureflags, qboolean loadpantsandshirt, qboolean loadglowtexture)
-{
-       // FIXME: it should be possible to disable loading gloss and normalmap using cvars, to prevent wasted loading time and memory usage
-       qboolean loadnormalmap = true;
-       qboolean loadgloss = true;
-       int j;
-       unsigned char *pixels;
-       unsigned char *bumppixels;
-       unsigned char *basepixels;
-       int basepixels_width;
-       int basepixels_height;
-       char name[MAX_QPATH];
-       memset(skinframe, 0, sizeof(*skinframe));
-       Image_StripImageExtension(basename, name, sizeof(name));
-       skinframe->base = r_texture_notexture;
-       if (cls.state == ca_dedicated)
-               return false;
-
-       basepixels = loadimagepixels(name, false, 0, 0);
-       if (basepixels == NULL)
-               return false;
-       basepixels_width = image_width;
-       basepixels_height = image_height;
-       skinframe->base = R_LoadTexture2D (loadmodel->texturepool, basename, basepixels_width, basepixels_height, basepixels, TEXTYPE_RGBA, textureflags, NULL);
-
-       if (textureflags & TEXF_ALPHA)
-       {
-               for (j = 3;j < basepixels_width * basepixels_height * 4;j += 4)
-                       if (basepixels[j] < 255)
-                               break;
-               if (j < basepixels_width * basepixels_height * 4)
-               {
-                       // has transparent pixels
-                       pixels = (unsigned char *)Mem_Alloc(loadmodel->mempool, image_width * image_height * 4);
-                       for (j = 0;j < image_width * image_height * 4;j += 4)
-                       {
-                               pixels[j+0] = 255;
-                               pixels[j+1] = 255;
-                               pixels[j+2] = 255;
-                               pixels[j+3] = basepixels[j+3];
-                       }
-                       skinframe->fog = R_LoadTexture2D (loadmodel->texturepool, va("%s_mask", basename), image_width, image_height, pixels, TEXTYPE_RGBA, textureflags, NULL);
-                       Mem_Free(pixels);
-               }
-       }
-
-       // _luma is supported for tenebrae compatibility
-       // (I think it's a very stupid name, but oh well)
-       if (loadglowtexture && ((pixels = loadimagepixels(va("%s_glow", name), false, 0, 0)) != NULL || (pixels = loadimagepixels(va("%s_luma", name), false, 0, 0)) != NULL)) {skinframe->glow = R_LoadTexture2D (loadmodel->texturepool, va("%s_glow", basename), image_width, image_height, pixels, TEXTYPE_RGBA, textureflags, NULL);Mem_Free(pixels);pixels = NULL;}
-       // _norm is the name used by tenebrae and has been adopted as standard
-       if (loadnormalmap)
-       {
-               if ((pixels = loadimagepixels(va("%s_norm", name), false, 0, 0)) != NULL)
-               {
-                       skinframe->nmap = R_LoadTexture2D (loadmodel->texturepool, va("%s_nmap", basename), image_width, image_height, pixels, TEXTYPE_RGBA, textureflags, NULL);
-                       Mem_Free(pixels);
-                       pixels = NULL;
-               }
-               else if (r_shadow_bumpscale_bumpmap.value > 0 && (bumppixels = loadimagepixels(va("%s_bump", name), false, 0, 0)) != NULL)
-               {
-                       pixels = (unsigned char *)Mem_Alloc(loadmodel->mempool, image_width * image_height * 4);
-                       Image_HeightmapToNormalmap(bumppixels, pixels, image_width, image_height, false, r_shadow_bumpscale_bumpmap.value);
-                       skinframe->nmap = R_LoadTexture2D (loadmodel->texturepool, va("%s_nmap", basename), image_width, image_height, pixels, TEXTYPE_RGBA, textureflags, NULL);
-                       Mem_Free(pixels);
-                       Mem_Free(bumppixels);
-               }
-               else if (r_shadow_bumpscale_basetexture.value > 0)
-               {
-                       pixels = (unsigned char *)Mem_Alloc(loadmodel->mempool, basepixels_width * basepixels_height * 4);
-                       Image_HeightmapToNormalmap(basepixels, pixels, basepixels_width, basepixels_height, false, r_shadow_bumpscale_basetexture.value);
-                       skinframe->nmap = R_LoadTexture2D (loadmodel->texturepool, va("%s_nmap", basename), basepixels_width, basepixels_height, pixels, TEXTYPE_RGBA, textureflags, NULL);
-                       Mem_Free(pixels);
-               }
-       }
-       if (loadgloss         && (pixels = loadimagepixels(va("%s_gloss", name), false, 0, 0)) != NULL) {skinframe->gloss = R_LoadTexture2D (loadmodel->texturepool, va("%s_gloss", basename), image_width, image_height, pixels, TEXTYPE_RGBA, textureflags, NULL);Mem_Free(pixels);pixels = NULL;}
-       if (loadpantsandshirt && (pixels = loadimagepixels(va("%s_pants", name), false, 0, 0)) != NULL) {skinframe->pants = R_LoadTexture2D (loadmodel->texturepool, va("%s_pants", basename), image_width, image_height, pixels, TEXTYPE_RGBA, textureflags, NULL);Mem_Free(pixels);pixels = NULL;}
-       if (loadpantsandshirt && (pixels = loadimagepixels(va("%s_shirt", name), false, 0, 0)) != NULL) {skinframe->shirt = R_LoadTexture2D (loadmodel->texturepool, va("%s_shirt", basename), image_width, image_height, pixels, TEXTYPE_RGBA, textureflags, NULL);Mem_Free(pixels);pixels = NULL;}
-
-       if (!skinframe->base)
-               skinframe->base = r_texture_notexture;
-       if (!skinframe->nmap)
-               skinframe->nmap = r_texture_blanknormalmap;
-
-       if (basepixels)
-               Mem_Free(basepixels);
-
-       return true;
-}
-
-int Mod_LoadSkinFrame_Internal(skinframe_t *skinframe, const char *basename, int textureflags, int loadpantsandshirt, int loadglowtexture, const unsigned char *skindata, int width, int height, int bitsperpixel, const unsigned int *palette, const unsigned int *alphapalette)
-{
-       int i;
-       unsigned char *temp1, *temp2;
-       memset(skinframe, 0, sizeof(*skinframe));
-       if (cls.state == ca_dedicated)
-               return false;
-       if (!skindata)
-               return false;
-       if (bitsperpixel == 32)
-       {
-               if (r_shadow_bumpscale_basetexture.value > 0)
-               {
-                       temp1 = (unsigned char *)Mem_Alloc(loadmodel->mempool, width * height * 8);
-                       temp2 = temp1 + width * height * 4;
-                       Image_HeightmapToNormalmap(skindata, temp2, width, height, false, r_shadow_bumpscale_basetexture.value);
-                       skinframe->nmap = R_LoadTexture2D(loadmodel->texturepool, va("%s_nmap", basename), width, height, temp2, TEXTYPE_RGBA, textureflags | TEXF_ALPHA, NULL);
-                       Mem_Free(temp1);
-               }
-               skinframe->base = skinframe->merged = R_LoadTexture2D(loadmodel->texturepool, basename, width, height, skindata, TEXTYPE_RGBA, textureflags, NULL);
-               if (textureflags & TEXF_ALPHA)
-               {
-                       for (i = 3;i < width * height * 4;i += 4)
-                               if (skindata[i] < 255)
-                                       break;
-                       if (i < width * height * 4)
-                       {
-                               unsigned char *fogpixels = (unsigned char *)Mem_Alloc(loadmodel->mempool, width * height * 4);
-                               memcpy(fogpixels, skindata, width * height * 4);
-                               for (i = 0;i < width * height * 4;i += 4)
-                                       fogpixels[i] = fogpixels[i+1] = fogpixels[i+2] = 255;
-                               skinframe->fog = R_LoadTexture2D(loadmodel->texturepool, va("%s_fog", basename), width, height, fogpixels, TEXTYPE_RGBA, textureflags, NULL);
-                               Mem_Free(fogpixels);
-                       }
-               }
-       }
-       else if (bitsperpixel == 8)
-       {
-               if (r_shadow_bumpscale_basetexture.value > 0)
-               {
-                       temp1 = (unsigned char *)Mem_Alloc(loadmodel->mempool, width * height * 8);
-                       temp2 = temp1 + width * height * 4;
-                       if (bitsperpixel == 32)
-                               Image_HeightmapToNormalmap(skindata, temp2, width, height, false, r_shadow_bumpscale_basetexture.value);
-                       else
-                       {
-                               // use either a custom palette or the quake palette
-                               Image_Copy8bitRGBA(skindata, temp1, width * height, palette ? palette : palette_complete);
-                               Image_HeightmapToNormalmap(temp1, temp2, width, height, false, r_shadow_bumpscale_basetexture.value);
-                       }
-                       skinframe->nmap = R_LoadTexture2D(loadmodel->texturepool, va("%s_nmap", basename), width, height, temp2, TEXTYPE_RGBA, textureflags | TEXF_ALPHA, NULL);
-                       Mem_Free(temp1);
-               }
-               // use either a custom palette, or the quake palette
-               skinframe->base = skinframe->merged = GL_TextureForSkinLayer(skindata, width, height, va("%s_merged", basename), palette ? palette : (loadglowtexture ? palette_nofullbrights : ((textureflags & TEXF_ALPHA) ? palette_transparent : palette_complete)), textureflags, true); // all
-               if (!palette && loadglowtexture)
-                       skinframe->glow = GL_TextureForSkinLayer(skindata, width, height, va("%s_glow", basename), palette_onlyfullbrights, textureflags, false); // glow
-               if (!palette && loadpantsandshirt)
-               {
-                       skinframe->pants = GL_TextureForSkinLayer(skindata, width, height, va("%s_pants", basename), palette_pantsaswhite, textureflags, false); // pants
-                       skinframe->shirt = GL_TextureForSkinLayer(skindata, width, height, va("%s_shirt", basename), palette_shirtaswhite, textureflags, false); // shirt
-               }
-               if (skinframe->pants || skinframe->shirt)
-                       skinframe->base = GL_TextureForSkinLayer(skindata, width, height, va("%s_nospecial", basename),loadglowtexture ? palette_nocolormapnofullbrights : palette_nocolormap, textureflags, false); // no special colors
-               if (textureflags & TEXF_ALPHA)
-               {
-                       // if not using a custom alphapalette, use the quake one
-                       if (!alphapalette)
-                               alphapalette = palette_alpha;
-                       for (i = 0;i < width * height;i++)
-                               if (((unsigned char *)alphapalette)[skindata[i]*4+3] < 255)
-                                       break;
-                       if (i < width * height)
-                               skinframe->fog = GL_TextureForSkinLayer(skindata, width, height, va("%s_fog", basename), alphapalette, textureflags, true); // fog mask
-               }
-       }
-       else
-               return false;
-       if (!skinframe->nmap)
-               skinframe->nmap = r_texture_blanknormalmap;
-       return true;
-}
-
 void Mod_GetTerrainVertex3fTexCoord2fFromRGBA(const unsigned char *imagepixels, int imagewidth, int imageheight, int ix, int iy, float *vertex3f, float *texcoord2f, matrix4x4_t *pixelstepmatrix, matrix4x4_t *pixeltexturestepmatrix)
 {
        float v[3], tc[3];
index fe0ff49..023bba1 100644 (file)
@@ -53,6 +53,23 @@ typedef struct skinframe_s
        rtexture_t *gloss; // glossmap (for dot3)
        rtexture_t *glow; // glow only (fullbrights)
        rtexture_t *fog; // alpha of the base texture (if not opaque)
+       // accounting data for hash searches:
+       // the compare variables are used to identify internal skins from certain
+       // model formats
+       // (so that two q1bsp maps with the same texture name for different
+       //  textures do not have any conflicts)
+       struct skinframe_s *next; // next on hash chain
+       char basename[MAX_QPATH]; // name of this
+       int textureflags; // texture flags to use
+       int comparewidth;
+       int compareheight;
+       int comparecrc;
+       // mark and sweep garbage collection, this value is updated to a new value
+       // on each level change for the used skinframes, if some are not used they
+       // are freed
+       int loadsequence;
+       // on 32bit systems this makes the struct 128 bytes long
+       int padding;
 }
 skinframe_t;
 
@@ -218,12 +235,12 @@ typedef struct texture_s
        skinframe_t *currentskinframe;
        int numskinframes;
        float skinframerate;
-       skinframe_t skinframes[TEXTURE_MAXFRAMES];
+       skinframe_t *skinframes[TEXTURE_MAXFRAMES];
        // background layer (for terrain texture blending)
        skinframe_t *backgroundcurrentskinframe;
        int backgroundnumskinframes;
        float backgroundskinframerate;
-       skinframe_t backgroundskinframes[TEXTURE_MAXFRAMES];
+       skinframe_t *backgroundskinframes[TEXTURE_MAXFRAMES];
 
        // total frames in sequence and alternate sequence
        int anim_total[2];
@@ -261,8 +278,6 @@ typedef struct texture_s
        int supercontents;
        int surfaceparms;
        int textureflags;
-
-       //skinframe_t skin;
 }
 texture_t;
 
@@ -677,9 +692,6 @@ shadowmesh_t *Mod_ShadowMesh_Finish(mempool_t *mempool, shadowmesh_t *firstmesh,
 void Mod_ShadowMesh_CalcBBox(shadowmesh_t *firstmesh, vec3_t mins, vec3_t maxs, vec3_t center, float *radius);
 void Mod_ShadowMesh_Free(shadowmesh_t *mesh);
 
-int Mod_LoadSkinFrame(skinframe_t *skinframe, const char *basename, int textureflags, qboolean loadpantsandshirt, qboolean loadglowtexture);
-int Mod_LoadSkinFrame_Internal(skinframe_t *skinframe, const char *basename, int textureflags, int loadpantsandshirt, int loadglowtexture, const unsigned char *skindata, int width, int height, int bitsperpixel, const unsigned int *palette, const unsigned int *alphapalette);
-
 extern cvar_t r_mipskins;
 
 typedef struct skinfileitem_s
index 2843b5d..d2453d9 100644 (file)
@@ -47,10 +47,10 @@ static void Mod_SpriteSetupTexture(mspriteframe_t *frame, qboolean fullbright, q
                texture->basematerialflags |= MATERIALFLAG_FULLBRIGHT;
        if (additive)
                texture->basematerialflags |= MATERIALFLAG_ADD | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
-       else if (texture->skinframes[0].fog)
+       else if (texture->skinframes[0]->fog)
                texture->basematerialflags |= MATERIALFLAG_ALPHA | MATERIALFLAG_BLENDED | MATERIALFLAG_NOSHADOW;
        texture->currentmaterialflags = texture->basematerialflags;
-       texture->currentskinframe = texture->skinframes + 0;
+       texture->currentskinframe = texture->skinframes[0];
 }
 
 static void Mod_Sprite_SharedSetup(const unsigned char *datapointer, int version, const unsigned int *palette, const unsigned int *alphapalette, qboolean additive)
@@ -170,16 +170,16 @@ static void Mod_Sprite_SharedSetup(const unsigned char *datapointer, int version
                                        sprintf (name, "%s_%i_%i", loadmodel->name, i, j);
                                else
                                        sprintf (name, "%s_%i", loadmodel->name, i);
-                               if (!Mod_LoadSkinFrame(&loadmodel->sprite.sprdata_frames[realframes].texture.skinframes[0], name, texflags, false, false))
+                               if (!(loadmodel->sprite.sprdata_frames[realframes].texture.skinframes[0] = R_SkinFrame_LoadExternal(name, texflags)))
                                {
                                        if (groupframes > 1)
                                                sprintf (fogname, "%s_%i_%ifog", loadmodel->name, i, j);
                                        else
                                                sprintf (fogname, "%s_%ifog", loadmodel->name, i);
                                        if (version == SPRITE32_VERSION)
-                                               Mod_LoadSkinFrame_Internal(&loadmodel->sprite.sprdata_frames[realframes].texture.skinframes[0], name, texflags, false, false, datapointer, width, height, 32, NULL, NULL);
+                                               loadmodel->sprite.sprdata_frames[realframes].texture.skinframes[0] = R_SkinFrame_LoadInternal(name, texflags, false, false, datapointer, width, height, 32, NULL, NULL);
                                        else //if (version == SPRITE_VERSION || version == SPRITEHL_VERSION)
-                                               Mod_LoadSkinFrame_Internal(&loadmodel->sprite.sprdata_frames[realframes].texture.skinframes[0], name, texflags, false, false, datapointer, width, height, 8, palette, alphapalette);
+                                               loadmodel->sprite.sprdata_frames[realframes].texture.skinframes[0] = R_SkinFrame_LoadInternal(name, texflags, false, false, datapointer, width, height, 8, palette, alphapalette);
                                }
                        }
 
@@ -390,8 +390,13 @@ void Mod_IDS2_Load(model_t *mod, void *buffer, void *bufferend)
                        modelradius = x + y;
 
                if (width > 0 && height > 0 && cls.state != ca_dedicated)
-                       if (!Mod_LoadSkinFrame(&sprframe->texture.skinframes[0], pinframe->name, texflags, false, false))
+               {
+                       if (!(sprframe->texture.skinframes[0] = R_SkinFrame_LoadExternal(pinframe->name, texflags)))
+                       {
                                Con_Printf("Mod_IDS2_Load: failed to load %s", pinframe->name);
+                               sprframe->texture.skinframes[0] = R_SkinFrame_LoadExternal("missing", TEXF_PRECACHE);
+                       }
+               }
 
                Mod_SpriteSetupTexture(sprframe, fullbright, false);
        }
index f352e01..fd60cac 100644 (file)
@@ -75,7 +75,7 @@ void R_Model_Sprite_Draw_TransparentCallback(const entity_render_t *ent, const r
                        texture_t *texture = &frame->texture;
                        R_UpdateTextureInfo(ent, texture);
                        // FIXME: negate left and right in loader
-                       R_DrawSprite(texture->currentlayers[0].blendfunc1, texture->currentlayers[0].blendfunc2, frame->texture.currentskinframe->base, frame->texture.currentskinframe->fog, (texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST), (texture->currentmaterialflags & MATERIALFLAG_SHORTDEPTHRANGE), org, left, up, frame->left, frame->right, frame->down, frame->up, texture->currentlayers[0].color[0], texture->currentlayers[0].color[1], texture->currentlayers[0].color[2], ent->alpha * ent->frameblend[i].lerp);
+                       R_DrawSprite(texture->currentlayers[0].blendfunc1, texture->currentlayers[0].blendfunc2, texture->basetexture, texture->currentskinframe->fog, (texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST), (texture->currentmaterialflags & MATERIALFLAG_SHORTDEPTHRANGE), org, left, up, frame->left, frame->right, frame->down, frame->up, texture->currentlayers[0].color[0], texture->currentlayers[0].color[1], texture->currentlayers[0].color[2], ent->alpha * ent->frameblend[i].lerp);
                }
        }
 }
index dccf408..96469b1 100644 (file)
--- a/render.h
+++ b/render.h
@@ -128,6 +128,14 @@ void R_RenderView(void); // must set r_refdef and call R_UpdateVariables first
 
 void R_InitSky (unsigned char *src, int bytesperpixel); // called at level load
 
+void R_SkinFrame_PrepareForPurge(void);
+void R_SkinFrame_MarkUsed(skinframe_t *skinframe);
+void R_SkinFrame_Purge(void);
+skinframe_t *R_SkinFrame_Find(const char *name, int textureflags, int comparewidth, int compareheight, int comparecrc, qboolean add);
+skinframe_t *R_SkinFrame_LoadExternal(const char *name, int textureflags);
+skinframe_t *R_SkinFrame_LoadInternal(const char *name, int textureflags, int loadpantsandshirt, int loadglowtexture, const unsigned char *skindata, int width, int height, int bitsperpixel, const unsigned int *palette, const unsigned int *alphapalette);
+skinframe_t *R_SkinFrame_LoadMissing(void);
+
 void R_View_WorldVisibility();
 void R_DrawParticles(void);
 void R_DrawExplosions(void);