implemented fake reflection cubemaps... requires that a .shader
[divverent/darkplaces.git] / gl_textures.c
1
2 #include "quakedef.h"
3 #include "image.h"
4 #include "jpeg.h"
5 #include "image_png.h"
6 #include "intoverflow.h"
7
8 cvar_t gl_max_size = {CVAR_SAVE, "gl_max_size", "2048", "maximum allowed texture size, can be used to reduce video memory usage, limited by hardware capabilities (typically 2048, 4096, or 8192)"};
9 cvar_t gl_max_lightmapsize = {CVAR_SAVE, "gl_max_lightmapsize", "1024", "maximum allowed texture size for lightmap textures, use larger values to improve rendering speed, as long as there is enough video memory available (setting it too high for the hardware will cause very bad performance)"};
10 cvar_t gl_picmip = {CVAR_SAVE, "gl_picmip", "0", "reduces resolution of textures by powers of 2, for example 1 will halve width/height, reducing texture memory usage by 75%"};
11 cvar_t r_lerpimages = {CVAR_SAVE, "r_lerpimages", "1", "bilinear filters images when scaling them up to power of 2 size (mode 1), looks better than glquake (mode 0)"};
12 cvar_t gl_texture_anisotropy = {CVAR_SAVE, "gl_texture_anisotropy", "1", "anisotropic filtering quality (if supported by hardware), 1 sample (no anisotropy) and 8 sample (8 tap anisotropy) are recommended values"};
13 cvar_t gl_texturecompression = {CVAR_SAVE, "gl_texturecompression", "0", "whether to compress textures, a value of 0 disables compression (even if the individual cvars are 1), 1 enables fast (low quality) compression at startup, 2 enables slow (high quality) compression at startup"};
14 cvar_t gl_texturecompression_color = {CVAR_SAVE, "gl_texturecompression_color", "1", "whether to compress colormap (diffuse) textures"};
15 cvar_t gl_texturecompression_normal = {CVAR_SAVE, "gl_texturecompression_normal", "0", "whether to compress normalmap (normalmap) textures"};
16 cvar_t gl_texturecompression_gloss = {CVAR_SAVE, "gl_texturecompression_gloss", "1", "whether to compress glossmap (specular) textures"};
17 cvar_t gl_texturecompression_glow = {CVAR_SAVE, "gl_texturecompression_glow", "1", "whether to compress glowmap (luma) textures"};
18 cvar_t gl_texturecompression_2d = {CVAR_SAVE, "gl_texturecompression_2d", "0", "whether to compress 2d (hud/menu) textures other than the font"};
19 cvar_t gl_texturecompression_q3bsplightmaps = {CVAR_SAVE, "gl_texturecompression_q3bsplightmaps", "0", "whether to compress lightmaps in q3bsp format levels"};
20 cvar_t gl_texturecompression_q3bspdeluxemaps = {CVAR_SAVE, "gl_texturecompression_q3bspdeluxemaps", "0", "whether to compress deluxemaps in q3bsp format levels (only levels compiled with q3map2 -deluxe have these)"};
21 cvar_t gl_texturecompression_sky = {CVAR_SAVE, "gl_texturecompression_sky", "0", "whether to compress sky textures"};
22 cvar_t gl_texturecompression_lightcubemaps = {CVAR_SAVE, "gl_texturecompression_lightcubemaps", "1", "whether to compress light cubemaps (spotlights and other light projection images)"};
23 cvar_t gl_texturecompression_reflectmask = {CVAR_SAVE, "gl_texturecompression_reflectmask", "1", "whether to compress reflection cubemap masks (mask of which areas of the texture should reflect the generic shiny cubemap)"};
24 cvar_t gl_nopartialtextureupdates = {CVAR_SAVE, "gl_nopartialtextureupdates", "1", "use alternate path for dynamic lightmap updates that avoids a possibly slow code path in the driver"};
25
26 int             gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
27 int             gl_filter_mag = GL_LINEAR;
28
29
30 static mempool_t *texturemempool;
31 static memexpandablearray_t texturearray;
32
33 // note: this must not conflict with TEXF_ flags in r_textures.h
34 // bitmask for mismatch checking
35 #define GLTEXF_IMPORTANTBITS (0)
36 // dynamic texture (treat texnum == 0 differently)
37 #define GLTEXF_DYNAMIC          0x00080000
38
39 typedef struct textypeinfo_s
40 {
41         textype_t textype;
42         int inputbytesperpixel;
43         int internalbytesperpixel;
44         float glinternalbytesperpixel;
45         int glinternalformat;
46         int glformat;
47         int gltype;
48 }
49 textypeinfo_t;
50
51
52 static textypeinfo_t textype_palette                = {TEXTYPE_PALETTE    , 1, 4, 4.0f, 3                               , GL_BGRA           , GL_UNSIGNED_BYTE };
53 static textypeinfo_t textype_palette_alpha          = {TEXTYPE_PALETTE    , 1, 4, 4.0f, 4                               , GL_BGRA           , GL_UNSIGNED_BYTE };
54 static textypeinfo_t textype_rgba                   = {TEXTYPE_RGBA       , 4, 4, 4.0f, 3                               , GL_RGBA           , GL_UNSIGNED_BYTE };
55 static textypeinfo_t textype_rgba_alpha             = {TEXTYPE_RGBA       , 4, 4, 4.0f, 4                               , GL_RGBA           , GL_UNSIGNED_BYTE };
56 static textypeinfo_t textype_rgba_compress          = {TEXTYPE_RGBA       , 4, 4, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , GL_RGBA           , GL_UNSIGNED_BYTE };
57 static textypeinfo_t textype_rgba_alpha_compress    = {TEXTYPE_RGBA       , 4, 4, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA           , GL_UNSIGNED_BYTE };
58 static textypeinfo_t textype_bgra                   = {TEXTYPE_BGRA       , 4, 4, 4.0f, 3                               , GL_BGRA           , GL_UNSIGNED_BYTE };
59 static textypeinfo_t textype_bgra_alpha             = {TEXTYPE_BGRA       , 4, 4, 4.0f, 4                               , GL_BGRA           , GL_UNSIGNED_BYTE };
60 static textypeinfo_t textype_bgra_compress          = {TEXTYPE_BGRA       , 4, 4, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , GL_BGRA           , GL_UNSIGNED_BYTE };
61 static textypeinfo_t textype_bgra_alpha_compress    = {TEXTYPE_BGRA       , 4, 4, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_BGRA           , GL_UNSIGNED_BYTE };
62 static textypeinfo_t textype_shadowmap16            = {TEXTYPE_SHADOWMAP  , 2, 2, 2.0f, GL_DEPTH_COMPONENT16_ARB        , GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT};
63 static textypeinfo_t textype_shadowmap24            = {TEXTYPE_SHADOWMAP  , 4, 4, 4.0f, GL_DEPTH_COMPONENT24_ARB        , GL_DEPTH_COMPONENT, GL_UNSIGNED_INT  };
64 static textypeinfo_t textype_alpha                  = {TEXTYPE_ALPHA      , 1, 4, 4.0f, GL_ALPHA                        , GL_ALPHA          , GL_UNSIGNED_BYTE };
65 static textypeinfo_t textype_dxt1                   = {TEXTYPE_DXT1       , 4, 0, 0.5f, GL_COMPRESSED_RGB_S3TC_DXT1_EXT , 0                 , 0                };
66 static textypeinfo_t textype_dxt1a                  = {TEXTYPE_DXT1A      , 4, 0, 0.5f, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0                 , 0                };
67 static textypeinfo_t textype_dxt3                   = {TEXTYPE_DXT3       , 4, 0, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0                 , 0                };
68 static textypeinfo_t textype_dxt5                   = {TEXTYPE_DXT5       , 4, 0, 1.0f, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0                 , 0                };
69 static textypeinfo_t textype_colorbuffer            = {TEXTYPE_COLORBUFFER, 4, 4, 4.0f, 4                               , GL_BGRA           , GL_UNSIGNED_BYTE };
70
71
72 typedef enum gltexturetype_e
73 {
74         GLTEXTURETYPE_2D,
75         GLTEXTURETYPE_3D,
76         GLTEXTURETYPE_CUBEMAP,
77         GLTEXTURETYPE_RECTANGLE,
78         GLTEXTURETYPE_TOTAL
79 }
80 gltexturetype_t;
81
82 static int gltexturetypeenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_RECTANGLE_ARB};
83 static int gltexturetypedimensions[GLTEXTURETYPE_TOTAL] = {2, 3, 2, 2};
84 static int cubemapside[6] =
85 {
86         GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
87         GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
88         GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
89         GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
90         GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
91         GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
92 };
93
94 typedef struct gltexture_s
95 {
96         // this portion of the struct is exposed to the R_GetTexture macro for
97         // speed reasons, must be identical in rtexture_t!
98         int texnum; // GL texture slot number
99         qboolean dirty; // indicates that R_RealGetTexture should be called
100         int gltexturetypeenum; // used by R_Mesh_TexBind
101
102         // dynamic texture stuff [11/22/2007 Black]
103         updatecallback_t updatecallback;
104         void *updatacallback_data;
105         // --- [11/22/2007 Black]
106
107         // stores backup copy of texture for deferred texture updates (gl_nopartialtextureupdates cvar)
108         unsigned char *bufferpixels;
109         qboolean buffermodified;
110
111         // pointer to texturepool (check this to see if the texture is allocated)
112         struct gltexturepool_s *pool;
113         // pointer to next texture in texturepool chain
114         struct gltexture_s *chain;
115         // name of the texture (this might be removed someday), no duplicates
116         char identifier[MAX_QPATH + 32];
117         // original data size in *inputtexels
118         int inputwidth, inputheight, inputdepth;
119         // copy of the original texture(s) supplied to the upload function, for
120         // delayed uploads (non-precached)
121         unsigned char *inputtexels;
122         // original data size in *inputtexels
123         int inputdatasize;
124         // flags supplied to the LoadTexture function
125         // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
126         int flags;
127         // pointer to one of the textype_ structs
128         textypeinfo_t *textype;
129         // one of the GLTEXTURETYPE_ values
130         int texturetype;
131         // palette if the texture is TEXTYPE_PALETTE
132         const unsigned int *palette;
133         // actual stored texture size after gl_picmip and gl_max_size are applied
134         // (power of 2 if vid.support.arb_texture_non_power_of_two is not supported)
135         int tilewidth, tileheight, tiledepth;
136         // 1 or 6 depending on texturetype
137         int sides;
138         // bytes per pixel
139         int bytesperpixel;
140         // GL_RGB or GL_RGBA or GL_DEPTH_COMPONENT
141         int glformat;
142         // 3 or 4
143         int glinternalformat;
144         // GL_UNSIGNED_BYTE or GL_UNSIGNED_INT or GL_UNSIGNED_SHORT or GL_FLOAT
145         int gltype;
146 }
147 gltexture_t;
148
149 #define TEXTUREPOOL_SENTINEL 0xC0DEDBAD
150
151 typedef struct gltexturepool_s
152 {
153         unsigned int sentinel;
154         struct gltexture_s *gltchain;
155         struct gltexturepool_s *next;
156 }
157 gltexturepool_t;
158
159 static gltexturepool_t *gltexturepoolchain = NULL;
160
161 static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
162 static int resizebuffersize = 0;
163 static const unsigned char *texturebuffer;
164 static int texturebuffersize = 0;
165
166 static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
167 {
168         switch(textype)
169         {
170         case TEXTYPE_DXT1:
171                 return &textype_dxt1;
172         case TEXTYPE_DXT1A:
173                 return &textype_dxt1a;
174         case TEXTYPE_DXT3:
175                 return &textype_dxt3;
176         case TEXTYPE_DXT5:
177                 return &textype_dxt5;
178         case TEXTYPE_PALETTE:
179                 return (flags & TEXF_ALPHA) ? &textype_palette_alpha : &textype_palette;
180         case TEXTYPE_RGBA:
181                 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
182                         return (flags & TEXF_ALPHA) ? &textype_rgba_alpha_compress : &textype_rgba_compress;
183                 else
184                         return (flags & TEXF_ALPHA) ? &textype_rgba_alpha : &textype_rgba;
185                 break;
186         case TEXTYPE_BGRA:
187                 if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
188                         return (flags & TEXF_ALPHA) ? &textype_bgra_alpha_compress : &textype_bgra_compress;
189                 else
190                         return (flags & TEXF_ALPHA) ? &textype_bgra_alpha : &textype_bgra;
191                 break;
192         case TEXTYPE_ALPHA:
193                 return &textype_alpha;
194         case TEXTYPE_SHADOWMAP:
195                 return (flags & TEXF_LOWPRECISION) ? &textype_shadowmap16 : &textype_shadowmap24;
196         case TEXTYPE_COLORBUFFER:
197                 return &textype_colorbuffer;
198         default:
199                 Host_Error("R_GetTexTypeInfo: unknown texture format");
200                 return NULL;
201         }
202         return NULL; // this line only to hush compiler warnings
203 }
204
205 // dynamic texture code [11/22/2007 Black]
206 void R_MarkDirtyTexture(rtexture_t *rt) {
207         gltexture_t *glt = (gltexture_t*) rt;
208         if( !glt ) {
209                 return;
210         }
211
212         // dont do anything if the texture is already dirty (and make sure this *is* a dynamic texture after all!)
213         if (glt->flags & GLTEXF_DYNAMIC)
214         {
215                 // mark it as dirty, so R_RealGetTexture gets called
216                 glt->dirty = true;
217         }
218 }
219
220 void R_MakeTextureDynamic(rtexture_t *rt, updatecallback_t updatecallback, void *data) {
221         gltexture_t *glt = (gltexture_t*) rt;
222         if( !glt ) {
223                 return;
224         }
225
226         glt->flags |= GLTEXF_DYNAMIC;
227         glt->updatecallback = updatecallback;
228         glt->updatacallback_data = data;
229 }
230
231 static void R_UpdateDynamicTexture(gltexture_t *glt) {
232         glt->dirty = false;
233         if( glt->updatecallback ) {
234                 glt->updatecallback( (rtexture_t*) glt, glt->updatacallback_data );
235         }
236 }
237
238 void R_PurgeTexture(rtexture_t *rt)
239 {
240         if(rt && !(((gltexture_t*) rt)->flags & TEXF_PERSISTENT)) {
241                 R_FreeTexture(rt);
242         }
243 }
244
245 void R_FreeTexture(rtexture_t *rt)
246 {
247         gltexture_t *glt, **gltpointer;
248
249         glt = (gltexture_t *)rt;
250         if (glt == NULL)
251                 Host_Error("R_FreeTexture: texture == NULL");
252
253         for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
254         if (*gltpointer == glt)
255                 *gltpointer = glt->chain;
256         else
257                 Host_Error("R_FreeTexture: texture \"%s\" not linked in pool", glt->identifier);
258
259         if (glt->texnum)
260         {
261                 CHECKGLERROR
262                 qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
263         }
264
265         if (glt->inputtexels)
266                 Mem_Free(glt->inputtexels);
267         Mem_ExpandableArray_FreeRecord(&texturearray, glt);
268 }
269
270 rtexturepool_t *R_AllocTexturePool(void)
271 {
272         gltexturepool_t *pool;
273         if (texturemempool == NULL)
274                 return NULL;
275         pool = (gltexturepool_t *)Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
276         if (pool == NULL)
277                 return NULL;
278         pool->next = gltexturepoolchain;
279         gltexturepoolchain = pool;
280         pool->sentinel = TEXTUREPOOL_SENTINEL;
281         return (rtexturepool_t *)pool;
282 }
283
284 void R_FreeTexturePool(rtexturepool_t **rtexturepool)
285 {
286         gltexturepool_t *pool, **poolpointer;
287         if (rtexturepool == NULL)
288                 return;
289         if (*rtexturepool == NULL)
290                 return;
291         pool = (gltexturepool_t *)(*rtexturepool);
292         *rtexturepool = NULL;
293         if (pool->sentinel != TEXTUREPOOL_SENTINEL)
294                 Host_Error("R_FreeTexturePool: pool already freed");
295         for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
296         if (*poolpointer == pool)
297                 *poolpointer = pool->next;
298         else
299                 Host_Error("R_FreeTexturePool: pool not linked");
300         while (pool->gltchain)
301                 R_FreeTexture((rtexture_t *)pool->gltchain);
302         Mem_Free(pool);
303 }
304
305
306 typedef struct glmode_s
307 {
308         char *name;
309         int minification, magnification;
310 }
311 glmode_t;
312
313 static glmode_t modes[6] =
314 {
315         {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
316         {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
317         {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
318         {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
319         {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
320         {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
321 };
322
323 static void GL_TextureMode_f (void)
324 {
325         int i;
326         GLint oldbindtexnum;
327         gltexture_t *glt;
328         gltexturepool_t *pool;
329
330         if (Cmd_Argc() == 1)
331         {
332                 for (i = 0;i < 6;i++)
333                 {
334                         if (gl_filter_min == modes[i].minification)
335                         {
336                                 Con_Printf("%s\n", modes[i].name);
337                                 return;
338                         }
339                 }
340                 Con_Print("current filter is unknown???\n");
341                 return;
342         }
343
344         for (i = 0;i < 6;i++)
345                 if (!strcasecmp (modes[i].name, Cmd_Argv(1) ) )
346                         break;
347         if (i == 6)
348         {
349                 Con_Print("bad filter name\n");
350                 return;
351         }
352
353         gl_filter_min = modes[i].minification;
354         gl_filter_mag = modes[i].magnification;
355
356         // change all the existing mipmap texture objects
357         // FIXME: force renderer(/client/something?) restart instead?
358         CHECKGLERROR
359         GL_ActiveTexture(0);
360         for (pool = gltexturepoolchain;pool;pool = pool->next)
361         {
362                 for (glt = pool->gltchain;glt;glt = glt->chain)
363                 {
364                         // only update already uploaded images
365                         if (glt->texnum && !(glt->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR)))
366                         {
367                                 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
368                                 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
369                                 if (glt->flags & TEXF_MIPMAP)
370                                 {
371                                         qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
372                                 }
373                                 else
374                                 {
375                                         qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
376                                 }
377                                 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
378                                 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
379                         }
380                 }
381         }
382 }
383
384 static void GL_Texture_CalcImageSize(int texturetype, int flags, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth)
385 {
386         int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1;
387
388         switch (texturetype)
389         {
390         default:
391         case GLTEXTURETYPE_2D:
392                 maxsize = vid.maxtexturesize_2d;
393                 if (flags & TEXF_PICMIP)
394                 {
395                         maxsize = bound(1, gl_max_size.integer, maxsize);
396                         picmip = gl_picmip.integer;
397                 }
398                 break;
399         case GLTEXTURETYPE_3D:
400                 maxsize = vid.maxtexturesize_3d;
401                 break;
402         case GLTEXTURETYPE_CUBEMAP:
403                 maxsize = vid.maxtexturesize_cubemap;
404                 break;
405         }
406
407         if (outwidth)
408         {
409                 if (vid.support.arb_texture_non_power_of_two)
410                         width2 = min(inwidth >> picmip, maxsize);
411                 else
412                 {
413                         for (width2 = 1;width2 < inwidth;width2 <<= 1);
414                         for (width2 >>= picmip;width2 > maxsize;width2 >>= 1);
415                 }
416                 *outwidth = max(1, width2);
417         }
418         if (outheight)
419         {
420                 if (vid.support.arb_texture_non_power_of_two)
421                         height2 = min(inheight >> picmip, maxsize);
422                 else
423                 {
424                         for (height2 = 1;height2 < inheight;height2 <<= 1);
425                         for (height2 >>= picmip;height2 > maxsize;height2 >>= 1);
426                 }
427                 *outheight = max(1, height2);
428         }
429         if (outdepth)
430         {
431                 if (vid.support.arb_texture_non_power_of_two)
432                         depth2 = min(indepth >> picmip, maxsize);
433                 else
434                 {
435                         for (depth2 = 1;depth2 < indepth;depth2 <<= 1);
436                         for (depth2 >>= picmip;depth2 > maxsize;depth2 >>= 1);
437                 }
438                 *outdepth = max(1, depth2);
439         }
440 }
441
442
443 static int R_CalcTexelDataSize (gltexture_t *glt)
444 {
445         int width2, height2, depth2, size;
446
447         GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2);
448
449         size = width2 * height2 * depth2;
450
451         if (glt->flags & TEXF_MIPMAP)
452         {
453                 while (width2 > 1 || height2 > 1 || depth2 > 1)
454                 {
455                         if (width2 > 1)
456                                 width2 >>= 1;
457                         if (height2 > 1)
458                                 height2 >>= 1;
459                         if (depth2 > 1)
460                                 depth2 >>= 1;
461                         size += width2 * height2 * depth2;
462                 }
463         }
464
465         return (int)(size * glt->textype->glinternalbytesperpixel) * glt->sides;
466 }
467
468 void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean printtotal)
469 {
470         int glsize;
471         int isloaded;
472         int pooltotal = 0, pooltotalt = 0, pooltotalp = 0, poolloaded = 0, poolloadedt = 0, poolloadedp = 0;
473         int sumtotal = 0, sumtotalt = 0, sumtotalp = 0, sumloaded = 0, sumloadedt = 0, sumloadedp = 0;
474         gltexture_t *glt;
475         gltexturepool_t *pool;
476         if (printeach)
477                 Con_Print("glsize input loaded mip alpha name\n");
478         for (pool = gltexturepoolchain;pool;pool = pool->next)
479         {
480                 pooltotal = 0;
481                 pooltotalt = 0;
482                 pooltotalp = 0;
483                 poolloaded = 0;
484                 poolloadedt = 0;
485                 poolloadedp = 0;
486                 for (glt = pool->gltchain;glt;glt = glt->chain)
487                 {
488                         glsize = R_CalcTexelDataSize(glt);
489                         isloaded = glt->texnum != 0;
490                         pooltotal++;
491                         pooltotalt += glsize;
492                         pooltotalp += glt->inputdatasize;
493                         if (isloaded)
494                         {
495                                 poolloaded++;
496                                 poolloadedt += glsize;
497                                 poolloadedp += glt->inputdatasize;
498                         }
499                         if (printeach)
500                                 Con_Printf("%c%4i%c%c%4i%c %s %s %s %s\n", isloaded ? '[' : ' ', (glsize + 1023) / 1024, isloaded ? ']' : ' ', glt->inputtexels ? '[' : ' ', (glt->inputdatasize + 1023) / 1024, glt->inputtexels ? ']' : ' ', isloaded ? "loaded" : "      ", (glt->flags & TEXF_MIPMAP) ? "mip" : "   ", (glt->flags & TEXF_ALPHA) ? "alpha" : "     ", glt->identifier);
501                 }
502                 if (printpool)
503                         Con_Printf("texturepool %10p total: %i (%.3fMB, %.3fMB original), uploaded %i (%.3fMB, %.3fMB original), upload on demand %i (%.3fMB, %.3fMB original)\n", (void *)pool, pooltotal, pooltotalt / 1048576.0, pooltotalp / 1048576.0, poolloaded, poolloadedt / 1048576.0, poolloadedp / 1048576.0, pooltotal - poolloaded, (pooltotalt - poolloadedt) / 1048576.0, (pooltotalp - poolloadedp) / 1048576.0);
504                 sumtotal += pooltotal;
505                 sumtotalt += pooltotalt;
506                 sumtotalp += pooltotalp;
507                 sumloaded += poolloaded;
508                 sumloadedt += poolloadedt;
509                 sumloadedp += poolloadedp;
510         }
511         if (printtotal)
512                 Con_Printf("textures total: %i (%.3fMB, %.3fMB original), uploaded %i (%.3fMB, %.3fMB original), upload on demand %i (%.3fMB, %.3fMB original)\n", sumtotal, sumtotalt / 1048576.0, sumtotalp / 1048576.0, sumloaded, sumloadedt / 1048576.0, sumloadedp / 1048576.0, sumtotal - sumloaded, (sumtotalt - sumloadedt) / 1048576.0, (sumtotalp - sumloadedp) / 1048576.0);
513 }
514
515 static void R_TextureStats_f(void)
516 {
517         R_TextureStats_Print(true, true, true);
518 }
519
520 static void r_textures_start(void)
521 {
522         // LordHavoc: allow any alignment
523         CHECKGLERROR
524         qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);CHECKGLERROR
525         qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
526
527         texturemempool = Mem_AllocPool("texture management", 0, NULL);
528         Mem_ExpandableArray_NewArray(&texturearray, texturemempool, sizeof(gltexture_t), 512);
529
530         // Disable JPEG screenshots if the DLL isn't loaded
531         if (! JPEG_OpenLibrary ())
532                 Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
533         // TODO: support png screenshots?
534         PNG_OpenLibrary ();
535 }
536
537 static void r_textures_shutdown(void)
538 {
539         rtexturepool_t *temp;
540
541         JPEG_CloseLibrary ();
542
543         while(gltexturepoolchain)
544         {
545                 temp = (rtexturepool_t *) gltexturepoolchain;
546                 R_FreeTexturePool(&temp);
547         }
548
549         resizebuffersize = 0;
550         texturebuffersize = 0;
551         resizebuffer = NULL;
552         colorconvertbuffer = NULL;
553         texturebuffer = NULL;
554         Mem_ExpandableArray_FreeArray(&texturearray);
555         Mem_FreePool(&texturemempool);
556 }
557
558 static void r_textures_newmap(void)
559 {
560 }
561
562 void R_Textures_Init (void)
563 {
564         Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc)");
565         Cmd_AddCommand("r_texturestats", R_TextureStats_f, "print information about all loaded textures and some statistics");
566         Cvar_RegisterVariable (&gl_max_size);
567         Cvar_RegisterVariable (&gl_picmip);
568         Cvar_RegisterVariable (&gl_max_lightmapsize);
569         Cvar_RegisterVariable (&r_lerpimages);
570         Cvar_RegisterVariable (&gl_texture_anisotropy);
571         Cvar_RegisterVariable (&gl_texturecompression);
572         Cvar_RegisterVariable (&gl_texturecompression_color);
573         Cvar_RegisterVariable (&gl_texturecompression_normal);
574         Cvar_RegisterVariable (&gl_texturecompression_gloss);
575         Cvar_RegisterVariable (&gl_texturecompression_glow);
576         Cvar_RegisterVariable (&gl_texturecompression_2d);
577         Cvar_RegisterVariable (&gl_texturecompression_q3bsplightmaps);
578         Cvar_RegisterVariable (&gl_texturecompression_q3bspdeluxemaps);
579         Cvar_RegisterVariable (&gl_texturecompression_sky);
580         Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
581         Cvar_RegisterVariable (&gl_texturecompression_reflectmask);
582         Cvar_RegisterVariable (&gl_nopartialtextureupdates);
583
584         R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
585 }
586
587 void R_Textures_Frame (void)
588 {
589         static int old_aniso = 0;
590
591         // could do procedural texture animation here, if we keep track of which
592         // textures were accessed this frame...
593
594         // free the resize buffers
595         resizebuffersize = 0;
596         if (resizebuffer)
597         {
598                 Mem_Free(resizebuffer);
599                 resizebuffer = NULL;
600         }
601         if (colorconvertbuffer)
602         {
603                 Mem_Free(colorconvertbuffer);
604                 colorconvertbuffer = NULL;
605         }
606
607         if (old_aniso != gl_texture_anisotropy.integer)
608         {
609                 gltexture_t *glt;
610                 gltexturepool_t *pool;
611                 GLint oldbindtexnum;
612
613                 old_aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
614
615                 Cvar_SetValueQuick(&gl_texture_anisotropy, old_aniso);
616
617                 CHECKGLERROR
618                 GL_ActiveTexture(0);
619                 for (pool = gltexturepoolchain;pool;pool = pool->next)
620                 {
621                         for (glt = pool->gltchain;glt;glt = glt->chain)
622                         {
623                                 // only update already uploaded images
624                                 if (glt->texnum && (glt->flags & TEXF_MIPMAP) == TEXF_MIPMAP)
625                                 {
626                                         oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
627
628                                         qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
629                                         qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
630
631                                         qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
632                                 }
633                         }
634                 }
635         }
636 }
637
638 void R_MakeResizeBufferBigger(int size)
639 {
640         if (resizebuffersize < size)
641         {
642                 resizebuffersize = size;
643                 if (resizebuffer)
644                         Mem_Free(resizebuffer);
645                 if (colorconvertbuffer)
646                         Mem_Free(colorconvertbuffer);
647                 resizebuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
648                 colorconvertbuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
649                 if (!resizebuffer || !colorconvertbuffer)
650                         Host_Error("R_Upload: out of memory");
651         }
652 }
653
654 static void GL_SetupTextureParameters(int flags, textype_t textype, int texturetype)
655 {
656         int textureenum = gltexturetypeenums[texturetype];
657         int wrapmode = (flags & TEXF_CLAMP) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
658
659         CHECKGLERROR
660
661         if (vid.support.ext_texture_filter_anisotropic && (flags & TEXF_MIPMAP))
662         {
663                 int aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
664                 if (gl_texture_anisotropy.integer != aniso)
665                         Cvar_SetValueQuick(&gl_texture_anisotropy, aniso);
666                 qglTexParameteri(textureenum, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);CHECKGLERROR
667         }
668         qglTexParameteri(textureenum, GL_TEXTURE_WRAP_S, wrapmode);CHECKGLERROR
669         qglTexParameteri(textureenum, GL_TEXTURE_WRAP_T, wrapmode);CHECKGLERROR
670         if (gltexturetypedimensions[texturetype] >= 3)
671         {
672                 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_R, wrapmode);CHECKGLERROR
673         }
674
675         CHECKGLERROR
676         if (flags & TEXF_FORCENEAREST)
677         {
678                 if (flags & TEXF_MIPMAP)
679                 {
680                         qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);CHECKGLERROR
681                 }
682                 else
683                 {
684                         qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST);CHECKGLERROR
685                 }
686                 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_NEAREST);CHECKGLERROR
687         }
688         else if (flags & TEXF_FORCELINEAR)
689         {
690                 if (flags & TEXF_MIPMAP)
691                 {
692                         if (gl_filter_min == GL_NEAREST_MIPMAP_LINEAR || gl_filter_min == GL_LINEAR_MIPMAP_LINEAR)
693                         {
694                                 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);CHECKGLERROR
695                         }
696                         else
697                         {
698                                 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);CHECKGLERROR
699                         }
700                 }
701                 else
702                 {
703                         qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR);CHECKGLERROR
704                 }
705                 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_LINEAR);CHECKGLERROR
706         }
707         else
708         {
709                 if (flags & TEXF_MIPMAP)
710                 {
711                         qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
712                 }
713                 else
714                 {
715                         qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
716                 }
717                 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
718         }
719
720         if (textype == TEXTYPE_SHADOWMAP)
721         {
722                 if (vid.support.arb_shadow)
723                 {
724                         if (flags & TEXF_COMPARE)
725                         {
726                                 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);CHECKGLERROR
727                         }
728                         else
729                         {
730                                 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);CHECKGLERROR
731                         }
732                         qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);CHECKGLERROR
733                 }
734                 qglTexParameteri(textureenum, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);CHECKGLERROR
735         }
736
737         CHECKGLERROR
738 }
739
740 static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
741 {
742         int i, mip, width, height, depth;
743         GLint oldbindtexnum;
744         const unsigned char *prevbuffer;
745         prevbuffer = data;
746
747         CHECKGLERROR
748
749         // we need to restore the texture binding after finishing the upload
750         GL_ActiveTexture(0);
751         oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
752         qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
753
754         // these are rounded up versions of the size to do better resampling
755         if (vid.support.arb_texture_non_power_of_two || glt->texturetype == GLTEXTURETYPE_RECTANGLE)
756         {
757                 width = glt->inputwidth;
758                 height = glt->inputheight;
759                 depth = glt->inputdepth;
760         }
761         else
762         {
763                 for (width  = 1;width  < glt->inputwidth ;width  <<= 1);
764                 for (height = 1;height < glt->inputheight;height <<= 1);
765                 for (depth  = 1;depth  < glt->inputdepth ;depth  <<= 1);
766         }
767
768         R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
769         R_MakeResizeBufferBigger(fragwidth * fragheight * fragdepth * glt->sides * glt->bytesperpixel);
770
771         if (prevbuffer == NULL)
772         {
773                 memset(resizebuffer, 0, fragwidth * fragheight * fragdepth * glt->bytesperpixel);
774                 prevbuffer = resizebuffer;
775         }
776         else if (glt->textype->textype == TEXTYPE_PALETTE)
777         {
778                 // promote paletted to BGRA, so we only have to worry about BGRA in the rest of this code
779                 Image_Copy8bitBGRA(prevbuffer, colorconvertbuffer, fragwidth * fragheight * fragdepth * glt->sides, glt->palette);
780                 prevbuffer = colorconvertbuffer;
781         }
782
783         // upload the image - preferring to do only complete uploads (drivers do not really like partial updates)
784
785         if ((glt->flags & (TEXF_MIPMAP | TEXF_PICMIP)) == 0 && glt->inputwidth == glt->tilewidth && glt->inputheight == glt->tileheight && glt->inputdepth == glt->tiledepth && (fragx != 0 || fragy != 0 || fragwidth != glt->tilewidth || fragheight != glt->tileheight))
786         {
787                 // update a portion of the image
788                 switch(glt->texturetype)
789                 {
790                 case GLTEXTURETYPE_2D:
791                         qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
792                         break;
793                 case GLTEXTURETYPE_3D:
794                         qglTexSubImage3D(GL_TEXTURE_3D, 0, fragx, fragy, fragz, fragwidth, fragheight, fragdepth, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
795                         break;
796                 default:
797                         Host_Error("R_Upload: partial update of type other than 2D");
798                         break;
799                 }
800         }
801         else
802         {
803                 if (fragx || fragy || fragz || glt->inputwidth != fragwidth || glt->inputheight != fragheight || glt->inputdepth != fragdepth)
804                         Host_Error("R_Upload: partial update not allowed on initial upload or in combination with PICMIP or MIPMAP\n");
805
806                 // cubemaps contain multiple images and thus get processed a bit differently
807                 if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
808                 {
809                         if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
810                         {
811                                 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
812                                 prevbuffer = resizebuffer;
813                         }
814                         // picmip/max_size
815                         while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
816                         {
817                                 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
818                                 prevbuffer = resizebuffer;
819                         }
820                 }
821                 mip = 0;
822                 if (qglGetCompressedTexImageARB)
823                 {
824                         if (gl_texturecompression.integer >= 2)
825                                 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
826                         else
827                                 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_FASTEST);
828                         CHECKGLERROR
829                 }
830                 switch(glt->texturetype)
831                 {
832                 case GLTEXTURETYPE_2D:
833                         qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
834                         if (glt->flags & TEXF_MIPMAP)
835                         {
836                                 while (width > 1 || height > 1 || depth > 1)
837                                 {
838                                         Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
839                                         prevbuffer = resizebuffer;
840                                         qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
841                                 }
842                         }
843                         break;
844                 case GLTEXTURETYPE_3D:
845                         qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
846                         if (glt->flags & TEXF_MIPMAP)
847                         {
848                                 while (width > 1 || height > 1 || depth > 1)
849                                 {
850                                         Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
851                                         prevbuffer = resizebuffer;
852                                         qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
853                                 }
854                         }
855                         break;
856                 case GLTEXTURETYPE_CUBEMAP:
857                         // convert and upload each side in turn,
858                         // from a continuous block of input texels
859                         texturebuffer = (unsigned char *)prevbuffer;
860                         for (i = 0;i < 6;i++)
861                         {
862                                 prevbuffer = texturebuffer;
863                                 texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
864                                 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
865                                 {
866                                         Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
867                                         prevbuffer = resizebuffer;
868                                 }
869                                 // picmip/max_size
870                                 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
871                                 {
872                                         Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
873                                         prevbuffer = resizebuffer;
874                                 }
875                                 mip = 0;
876                                 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
877                                 if (glt->flags & TEXF_MIPMAP)
878                                 {
879                                         while (width > 1 || height > 1 || depth > 1)
880                                         {
881                                                 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
882                                                 prevbuffer = resizebuffer;
883                                                 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
884                                         }
885                                 }
886                         }
887                         break;
888                 case GLTEXTURETYPE_RECTANGLE:
889                         qglTexImage2D(GL_TEXTURE_RECTANGLE_ARB, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, NULL);CHECKGLERROR
890                         break;
891                 }
892                 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
893         }
894         qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
895 }
896
897 static rtexture_t *R_SetupTexture(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int depth, int sides, int flags, textype_t textype, int texturetype, const unsigned char *data, const unsigned int *palette)
898 {
899         int i, size;
900         gltexture_t *glt;
901         gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
902         textypeinfo_t *texinfo, *texinfo2;
903
904         if (cls.state == ca_dedicated)
905                 return NULL;
906
907         if (texturetype == GLTEXTURETYPE_RECTANGLE && !vid.support.arb_texture_rectangle)
908         {
909                 Con_Printf ("R_LoadTexture: rectangle texture not supported by driver\n");
910                 return NULL;
911         }
912         if (texturetype == GLTEXTURETYPE_CUBEMAP && !vid.support.arb_texture_cube_map)
913         {
914                 Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
915                 return NULL;
916         }
917         if (texturetype == GLTEXTURETYPE_3D && !vid.support.ext_texture_3d)
918         {
919                 Con_Printf ("R_LoadTexture: 3d texture not supported by driver\n");
920                 return NULL;
921         }
922
923         texinfo = R_GetTexTypeInfo(textype, flags);
924         size = width * height * depth * sides * texinfo->inputbytesperpixel;
925         if (size < 1)
926         {
927                 Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides, size);
928                 return NULL;
929         }
930
931         // clear the alpha flag if the texture has no transparent pixels
932         switch(textype)
933         {
934         case TEXTYPE_PALETTE:
935                 if (flags & TEXF_ALPHA)
936                 {
937                         flags &= ~TEXF_ALPHA;
938                         if (data)
939                         {
940                                 for (i = 0;i < size;i++)
941                                 {
942                                         if (((unsigned char *)&palette[data[i]])[3] < 255)
943                                         {
944                                                 flags |= TEXF_ALPHA;
945                                                 break;
946                                         }
947                                 }
948                         }
949                 }
950                 break;
951         case TEXTYPE_RGBA:
952         case TEXTYPE_BGRA:
953                 if (flags & TEXF_ALPHA)
954                 {
955                         flags &= ~TEXF_ALPHA;
956                         if (data)
957                         {
958                                 for (i = 3;i < size;i += 4)
959                                 {
960                                         if (data[i] < 255)
961                                         {
962                                                 flags |= TEXF_ALPHA;
963                                                 break;
964                                         }
965                                 }
966                         }
967                 }
968                 break;
969         case TEXTYPE_SHADOWMAP:
970                 break;
971         case TEXTYPE_DXT1:
972                 break;
973         case TEXTYPE_DXT1A:
974         case TEXTYPE_DXT3:
975         case TEXTYPE_DXT5:
976                 flags |= TEXF_ALPHA;
977                 break;
978         case TEXTYPE_ALPHA:
979                 flags |= TEXF_ALPHA;
980                 break;
981         case TEXTYPE_COLORBUFFER:
982                 flags |= TEXF_ALPHA;
983                 break;
984         default:
985                 Host_Error("R_LoadTexture: unknown texture type");
986         }
987
988         texinfo2 = R_GetTexTypeInfo(textype, flags);
989         if(size == width * height * depth * sides * texinfo->inputbytesperpixel)
990                 texinfo = texinfo2;
991         else
992                 Con_Printf ("R_LoadTexture: input size changed after alpha fallback\n");
993
994         glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
995         if (identifier)
996                 strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
997         glt->pool = pool;
998         glt->chain = pool->gltchain;
999         pool->gltchain = glt;
1000         glt->inputwidth = width;
1001         glt->inputheight = height;
1002         glt->inputdepth = depth;
1003         glt->flags = flags;
1004         glt->textype = texinfo;
1005         glt->texturetype = texturetype;
1006         glt->inputdatasize = size;
1007         glt->palette = palette;
1008         glt->glinternalformat = texinfo->glinternalformat;
1009         glt->glformat = texinfo->glformat;
1010         glt->gltype = texinfo->gltype;
1011         glt->bytesperpixel = texinfo->internalbytesperpixel;
1012         glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
1013         glt->texnum = 0;
1014         glt->dirty = false;
1015         glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1016         // init the dynamic texture attributes, too [11/22/2007 Black]
1017         glt->updatecallback = NULL;
1018         glt->updatacallback_data = NULL;
1019
1020         GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
1021
1022         // upload the texture
1023         // data may be NULL (blank texture for dynamic rendering)
1024         CHECKGLERROR
1025         qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1026         R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
1027         if ((glt->flags & TEXF_ALLOWUPDATES) && gl_nopartialtextureupdates.integer)
1028                 glt->bufferpixels = Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
1029
1030         // texture converting and uploading can take a while, so make sure we're sending keepalives
1031         CL_KeepaliveMessage(false);
1032
1033         return (rtexture_t *)glt;
1034 }
1035
1036 rtexture_t *R_LoadTexture2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, const unsigned char *data, textype_t textype, int flags, const unsigned int *palette)
1037 {
1038         return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_2D, data, palette);
1039 }
1040
1041 rtexture_t *R_LoadTexture3D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int depth, const unsigned char *data, textype_t textype, int flags, const unsigned int *palette)
1042 {
1043         return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, textype, GLTEXTURETYPE_3D, data, palette);
1044 }
1045
1046 rtexture_t *R_LoadTextureCubeMap(rtexturepool_t *rtexturepool, const char *identifier, int width, const unsigned char *data, textype_t textype, int flags, const unsigned int *palette)
1047 {
1048         return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
1049 }
1050
1051 rtexture_t *R_LoadTextureRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, const unsigned char *data, textype_t textype, int flags, const unsigned int *palette)
1052 {
1053         return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_RECTANGLE, data, palette);
1054 }
1055
1056 static int R_ShadowMapTextureFlags(int precision, qboolean filter)
1057 {
1058         int flags = TEXF_CLAMP;
1059         if (filter)
1060                 flags |= TEXF_FORCELINEAR | TEXF_COMPARE;
1061         else
1062                 flags |= TEXF_FORCENEAREST;
1063         if (precision <= 16)
1064                 flags |= TEXF_LOWPRECISION;
1065         return flags;
1066 }
1067
1068 rtexture_t *R_LoadTextureShadowMapRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1069 {
1070         return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_RECTANGLE, NULL, NULL);
1071 }
1072
1073 rtexture_t *R_LoadTextureShadowMap2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1074 {
1075         return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_2D, NULL, NULL);
1076 }
1077
1078 rtexture_t *R_LoadTextureShadowMapCube(rtexturepool_t *rtexturepool, const char *identifier, int width, int precision, qboolean filter)
1079 {
1080     return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_CUBEMAP, NULL, NULL);
1081 }
1082
1083 int R_SaveTextureDDSFile(rtexture_t *rt, const char *filename, qboolean skipuncompressed)
1084 {
1085         gltexture_t *glt = (gltexture_t *)rt;
1086         unsigned char *dds;
1087         int oldbindtexnum;
1088         int bytesperpixel = 0;
1089         int bytesperblock = 0;
1090         int dds_flags;
1091         int dds_format_flags;
1092         int dds_caps1;
1093         int dds_caps2;
1094         int ret;
1095         int mip;
1096         int mipmaps;
1097         int mipinfo[16][4];
1098         int ddssize = 128;
1099         GLint internalformat;
1100         const char *ddsfourcc;
1101         if (!rt)
1102                 return -1; // NULL pointer
1103         if (!strcmp(gl_version, "2.0.5885 WinXP Release"))
1104                 return -2; // broken driver - crashes on reading internal format
1105         if (!qglGetTexLevelParameteriv)
1106                 return -2;
1107         GL_ActiveTexture(0);
1108         oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
1109         qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
1110         qglGetTexLevelParameteriv(gltexturetypeenums[glt->texturetype], 0, GL_TEXTURE_INTERNAL_FORMAT, &internalformat);
1111         switch(internalformat)
1112         {
1113         default: ddsfourcc = NULL;bytesperpixel = 4;break;
1114         case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
1115         case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: ddsfourcc = "DXT1";bytesperblock = 8;break;
1116         case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: ddsfourcc = "DXT3";bytesperblock = 16;break;
1117         case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: ddsfourcc = "DXT5";bytesperblock = 16;break;
1118         }
1119         if (!bytesperblock && skipuncompressed)
1120                 return -3; // skipped
1121         memset(mipinfo, 0, sizeof(mipinfo));
1122         mipinfo[0][0] = glt->tilewidth;
1123         mipinfo[0][1] = glt->tileheight;
1124         mipmaps = 1;
1125         if (glt->flags & TEXF_MIPMAP)
1126         {
1127                 for (mip = 1;mip < 16;mip++)
1128                 {
1129                         mipinfo[mip][0] = mipinfo[mip-1][0] > 1 ? mipinfo[mip-1][0] >> 1 : 1;
1130                         mipinfo[mip][1] = mipinfo[mip-1][1] > 1 ? mipinfo[mip-1][1] >> 1 : 1;
1131                         if (mipinfo[mip][0] == 1 && mipinfo[mip][1] == 1)
1132                         {
1133                                 mip++;
1134                                 break;
1135                         }
1136                 }
1137                 mipmaps = mip;
1138         }
1139         for (mip = 0;mip < mipmaps;mip++)
1140         {
1141                 mipinfo[mip][2] = bytesperblock ? ((mipinfo[mip][0]+3)/4)*((mipinfo[mip][1]+3)/4)*bytesperblock : mipinfo[mip][0]*mipinfo[mip][1]*bytesperpixel;
1142                 mipinfo[mip][3] = ddssize;
1143                 ddssize += mipinfo[mip][2];
1144         }
1145         dds = Mem_Alloc(tempmempool, ddssize);
1146         if (!dds)
1147                 return -4;
1148         dds_caps1 = 0x1000; // DDSCAPS_TEXTURE
1149         dds_caps2 = 0;
1150         if (bytesperblock)
1151         {
1152                 dds_flags = 0x81007; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_LINEARSIZE
1153                 dds_format_flags = 0x4; // DDPF_FOURCC
1154         }
1155         else
1156         {
1157                 dds_flags = 0x100F; // DDSD_CAPS | DDSD_PIXELFORMAT | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH
1158                 dds_format_flags = 0x41; // DDPF_RGB | DDPF_ALPHAPIXELS
1159         }
1160         if (mipmaps)
1161         {
1162                 dds_flags |= 0x20000; // DDSD_MIPMAPCOUNT
1163                 dds_caps1 |= 0x400008; // DDSCAPS_MIPMAP | DDSCAPS_COMPLEX
1164         }
1165         memcpy(dds, "DDS ", 4);
1166         StoreLittleLong(dds+4, ddssize);
1167         StoreLittleLong(dds+8, dds_flags);
1168         StoreLittleLong(dds+12, mipinfo[0][1]); // height
1169         StoreLittleLong(dds+16, mipinfo[0][0]); // width
1170         StoreLittleLong(dds+24, 1); // depth
1171         StoreLittleLong(dds+28, mipmaps); // mipmaps
1172         StoreLittleLong(dds+76, 32); // format size
1173         StoreLittleLong(dds+80, dds_format_flags);
1174         StoreLittleLong(dds+108, dds_caps1);
1175         StoreLittleLong(dds+112, dds_caps2);
1176         if (bytesperblock)
1177         {
1178                 StoreLittleLong(dds+20, mipinfo[0][2]); // linear size
1179                 memcpy(dds+84, ddsfourcc, 4);
1180                 for (mip = 0;mip < mipmaps;mip++)
1181                 {
1182                         qglGetCompressedTexImageARB(gltexturetypeenums[glt->texturetype], mip, dds + mipinfo[mip][3]);CHECKGLERROR
1183                 }
1184         }
1185         else
1186         {
1187                 StoreLittleLong(dds+20, mipinfo[0][0]*bytesperpixel); // pitch
1188                 StoreLittleLong(dds+88, bytesperpixel*8); // bits per pixel
1189                 dds[94] = dds[97] = dds[100] = dds[107] = 255; // bgra byte order masks
1190                 for (mip = 0;mip < mipmaps;mip++)
1191                 {
1192                         qglGetTexImage(gltexturetypeenums[glt->texturetype], mip, GL_BGRA, GL_UNSIGNED_BYTE, dds + mipinfo[mip][3]);CHECKGLERROR
1193                 }
1194         }
1195         qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
1196         ret = FS_WriteFile(filename, dds, ddssize);
1197         Mem_Free(dds);
1198         return ret ? ddssize : -5;
1199 }
1200
1201 rtexture_t *R_LoadTextureDDSFile(rtexturepool_t *rtexturepool, const char *filename, int flags, qboolean *hasalphaflag, float *avgcolor)
1202 {
1203         int i, size, dds_flags, dds_format_flags, dds_miplevels, dds_width, dds_height, textype;
1204         int bytesperblock, bytesperpixel;
1205         int mipcomplete;
1206         gltexture_t *glt;
1207         gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
1208         textypeinfo_t *texinfo;
1209         int mip, mipwidth, mipheight, mipsize;
1210         unsigned int c;
1211         GLint oldbindtexnum;
1212         const unsigned char *mippixels, *ddspixels;
1213         unsigned char *dds;
1214         fs_offset_t ddsfilesize;
1215         unsigned int ddssize;
1216
1217         if (cls.state == ca_dedicated)
1218                 return NULL;
1219
1220         dds = FS_LoadFile(filename, tempmempool, true, &ddsfilesize);
1221         ddssize = ddsfilesize;
1222
1223         if (!dds)
1224         {
1225                 Log_Printf("ddstexturefailures.log", "%s\n", filename);
1226                 return NULL; // not found
1227         }
1228
1229         if (ddsfilesize <= 128 || memcmp(dds, "DDS ", 4) || ddssize < (unsigned int)BuffLittleLong(dds+4) || BuffLittleLong(dds+76) != 32)
1230         {
1231                 Mem_Free(dds);
1232                 Con_Printf("^1%s: not a DDS image\n", filename);
1233                 return NULL;
1234         }
1235
1236         dds_flags = BuffLittleLong(dds+8);
1237         dds_format_flags = BuffLittleLong(dds+80);
1238         dds_miplevels = (BuffLittleLong(dds+108) & 0x400000) ? BuffLittleLong(dds+28) : 1;
1239         dds_width = BuffLittleLong(dds+16);
1240         dds_height = BuffLittleLong(dds+12);
1241         ddspixels = dds + 128;
1242
1243         flags &= ~TEXF_ALPHA;
1244         if ((dds_format_flags & 0x40) && BuffLittleLong(dds+88) == 32)
1245         {
1246                 // very sloppy BGRA 32bit identification
1247                 textype = TEXTYPE_BGRA;
1248                 bytesperblock = 0;
1249                 bytesperpixel = 4;
1250                 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(dds_width, dds_height), bytesperpixel);
1251                 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1252                 {
1253                         Mem_Free(dds);
1254                         Con_Printf("^1%s: invalid BGRA DDS image\n", filename);
1255                         return NULL;
1256                 }
1257                 // check alpha
1258                 for (i = 3;i < size;i += 4)
1259                         if (ddspixels[i] < 255)
1260                                 break;
1261                 if (i >= size)
1262                         flags &= ~TEXF_ALPHA;
1263         }
1264         else if (!memcmp(dds+84, "DXT1", 4))
1265         {
1266                 // we need to find out if this is DXT1 (opaque) or DXT1A (transparent)
1267                 // LordHavoc: it is my belief that this does not infringe on the
1268                 // patent because it is not decoding pixels...
1269                 textype = TEXTYPE_DXT1;
1270                 bytesperblock = 8;
1271                 bytesperpixel = 0;
1272                 //size = ((dds_width+3)/4)*((dds_height+3)/4)*bytesperblock;
1273                 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1274                 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1275                 {
1276                         Mem_Free(dds);
1277                         Con_Printf("^1%s: invalid DXT1 DDS image\n", filename);
1278                         return NULL;
1279                 }
1280                 for (i = 0;i < size;i += bytesperblock)
1281                         if (ddspixels[i+0] + ddspixels[i+1] * 256 <= ddspixels[i+2] + ddspixels[i+3] * 256)
1282                                 break;
1283                 if (i < size)
1284                         textype = TEXTYPE_DXT1A;
1285                 else
1286                         flags &= ~TEXF_ALPHA;
1287         }
1288         else if (!memcmp(dds+84, "DXT3", 4))
1289         {
1290                 textype = TEXTYPE_DXT3;
1291                 bytesperblock = 16;
1292                 bytesperpixel = 0;
1293                 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1294                 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1295                 {
1296                         Mem_Free(dds);
1297                         Con_Printf("^1%s: invalid DXT3 DDS image\n", filename);
1298                         return NULL;
1299                 }
1300         }
1301         else if (!memcmp(dds+84, "DXT5", 4))
1302         {
1303                 textype = TEXTYPE_DXT5;
1304                 bytesperblock = 16;
1305                 bytesperpixel = 0;
1306                 size = INTOVERFLOW_MUL(INTOVERFLOW_MUL(INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_width, 3), 4), INTOVERFLOW_DIV(INTOVERFLOW_ADD(dds_height, 3), 4)), bytesperblock);
1307                 if(INTOVERFLOW_ADD(128, size) > INTOVERFLOW_NORMALIZE(ddsfilesize))
1308                 {
1309                         Mem_Free(dds);
1310                         Con_Printf("^1%s: invalid DXT5 DDS image\n", filename);
1311                         return NULL;
1312                 }
1313         }
1314         else
1315         {
1316                 Mem_Free(dds);
1317                 Con_Printf("^1%s: unrecognized/unsupported DDS format\n", filename);
1318                 return NULL;
1319         }
1320
1321         // return whether this texture is transparent
1322         if (hasalphaflag)
1323                 *hasalphaflag = (flags & TEXF_ALPHA) != 0;
1324
1325         // calculate average color if requested
1326         if (avgcolor)
1327         {
1328                 float f;
1329                 Vector4Clear(avgcolor);
1330                 if (bytesperblock)
1331                 {
1332                         for (i = bytesperblock == 16 ? 8 : 0;i < size;i += bytesperblock)
1333                         {
1334                                 c = ddspixels[i] + 256*ddspixels[i+1] + 65536*ddspixels[i+2] + 16777216*ddspixels[i+3];
1335                                 avgcolor[0] += ((c >> 11) & 0x1F) + ((c >> 27) & 0x1F);
1336                                 avgcolor[1] += ((c >>  5) & 0x3F) + ((c >> 21) & 0x3F);
1337                                 avgcolor[2] += ((c      ) & 0x1F) + ((c >> 16) & 0x1F);
1338                         }
1339                         f = (float)bytesperblock / size;
1340                         avgcolor[0] *= (0.5f / 31.0f) * f;
1341                         avgcolor[1] *= (0.5f / 63.0f) * f;
1342                         avgcolor[2] *= (0.5f / 31.0f) * f;
1343                         avgcolor[3] = 1; // too hard to calculate
1344                 }
1345                 else
1346                 {
1347                         for (i = 0;i < size;i += 4)
1348                         {
1349                                 avgcolor[0] += ddspixels[i+2];
1350                                 avgcolor[1] += ddspixels[i+1];
1351                                 avgcolor[2] += ddspixels[i];
1352                                 avgcolor[3] += ddspixels[i+3];
1353                         }
1354                         f = (1.0f / 255.0f) * bytesperpixel / size;
1355                         avgcolor[0] *= f;
1356                         avgcolor[1] *= f;
1357                         avgcolor[2] *= f;
1358                         avgcolor[3] *= f;
1359                 }
1360         }
1361
1362         if (dds_miplevels > 1)
1363                 flags |= TEXF_MIPMAP;
1364         else
1365                 flags &= ~TEXF_MIPMAP;
1366
1367         // if S3TC is not supported, there's very little we can do about it
1368         if (bytesperblock && !vid.support.ext_texture_compression_s3tc)
1369         {
1370                 Mem_Free(dds);
1371                 Con_Printf("^1%s: DDS file is compressed but OpenGL driver does not support S3TC\n", filename);
1372                 return NULL;
1373         }
1374
1375         texinfo = R_GetTexTypeInfo(textype, flags);
1376
1377         glt = (gltexture_t *)Mem_ExpandableArray_AllocRecord(&texturearray);
1378         strlcpy (glt->identifier, filename, sizeof(glt->identifier));
1379         glt->pool = pool;
1380         glt->chain = pool->gltchain;
1381         pool->gltchain = glt;
1382         glt->inputwidth = dds_width;
1383         glt->inputheight = dds_height;
1384         glt->inputdepth = 1;
1385         glt->flags = flags;
1386         glt->textype = texinfo;
1387         glt->texturetype = GLTEXTURETYPE_2D;
1388         glt->inputdatasize = ddssize;
1389         glt->glinternalformat = texinfo->glinternalformat;
1390         glt->glformat = texinfo->glformat;
1391         glt->gltype = texinfo->gltype;
1392         glt->bytesperpixel = texinfo->internalbytesperpixel;
1393         glt->sides = 1;
1394         glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1395         glt->tilewidth = dds_width;
1396         glt->tileheight = dds_height;
1397         glt->tiledepth = 1;
1398
1399         // texture uploading can take a while, so make sure we're sending keepalives
1400         CL_KeepaliveMessage(false);
1401
1402         // upload the texture
1403         // we need to restore the texture binding after finishing the upload
1404         CHECKGLERROR
1405         GL_ActiveTexture(0);
1406         oldbindtexnum = R_Mesh_TexBound(0, gltexturetypeenums[glt->texturetype]);
1407         qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1408         qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
1409         mippixels = ddspixels;
1410         mipwidth = dds_width;
1411         mipheight = dds_height;
1412         mipcomplete = false;
1413         for (mip = 0;mip < dds_miplevels+1;mip++)
1414         {
1415                 mipsize = bytesperblock ? ((mipwidth+3)/4)*((mipheight+3)/4)*bytesperblock : mipwidth*mipheight*bytesperpixel;
1416                 if (mippixels + mipsize > dds + ddssize)
1417                         break;
1418                 if (bytesperblock)
1419                 {
1420                         qglCompressedTexImage2DARB(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, mipsize, mippixels);CHECKGLERROR
1421                 }
1422                 else
1423                 {
1424                         qglTexImage2D(GL_TEXTURE_2D, mip, glt->glinternalformat, mipwidth, mipheight, 0, glt->glformat, glt->gltype, mippixels);CHECKGLERROR
1425                 }
1426                 mippixels += mipsize;
1427                 if (mipwidth <= 1 && mipheight <= 1)
1428                 {
1429                         mipcomplete = true;
1430                         break;
1431                 }
1432                 if (mipwidth > 1)
1433                         mipwidth >>= 1;
1434                 if (mipheight > 1)
1435                         mipheight >>= 1;
1436         }
1437         if (dds_miplevels > 1 && !mipcomplete)
1438         {
1439                 // need to set GL_TEXTURE_MAX_LEVEL
1440                 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_LEVEL, dds_miplevels - 1);CHECKGLERROR
1441         }
1442         GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
1443         qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
1444
1445         Mem_Free(dds);
1446         return (rtexture_t *)glt;
1447 }
1448
1449 int R_TextureWidth(rtexture_t *rt)
1450 {
1451         return rt ? ((gltexture_t *)rt)->inputwidth : 0;
1452 }
1453
1454 int R_TextureHeight(rtexture_t *rt)
1455 {
1456         return rt ? ((gltexture_t *)rt)->inputheight : 0;
1457 }
1458
1459 void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height)
1460 {
1461         gltexture_t *glt = (gltexture_t *)rt;
1462         if (data == NULL)
1463                 Host_Error("R_UpdateTexture: no data supplied");
1464         if (glt == NULL)
1465                 Host_Error("R_UpdateTexture: no texture supplied");
1466         if (!glt->texnum)
1467                 Host_Error("R_UpdateTexture: texture has not been uploaded yet");
1468         // update part of the texture
1469         if (glt->bufferpixels)
1470         {
1471                 int j;
1472                 int bpp = glt->bytesperpixel;
1473                 int inputskip = width*bpp;
1474                 int outputskip = glt->tilewidth*bpp;
1475                 const unsigned char *input = data;
1476                 unsigned char *output = glt->bufferpixels;
1477                 if (x < 0)
1478                 {
1479                         width += x;
1480                         input -= x*bpp;
1481                         x = 0;
1482                 }
1483                 if (y < 0)
1484                 {
1485                         height += y;
1486                         input -= y*inputskip;
1487                         y = 0;
1488                 }
1489                 if (width > glt->tilewidth - x)
1490                         width = glt->tilewidth - x;
1491                 if (height > glt->tileheight - y)
1492                         height = glt->tileheight - y;
1493                 if (width < 1 || height < 1)
1494                         return;
1495                 glt->dirty = true;
1496                 glt->buffermodified = true;
1497                 output += y*outputskip + x*bpp;
1498                 for (j = 0;j < height;j++, output += outputskip, input += inputskip)
1499                         memcpy(output, input, width*bpp);
1500         }
1501         else
1502                 R_Upload(glt, data, x, y, 0, width, height, 1);
1503 }
1504
1505 int R_RealGetTexture(rtexture_t *rt)
1506 {
1507         if (rt)
1508         {
1509                 gltexture_t *glt;
1510                 glt = (gltexture_t *)rt;
1511                 if (glt->flags & GLTEXF_DYNAMIC)
1512                         R_UpdateDynamicTexture(glt);
1513                 if (glt->buffermodified && glt->bufferpixels)
1514                 {
1515                         glt->buffermodified = false;
1516                         R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
1517                 }
1518                 glt->dirty = false;
1519                 return glt->texnum;
1520         }
1521         else
1522                 return 0;
1523 }
1524
1525 void R_ClearTexture (rtexture_t *rt)
1526 {
1527         gltexture_t *glt = (gltexture_t *)rt;
1528
1529         R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );
1530 }