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