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