#include "quakedef.h"
#include "r_shadow.h"
#include "polygon.h"
+#include "image.h"
mempool_t *r_main_mempool;
rtexturepool_t *r_main_texturepool;
}
}
+#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;
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();
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));
// 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;
texture_t *t;
int i;
const char *r, *newt;
+ skinframe_t *skinframe;
m = r_refdef.worldmodel;
if(Cmd_Argc() < 2)
{
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;
}
}
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)
{
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]
// 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;
}
}
}
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);
}
}
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;
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;
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;
// 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];
- }
}
}
unsigned short st;
}
*hash, **md2verthash, *md2verthashdata;
- skinframe_t tempskinframe;
+ skinframe_t *tempskinframe;
skinfile_t *skinfiles;
pinmodel = (md2_t *)buffer;
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
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)
{
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;
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));
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)
{
}
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)
{
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;
}
}
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
// 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)
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)
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"))
//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);
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++)
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;
// 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
}
}
-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];
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;
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];
int supercontents;
int surfaceparms;
int textureflags;
-
- //skinframe_t skin;
}
texture_t;
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
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)
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);
}
}
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);
}
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);
}
}
}
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);