From 276e5f9befb7c9ff042e400d9ae8e5cfb657aea1 Mon Sep 17 00:00:00 2001 From: havoc Date: Tue, 24 Apr 2007 13:19:07 +0000 Subject: [PATCH] replaced Mod_LoadSkinFrame functions with R_SkinFrame system, this 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 | 338 ++++++++++++++++++++++++++++++++++++++++++++++++- gl_rsurf.c | 5 +- model_alias.c | 49 ++++--- model_brush.c | 58 ++++++--- model_shared.c | 211 +++--------------------------- model_shared.h | 26 +++- model_sprite.c | 17 ++- r_sprites.c | 2 +- render.h | 8 ++ 9 files changed, 460 insertions(+), 254 deletions(-) diff --git a/gl_rmain.c b/gl_rmain.c index b34880db..b3294517 100644 --- a/gl_rmain.c +++ b/gl_rmain.c @@ -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; diff --git a/gl_rsurf.c b/gl_rsurf.c index 2448ded5..c245edb7 100644 --- a/gl_rsurf.c +++ b/gl_rsurf.c @@ -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; } } diff --git a/model_alias.c b/model_alias.c index b4e723f9..b0f0af3b 100644 --- a/model_alias.c +++ b/model_alias.c @@ -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 diff --git a/model_brush.c b/model_brush.c index d3ce3744..53191efd 100644 --- a/model_brush.c +++ b/model_brush.c @@ -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++) diff --git a/model_shared.c b/model_shared.c index a796f3f7..79298781 100644 --- a/model_shared.c +++ b/model_shared.c @@ -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]; diff --git a/model_shared.h b/model_shared.h index fe0ff497..023bba19 100644 --- a/model_shared.h +++ b/model_shared.h @@ -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 diff --git a/model_sprite.c b/model_sprite.c index 2843b5d0..d2453d99 100644 --- a/model_sprite.c +++ b/model_sprite.c @@ -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); } diff --git a/r_sprites.c b/r_sprites.c index f352e017..fd60cac4 100644 --- a/r_sprites.c +++ b/r_sprites.c @@ -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); } } } diff --git a/render.h b/render.h index dccf4084..96469b1c 100644 --- 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); -- 2.39.2