+ int textype;
+ int inputbytesperpixel;
+ int internalbytesperpixel;
+ int glformat;
+ int glinternalformat;
+ int align;
+}
+textypeinfo_t;
+
+static textypeinfo_t textype_qpalette = {TEXTYPE_QPALETTE, 1, 4, GL_RGBA, 3, 1};
+static textypeinfo_t textype_rgb = {TEXTYPE_RGB , 3, 3, GL_RGB , 3, 3};
+static textypeinfo_t textype_rgba = {TEXTYPE_RGBA , 4, 4, GL_RGBA, 3, 1};
+static textypeinfo_t textype_qpalette_alpha = {TEXTYPE_QPALETTE, 1, 4, GL_RGBA, 4, 1};
+static textypeinfo_t textype_rgba_alpha = {TEXTYPE_RGBA , 4, 4, GL_RGBA, 4, 1};
+
+// a tiling texture (most common type)
+#define GLIMAGETYPE_TILE 0
+// a fragments texture (contains one or more fragment textures)
+#define GLIMAGETYPE_FRAGMENTS 1
+
+// a gltextureimage can have one (or more if fragments) gltextures inside
+typedef struct gltextureimage_s
+{
+ struct gltextureimage_s *imagechain;
+ int texturecount;
+ int type; // one of the GLIMAGETYPE_ values
+ int texnum; // GL texture slot number
+ int width, height;
+ int bytesperpixel; // bytes per pixel
+ int glformat; // GL_RGB or GL_RGBA
+ int glinternalformat; // 3 or 4
+ int flags;
+ short *blockallocation; // fragment allocation
+}
+gltextureimage_t;
+
+typedef struct gltexture_s
+{
+ // pointer to texturepool (check this to see if the texture is allocated)
+ struct gltexturepool_s *pool;
+ // pointer to next texture in texturepool chain
+ struct gltexture_s *chain;
+ // pointer into gltextureimage array
+ gltextureimage_t *image;
+ // name of the texture (this might be removed someday), no duplicates
+ char *identifier;
+ // location in the image, and size
+ int x, y, width, height;
+ // copy of the original texture supplied to the upload function, for re-uploading or deferred uploads (non-precached)
+ qbyte *inputtexels;
+ // to identify cache mismatchs (this might be removed someday)
+ int crc;
+ // flags supplied to the LoadTexture/ProceduralTexture functions
+ // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
+ int flags;
+ // procedural texture generation function, called once per frame if the texture is used
+ int (*generate)(qbyte *buffer, int width, int height, void *parameterdata, int parameterdatasize);
+ // data provided to generate, persistent from call to call
+ qbyte *proceduraldata;
+ // size of data
+ int proceduraldatasize;
+ // used only to avoid updating the texture more than once per frame
+ int proceduralframecount;
+ // pointer to one of the textype_ structs
+ textypeinfo_t *textype;
+}
+gltexture_t;
+
+#define TEXTUREPOOL_SENTINEL 0xC0DEDBAD
+
+typedef struct gltexturepool_s
+{
+ int sentinel;
+ struct gltextureimage_s *imagechain;
+ struct gltexture_s *gltchain;
+ struct gltexturepool_s *next;
+}
+gltexturepool_t;
+
+static gltexturepool_t *gltexturepoolchain = NULL;
+
+static qbyte *resizebuffer = NULL, *colorconvertbuffer;
+static int resizebuffersize = 0;
+static qbyte *texturebuffer;
+static int texturebuffersize = 0;
+
+static int realmaxsize = 0;
+
+static textypeinfo_t *R_GetTexTypeInfo(int textype, int flags)
+{
+ if (flags & TEXF_ALPHA)
+ {
+ switch(textype)
+ {
+ case TEXTYPE_QPALETTE:
+ return &textype_qpalette_alpha;
+ case TEXTYPE_RGB:
+ Host_Error("R_GetTexTypeInfo: RGB format has no alpha, TEXF_ALPHA not allowed\n");
+ return NULL;
+ case TEXTYPE_RGBA:
+ return &textype_rgba_alpha;
+ default:
+ Host_Error("R_GetTexTypeInfo: unknown texture format\n");
+ return NULL;
+ }
+ }
+ else
+ {
+ switch(textype)
+ {
+ case TEXTYPE_QPALETTE:
+ return &textype_qpalette;
+ case TEXTYPE_RGB:
+ return &textype_rgb;
+ case TEXTYPE_RGBA:
+ return &textype_rgba;
+ default:
+ Host_Error("R_GetTexTypeInfo: unknown texture format\n");
+ return NULL;
+ }
+ }
+}
+
+static void R_UploadTexture(gltexture_t *t);
+
+static void R_PrecacheTexture(gltexture_t *glt)
+{
+ int precache;
+ precache = false;
+ if (glt->flags & TEXF_ALWAYSPRECACHE)
+ precache = true;
+ else if (r_precachetextures.integer >= 2)
+ precache = true;
+ else if (r_precachetextures.integer >= 1)
+ if (glt->flags & TEXF_PRECACHE)
+ precache = true;
+
+ if (precache)
+ R_UploadTexture(glt);
+}
+
+int R_GetTexture(rtexture_t *rt)
+{
+ gltexture_t *glt;
+ if (!rt)
+ return 0;
+ glt = (gltexture_t *)rt;
+ if (glt->flags & (GLTEXF_UPLOAD | GLTEXF_PROCEDURAL))
+ {
+ if (glt->flags & GLTEXF_PROCEDURAL)
+ {
+ if (glt->proceduralframecount != r_framecount)
+ {
+ glt->proceduralframecount = r_framecount;
+ R_UploadTexture(glt);
+ }
+ }
+ else
+ R_UploadTexture(glt);
+ }
+ return glt->image->texnum;
+}
+
+static void R_FreeTexture(gltexture_t *glt)
+{
+ gltexture_t **gltpointer;
+ gltextureimage_t *image, **gltimagepointer;
+ GLuint texnum;
+
+ if (glt == NULL)
+ Host_Error("R_FreeTexture: texture == NULL\n");
+
+ for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
+ if (*gltpointer == glt)
+ *gltpointer = glt->chain;
+ else
+ Host_Error("R_FreeTexture: texture \"%s\" not linked in pool\n", glt->identifier);
+
+ // note: if freeing a fragment texture, this will not make the claimed
+ // space available for new textures unless all other fragments in the
+ // image are also freed
+ image = glt->image;
+ image->texturecount--;
+ if (image->texturecount < 1)
+ {
+ for (gltimagepointer = &glt->pool->imagechain;*gltimagepointer && *gltimagepointer != image;gltimagepointer = &(*gltimagepointer)->imagechain);
+ if (*gltimagepointer == image)
+ *gltimagepointer = image->imagechain;
+ else
+ Host_Error("R_FreeTexture: image not linked in pool\n");
+ if (image->texnum)
+ {
+ texnum = image->texnum;
+ gltexnuminuse[image->texnum] = 0;
+ glDeleteTextures(1, &texnum);
+ }
+ if (image->blockallocation)
+ Mem_Free(image->blockallocation);
+ Mem_Free(image);
+ }
+
+ if (glt->identifier)
+ Mem_Free(glt->identifier);
+ if (glt->inputtexels)
+ Mem_Free(glt->inputtexels);
+ if (glt->proceduraldata)
+ Mem_Free(glt->proceduraldata);
+ Mem_Free(glt);
+}
+
+static gltexture_t *R_FindTexture (gltexturepool_t *pool, char *identifier)
+{
+ gltexture_t *glt;
+
+ if (!identifier)
+ return NULL;
+
+ for (glt = pool->gltchain;glt;glt = glt->chain)
+ if (glt->identifier && !strcmp (identifier, glt->identifier))
+ return glt;
+
+ return NULL;
+}
+
+rtexturepool_t *R_AllocTexturePool(void)
+{
+ gltexturepool_t *pool;
+ if (texturemempool == NULL)
+ return NULL;
+ pool = Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
+ if (pool == NULL)
+ return NULL;
+ //memset(pool, 0, sizeof(gltexturepool_t));
+ pool->next = gltexturepoolchain;
+ gltexturepoolchain = pool;
+ pool->sentinel = TEXTUREPOOL_SENTINEL;
+ return (rtexturepool_t *)pool;
+}
+
+void R_FreeTexturePool(rtexturepool_t **rtexturepool)
+{
+ gltexturepool_t *pool, **poolpointer;
+ if (rtexturepool == NULL)
+ return;
+ if (*rtexturepool == NULL)
+ return;
+ pool = (gltexturepool_t *)(*rtexturepool);
+ *rtexturepool = NULL;
+ if (pool->sentinel != TEXTUREPOOL_SENTINEL)
+ Host_Error("R_FreeTexturePool: pool already freed\n");
+ for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
+ if (*poolpointer == pool)
+ *poolpointer = pool->next;
+ else
+ Host_Error("R_FreeTexturePool: pool not linked\n");
+ while (pool->gltchain)
+ R_FreeTexture(pool->gltchain);
+ if (pool->imagechain)
+ Sys_Error("R_FreeTexturePool: not all images freed\n");
+ Mem_Free(pool);
+}
+