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