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