Modified: trunk/darkplaces/gl_rmain.c
===================================================================
--- trunk/darkplaces/gl_rmain.c	2007-04-24 13:12:35 UTC (rev 7167)
+++ trunk/darkplaces/gl_rmain.c	2007-04-24 13:19:07 UTC (rev 7168)
@@ -22,6 +22,7 @@
 #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 @@
 	}
 }
 
+#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 @@
 		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_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 @@
 
 	// 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;

Modified: trunk/darkplaces/gl_rsurf.c
===================================================================
--- trunk/darkplaces/gl_rsurf.c	2007-04-24 13:12:35 UTC (rev 7167)
+++ trunk/darkplaces/gl_rsurf.c	2007-04-24 13:19:07 UTC (rev 7168)
@@ -1111,6 +1111,7 @@
 	texture_t	*t;
 	int			i;
 	const char	*r, *newt;
+	skinframe_t *skinframe;
 	m = r_refdef.worldmodel;
 
 	if(Cmd_Argc() < 2)
@@ -1132,15 +1133,15 @@
 	{
 		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;
 			}
 		}

Modified: trunk/darkplaces/model_alias.c
===================================================================
--- trunk/darkplaces/model_alias.c	2007-04-24 13:12:35 UTC (rev 7167)
+++ trunk/darkplaces/model_alias.c	2007-04-24 13:19:07 UTC (rev 7168)
@@ -674,18 +674,14 @@
 
 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 @@
 {
 	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 @@
 				// 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 @@
 	}
 	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 @@
 	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 @@
 					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 @@
 			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 @@
 			// 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 @@
 		unsigned short st;
 	}
 	*hash, **md2verthash, *md2verthashdata;
-	skinframe_t tempskinframe;
+	skinframe_t *tempskinframe;
 	skinfile_t *skinfiles;
 
 	pinmodel = (md2_t *)buffer;
@@ -1147,9 +1143,10 @@
 		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

Modified: trunk/darkplaces/model_brush.c
===================================================================
--- trunk/darkplaces/model_brush.c	2007-04-24 13:12:35 UTC (rev 7167)
+++ trunk/darkplaces/model_brush.c	2007-04-24 13:19:07 UTC (rev 7168)
@@ -1252,6 +1252,10 @@
 	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 @@
 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 @@
 	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 @@
 		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 @@
 			}
 			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,22 +1465,18 @@
 						{
 							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 skinframe is still NULL the "missing" texture will be used
+				if (skinframe)
+					tx->skinframes[0] = skinframe;
 			}
-			if (tx->skinframes[0].base == NULL)
-			{
-				// no texture found
-				tx->width = 16;
-				tx->height = 16;
-				tx->skinframes[0].base = r_texture_notexture;
-			}
 		}
 
 		tx->basematerialflags = 0;
@@ -1513,11 +1516,12 @@
 			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 @@
 			// 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 @@
 				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 @@
 				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 @@
 			//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 @@
 	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++)

Modified: trunk/darkplaces/model_shared.c
===================================================================
--- trunk/darkplaces/model_shared.c	2007-04-24 13:12:35 UTC (rev 7167)
+++ trunk/darkplaces/model_shared.c	2007-04-24 13:19:07 UTC (rev 7168)
@@ -67,8 +67,28 @@
 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 @@
 
 		// 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 @@
 	}
 }
 
-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];

Modified: trunk/darkplaces/model_shared.h
===================================================================
--- trunk/darkplaces/model_shared.h	2007-04-24 13:12:35 UTC (rev 7167)
+++ trunk/darkplaces/model_shared.h	2007-04-24 13:19:07 UTC (rev 7168)
@@ -53,6 +53,23 @@
 	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 @@
 	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 @@
 	int supercontents;
 	int surfaceparms;
 	int textureflags;
-
-	//skinframe_t skin;
 }
 texture_t;
 
@@ -677,9 +692,6 @@
 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

Modified: trunk/darkplaces/model_sprite.c
===================================================================
--- trunk/darkplaces/model_sprite.c	2007-04-24 13:12:35 UTC (rev 7167)
+++ trunk/darkplaces/model_sprite.c	2007-04-24 13:19:07 UTC (rev 7168)
@@ -47,10 +47,10 @@
 		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 @@
 					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 @@
 			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);
 	}

Modified: trunk/darkplaces/r_sprites.c
===================================================================
--- trunk/darkplaces/r_sprites.c	2007-04-24 13:12:35 UTC (rev 7167)
+++ trunk/darkplaces/r_sprites.c	2007-04-24 13:19:07 UTC (rev 7168)
@@ -75,7 +75,7 @@
 			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);
 		}
 	}
 }

Modified: trunk/darkplaces/render.h
===================================================================
--- trunk/darkplaces/render.h	2007-04-24 13:12:35 UTC (rev 7167)
+++ trunk/darkplaces/render.h	2007-04-24 13:19:07 UTC (rev 7168)
@@ -128,6 +128,14 @@
 
 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);

