]> icculus.org git repositories - divverent/darkplaces.git/blob - gl_textures.c
fix u8_COM_StringLengthNoColors the same way other functions handle signedness
[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", "0", "use alternate path for dynamic lightmap updates"};
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 // cleared when a texture is uploaded
32 #define GLTEXF_UPLOAD           0x00010000
33 // bitmask for mismatch checking
34 #define GLTEXF_IMPORTANTBITS (0)
35 // set when image is uploaded and freed
36 #define GLTEXF_DESTROYED        0x00040000
37 // dynamic texture (treat texnum == 0 differently)
38 #define GLTEXF_DYNAMIC          0x00080000
39
40 typedef struct textypeinfo_s
41 {
42         textype_t textype;
43         int inputbytesperpixel;
44         int internalbytesperpixel;
45         float glinternalbytesperpixel;
46         int glformat;
47         int glinternalformat;
48         int gltype;
49 }
50 textypeinfo_t;
51
52 static textypeinfo_t textype_palette                = {TEXTYPE_PALETTE, 1, 4, 4.0f, GL_BGRA   , 3, GL_UNSIGNED_BYTE};
53 static textypeinfo_t textype_palette_alpha          = {TEXTYPE_PALETTE, 1, 4, 4.0f, GL_BGRA   , 4, GL_UNSIGNED_BYTE};
54 static textypeinfo_t textype_palette_compress       = {TEXTYPE_PALETTE, 1, 4, 0.5f, GL_BGRA   , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
55 static textypeinfo_t textype_palette_alpha_compress = {TEXTYPE_PALETTE, 1, 4, 1.0f, GL_BGRA   , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
56 static textypeinfo_t textype_rgba                   = {TEXTYPE_RGBA   , 4, 4, 4.0f, GL_RGBA   , 3, GL_UNSIGNED_BYTE};
57 static textypeinfo_t textype_rgba_alpha             = {TEXTYPE_RGBA   , 4, 4, 4.0f, GL_RGBA   , 4, GL_UNSIGNED_BYTE};
58 static textypeinfo_t textype_rgba_compress          = {TEXTYPE_RGBA   , 4, 4, 0.5f, GL_RGBA   , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
59 static textypeinfo_t textype_rgba_alpha_compress    = {TEXTYPE_RGBA   , 4, 4, 1.0f, GL_RGBA   , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
60 static textypeinfo_t textype_bgra                   = {TEXTYPE_BGRA   , 4, 4, 4.0f, GL_BGRA   , 3, GL_UNSIGNED_BYTE};
61 static textypeinfo_t textype_bgra_alpha             = {TEXTYPE_BGRA   , 4, 4, 4.0f, GL_BGRA   , 4, GL_UNSIGNED_BYTE};
62 static textypeinfo_t textype_bgra_compress          = {TEXTYPE_BGRA   , 4, 4, 0.5f, GL_BGRA   , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
63 static textypeinfo_t textype_bgra_alpha_compress    = {TEXTYPE_BGRA   , 4, 4, 1.0f, GL_BGRA   , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
64 static textypeinfo_t textype_shadowmap16            = {TEXTYPE_SHADOWMAP,2,2, 2.0f, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT16_ARB, GL_UNSIGNED_SHORT};
65 static textypeinfo_t textype_shadowmap24            = {TEXTYPE_SHADOWMAP,4,4, 4.0f, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT24_ARB, GL_UNSIGNED_INT};
66 static textypeinfo_t textype_alpha                  = {TEXTYPE_ALPHA  , 1, 4, 4.0f, GL_ALPHA  , 4, GL_UNSIGNED_BYTE};
67 static textypeinfo_t textype_alpha_compress         = {TEXTYPE_ALPHA  , 1, 4, 1.0f, GL_ALPHA  , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
68
69 typedef enum gltexturetype_e
70 {
71         GLTEXTURETYPE_2D,
72         GLTEXTURETYPE_3D,
73         GLTEXTURETYPE_CUBEMAP,
74         GLTEXTURETYPE_RECTANGLE,
75         GLTEXTURETYPE_TOTAL
76 }
77 gltexturetype_t;
78
79 static int gltexturetypeenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_RECTANGLE_ARB};
80 static int gltexturetypebindingenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_BINDING_2D, GL_TEXTURE_BINDING_3D, GL_TEXTURE_BINDING_CUBE_MAP_ARB, GL_TEXTURE_BINDING_RECTANGLE_ARB};
81 static int gltexturetypedimensions[GLTEXTURETYPE_TOTAL] = {2, 3, 2, 2};
82 static int cubemapside[6] =
83 {
84         GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
85         GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
86         GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
87         GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
88         GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
89         GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
90 };
91
92 typedef struct gltexture_s
93 {
94         // this field is exposed to the R_GetTexture macro, for speed reasons
95         // (must be identical in rtexture_t)
96         int texnum; // GL texture slot number
97
98         // dynamic texture stuff [11/22/2007 Black]
99         // used to hold the texture number of dirty textures   
100         int dirtytexnum;
101         updatecallback_t updatecallback;
102         void *updatacallback_data;
103         // --- [11/22/2007 Black]
104
105         // stores backup copy of texture for deferred texture updates (r_nopartialtextureupdates cvar)
106         unsigned char *bufferpixels;
107         qboolean buffermodified;
108
109         // pointer to texturepool (check this to see if the texture is allocated)
110         struct gltexturepool_s *pool;
111         // pointer to next texture in texturepool chain
112         struct gltexture_s *chain;
113         // name of the texture (this might be removed someday), no duplicates
114         char identifier[MAX_QPATH + 32];
115         // original data size in *inputtexels
116         int inputwidth, inputheight, inputdepth;
117         // copy of the original texture(s) supplied to the upload function, for
118         // delayed uploads (non-precached)
119         unsigned char *inputtexels;
120         // original data size in *inputtexels
121         int inputdatasize;
122         // flags supplied to the LoadTexture function
123         // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
124         int flags;
125         // pointer to one of the textype_ structs
126         textypeinfo_t *textype;
127         // one of the GLTEXTURETYPE_ values
128         int texturetype;
129         // palette if the texture is TEXTYPE_PALETTE
130         const unsigned int *palette;
131         // actual stored texture size after gl_picmip and gl_max_size are applied
132         // (power of 2 if vid.support.arb_texture_non_power_of_two is not supported)
133         int tilewidth, tileheight, tiledepth;
134         // 1 or 6 depending on texturetype
135         int sides;
136         // bytes per pixel
137         int bytesperpixel;
138         // GL_RGB or GL_RGBA or GL_DEPTH_COMPONENT
139         int glformat;
140         // 3 or 4
141         int glinternalformat;
142         // GL_UNSIGNED_BYTE or GL_UNSIGNED_INT or GL_UNSIGNED_SHORT or GL_FLOAT
143         int gltype;
144 }
145 gltexture_t;
146
147 #define TEXTUREPOOL_SENTINEL 0xC0DEDBAD
148
149 typedef struct gltexturepool_s
150 {
151         unsigned int sentinel;
152         struct gltexture_s *gltchain;
153         struct gltexturepool_s *next;
154 }
155 gltexturepool_t;
156
157 static gltexturepool_t *gltexturepoolchain = NULL;
158
159 static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
160 static int resizebuffersize = 0;
161 static const unsigned char *texturebuffer;
162 static int texturebuffersize = 0;
163
164 static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
165 {
166         if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
167         {
168                 if (flags & TEXF_ALPHA)
169                 {
170                         switch(textype)
171                         {
172                         case TEXTYPE_PALETTE:
173                                 return &textype_palette_alpha_compress;
174                         case TEXTYPE_RGBA:
175                                 return &textype_rgba_alpha_compress;
176                         case TEXTYPE_BGRA:
177                                 return &textype_bgra_alpha_compress;
178                         case TEXTYPE_ALPHA:
179                                 return &textype_alpha_compress;
180                         default:
181                                 Host_Error("R_GetTexTypeInfo: unknown texture format");
182                                 return NULL;
183                         }
184                 }
185                 else
186                 {
187                         switch(textype)
188                         {
189                         case TEXTYPE_PALETTE:
190                                 return &textype_palette_compress;
191                         case TEXTYPE_RGBA:
192                                 return &textype_rgba_compress;
193                         case TEXTYPE_BGRA:
194                                 return &textype_bgra_compress;
195                         case TEXTYPE_ALPHA:
196                                 return &textype_alpha_compress;
197                         default:
198                                 Host_Error("R_GetTexTypeInfo: unknown texture format");
199                                 return NULL;
200                         }
201                 }
202         }
203         else
204         {
205                 if (flags & TEXF_ALPHA)
206                 {
207                         switch(textype)
208                         {
209                         case TEXTYPE_PALETTE:
210                                 return &textype_palette_alpha;
211                         case TEXTYPE_RGBA:
212                                 return &textype_rgba_alpha;
213                         case TEXTYPE_BGRA:
214                                 return &textype_bgra_alpha;
215                         case TEXTYPE_ALPHA:
216                                 return &textype_alpha;
217                         default:
218                                 Host_Error("R_GetTexTypeInfo: unknown texture format");
219                                 return NULL;
220                         }
221                 }
222                 else
223                 {
224                         switch(textype)
225                         {
226                         case TEXTYPE_PALETTE:
227                                 return &textype_palette;
228                         case TEXTYPE_RGBA:
229                                 return &textype_rgba;
230                         case TEXTYPE_BGRA:
231                                 return &textype_bgra;
232                         case TEXTYPE_SHADOWMAP:
233                                 return (flags & TEXF_LOWPRECISION) ? &textype_shadowmap16 : &textype_shadowmap24;
234                         case TEXTYPE_ALPHA:
235                                 return &textype_alpha;
236                         default:
237                                 Host_Error("R_GetTexTypeInfo: unknown texture format");
238                                 return NULL;
239                         }
240                 }
241         }
242         return NULL; // this line only to hush compiler warnings
243 }
244
245 // dynamic texture code [11/22/2007 Black]
246 void R_MarkDirtyTexture(rtexture_t *rt) {
247         gltexture_t *glt = (gltexture_t*) rt;
248         if( !glt ) {
249                 return;
250         }
251
252         // dont do anything if the texture is already dirty (and make sure this *is* a dynamic texture after all!)
253         if( !glt->dirtytexnum && glt->flags & GLTEXF_DYNAMIC ) {
254                 glt->dirtytexnum = glt->texnum;
255                 // mark it as dirty, so R_RealGetTexture gets called
256                 glt->texnum = 0;
257         }
258 }
259
260 void R_MakeTextureDynamic(rtexture_t *rt, updatecallback_t updatecallback, void *data) {
261         gltexture_t *glt = (gltexture_t*) rt;
262         if( !glt ) {
263                 return;
264         }
265
266         glt->flags |= GLTEXF_DYNAMIC;
267         glt->updatecallback = updatecallback;
268         glt->updatacallback_data = data;
269         glt->dirtytexnum = 0;
270 }
271
272 static void R_UpdateDynamicTexture(gltexture_t *glt) {
273         glt->texnum = glt->dirtytexnum;
274         // reset dirtytexnum again (not dirty anymore)
275         glt->dirtytexnum = 0;
276         // TODO: now assert that t->texnum != 0 ?
277         if( glt->updatecallback ) {
278                 glt->updatecallback( (rtexture_t*) glt, glt->updatacallback_data );
279         }
280 }
281
282 void R_PurgeTexture(rtexture_t *rt)
283 {
284         if(rt && !(((gltexture_t*) rt)->flags & TEXF_PERSISTENT)) {
285                 R_FreeTexture(rt);
286         }
287 }
288
289 void R_FreeTexture(rtexture_t *rt)
290 {
291         gltexture_t *glt, **gltpointer;
292
293         glt = (gltexture_t *)rt;
294         if (glt == NULL)
295                 Host_Error("R_FreeTexture: texture == NULL");
296
297         for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
298         if (*gltpointer == glt)
299                 *gltpointer = glt->chain;
300         else
301                 Host_Error("R_FreeTexture: texture \"%s\" not linked in pool", glt->identifier);
302
303         if (!(glt->flags & GLTEXF_UPLOAD))
304         {
305                 CHECKGLERROR
306                 qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
307         }
308
309         if (glt->inputtexels)
310                 Mem_Free(glt->inputtexels);
311         Mem_Free(glt);
312 }
313
314 rtexturepool_t *R_AllocTexturePool(void)
315 {
316         gltexturepool_t *pool;
317         if (texturemempool == NULL)
318                 return NULL;
319         pool = (gltexturepool_t *)Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
320         if (pool == NULL)
321                 return NULL;
322         pool->next = gltexturepoolchain;
323         gltexturepoolchain = pool;
324         pool->sentinel = TEXTUREPOOL_SENTINEL;
325         return (rtexturepool_t *)pool;
326 }
327
328 void R_FreeTexturePool(rtexturepool_t **rtexturepool)
329 {
330         gltexturepool_t *pool, **poolpointer;
331         if (rtexturepool == NULL)
332                 return;
333         if (*rtexturepool == NULL)
334                 return;
335         pool = (gltexturepool_t *)(*rtexturepool);
336         *rtexturepool = NULL;
337         if (pool->sentinel != TEXTUREPOOL_SENTINEL)
338                 Host_Error("R_FreeTexturePool: pool already freed");
339         for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
340         if (*poolpointer == pool)
341                 *poolpointer = pool->next;
342         else
343                 Host_Error("R_FreeTexturePool: pool not linked");
344         while (pool->gltchain)
345                 R_FreeTexture((rtexture_t *)pool->gltchain);
346         Mem_Free(pool);
347 }
348
349
350 typedef struct glmode_s
351 {
352         char *name;
353         int minification, magnification;
354 }
355 glmode_t;
356
357 static glmode_t modes[6] =
358 {
359         {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
360         {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
361         {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
362         {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
363         {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
364         {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
365 };
366
367 static void GL_TextureMode_f (void)
368 {
369         int i;
370         GLint oldbindtexnum;
371         gltexture_t *glt;
372         gltexturepool_t *pool;
373
374         if (Cmd_Argc() == 1)
375         {
376                 for (i = 0;i < 6;i++)
377                 {
378                         if (gl_filter_min == modes[i].minification)
379                         {
380                                 Con_Printf("%s\n", modes[i].name);
381                                 return;
382                         }
383                 }
384                 Con_Print("current filter is unknown???\n");
385                 return;
386         }
387
388         for (i = 0;i < 6;i++)
389                 if (!strcasecmp (modes[i].name, Cmd_Argv(1) ) )
390                         break;
391         if (i == 6)
392         {
393                 Con_Print("bad filter name\n");
394                 return;
395         }
396
397         gl_filter_min = modes[i].minification;
398         gl_filter_mag = modes[i].magnification;
399
400         // change all the existing mipmap texture objects
401         // FIXME: force renderer(/client/something?) restart instead?
402         CHECKGLERROR
403         GL_ActiveTexture(0);
404         for (pool = gltexturepoolchain;pool;pool = pool->next)
405         {
406                 for (glt = pool->gltchain;glt;glt = glt->chain)
407                 {
408                         // only update already uploaded images
409                         if (!(glt->flags & (GLTEXF_UPLOAD | TEXF_FORCENEAREST | TEXF_FORCELINEAR)))
410                         {
411                                 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
412                                 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
413                                 if (glt->flags & TEXF_MIPMAP)
414                                 {
415                                         qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
416                                 }
417                                 else
418                                 {
419                                         qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
420                                 }
421                                 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
422                                 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
423                         }
424                 }
425         }
426 }
427
428 static void GL_Texture_CalcImageSize(int texturetype, int flags, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth)
429 {
430         int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1;
431
432         switch (texturetype)
433         {
434         default:
435         case GLTEXTURETYPE_2D:
436                 maxsize = vid.maxtexturesize_2d;
437                 if (flags & TEXF_PICMIP)
438                 {
439                         maxsize = bound(1, gl_max_size.integer, maxsize);
440                         picmip = gl_picmip.integer;
441                 }
442                 break;
443         case GLTEXTURETYPE_3D:
444                 maxsize = vid.maxtexturesize_3d;
445                 break;
446         case GLTEXTURETYPE_CUBEMAP:
447                 maxsize = vid.maxtexturesize_cubemap;
448                 break;
449         }
450
451         if (outwidth)
452         {
453                 if (vid.support.arb_texture_non_power_of_two)
454                         width2 = min(inwidth >> picmip, maxsize);
455                 else
456                 {
457                         for (width2 = 1;width2 < inwidth;width2 <<= 1);
458                         for (width2 >>= picmip;width2 > maxsize;width2 >>= 1);
459                 }
460                 *outwidth = max(1, width2);
461         }
462         if (outheight)
463         {
464                 if (vid.support.arb_texture_non_power_of_two)
465                         height2 = min(inheight >> picmip, maxsize);
466                 else
467                 {
468                         for (height2 = 1;height2 < inheight;height2 <<= 1);
469                         for (height2 >>= picmip;height2 > maxsize;height2 >>= 1);
470                 }
471                 *outheight = max(1, height2);
472         }
473         if (outdepth)
474         {
475                 if (vid.support.arb_texture_non_power_of_two)
476                         depth2 = min(indepth >> picmip, maxsize);
477                 else
478                 {
479                         for (depth2 = 1;depth2 < indepth;depth2 <<= 1);
480                         for (depth2 >>= picmip;depth2 > maxsize;depth2 >>= 1);
481                 }
482                 *outdepth = max(1, depth2);
483         }
484 }
485
486
487 static int R_CalcTexelDataSize (gltexture_t *glt)
488 {
489         int width2, height2, depth2, size;
490
491         GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2);
492
493         size = width2 * height2 * depth2;
494
495         if (glt->flags & TEXF_MIPMAP)
496         {
497                 while (width2 > 1 || height2 > 1 || depth2 > 1)
498                 {
499                         if (width2 > 1)
500                                 width2 >>= 1;
501                         if (height2 > 1)
502                                 height2 >>= 1;
503                         if (depth2 > 1)
504                                 depth2 >>= 1;
505                         size += width2 * height2 * depth2;
506                 }
507         }
508
509         return (int)(size * glt->textype->glinternalbytesperpixel) * glt->sides;
510 }
511
512 void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean printtotal)
513 {
514         int glsize;
515         int isloaded;
516         int pooltotal = 0, pooltotalt = 0, pooltotalp = 0, poolloaded = 0, poolloadedt = 0, poolloadedp = 0;
517         int sumtotal = 0, sumtotalt = 0, sumtotalp = 0, sumloaded = 0, sumloadedt = 0, sumloadedp = 0;
518         gltexture_t *glt;
519         gltexturepool_t *pool;
520         if (printeach)
521                 Con_Print("glsize input loaded mip alpha name\n");
522         for (pool = gltexturepoolchain;pool;pool = pool->next)
523         {
524                 pooltotal = 0;
525                 pooltotalt = 0;
526                 pooltotalp = 0;
527                 poolloaded = 0;
528                 poolloadedt = 0;
529                 poolloadedp = 0;
530                 for (glt = pool->gltchain;glt;glt = glt->chain)
531                 {
532                         glsize = R_CalcTexelDataSize(glt);
533                         isloaded = !(glt->flags & GLTEXF_UPLOAD);
534                         pooltotal++;
535                         pooltotalt += glsize;
536                         pooltotalp += glt->inputdatasize;
537                         if (isloaded)
538                         {
539                                 poolloaded++;
540                                 poolloadedt += glsize;
541                                 poolloadedp += glt->inputdatasize;
542                         }
543                         if (printeach)
544                                 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);
545                 }
546                 if (printpool)
547                         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);
548                 sumtotal += pooltotal;
549                 sumtotalt += pooltotalt;
550                 sumtotalp += pooltotalp;
551                 sumloaded += poolloaded;
552                 sumloadedt += poolloadedt;
553                 sumloadedp += poolloadedp;
554         }
555         if (printtotal)
556                 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);
557 }
558
559 static void R_TextureStats_f(void)
560 {
561         R_TextureStats_Print(true, true, true);
562 }
563
564 static void r_textures_start(void)
565 {
566         // LordHavoc: allow any alignment
567         CHECKGLERROR
568         qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);CHECKGLERROR
569         qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
570
571         texturemempool = Mem_AllocPool("texture management", 0, NULL);
572
573         // Disable JPEG screenshots if the DLL isn't loaded
574         if (! JPEG_OpenLibrary ())
575                 Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
576         // TODO: support png screenshots?
577         PNG_OpenLibrary ();
578 }
579
580 static void r_textures_shutdown(void)
581 {
582         rtexturepool_t *temp;
583
584         JPEG_CloseLibrary ();
585
586         while(gltexturepoolchain)
587         {
588                 temp = (rtexturepool_t *) gltexturepoolchain;
589                 R_FreeTexturePool(&temp);
590         }
591
592         resizebuffersize = 0;
593         texturebuffersize = 0;
594         resizebuffer = NULL;
595         colorconvertbuffer = NULL;
596         texturebuffer = NULL;
597         Mem_FreePool(&texturemempool);
598 }
599
600 static void r_textures_newmap(void)
601 {
602 }
603
604 void R_Textures_Init (void)
605 {
606         Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc)");
607         Cmd_AddCommand("r_texturestats", R_TextureStats_f, "print information about all loaded textures and some statistics");
608         Cvar_RegisterVariable (&gl_max_size);
609         Cvar_RegisterVariable (&gl_picmip);
610         Cvar_RegisterVariable (&gl_max_lightmapsize);
611         Cvar_RegisterVariable (&r_lerpimages);
612         Cvar_RegisterVariable (&gl_texture_anisotropy);
613         Cvar_RegisterVariable (&gl_texturecompression);
614         Cvar_RegisterVariable (&gl_texturecompression_color);
615         Cvar_RegisterVariable (&gl_texturecompression_normal);
616         Cvar_RegisterVariable (&gl_texturecompression_gloss);
617         Cvar_RegisterVariable (&gl_texturecompression_glow);
618         Cvar_RegisterVariable (&gl_texturecompression_2d);
619         Cvar_RegisterVariable (&gl_texturecompression_q3bsplightmaps);
620         Cvar_RegisterVariable (&gl_texturecompression_q3bspdeluxemaps);
621         Cvar_RegisterVariable (&gl_texturecompression_sky);
622         Cvar_RegisterVariable (&gl_texturecompression_lightcubemaps);
623         Cvar_RegisterVariable (&gl_nopartialtextureupdates);
624
625         R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
626 }
627
628 void R_Textures_Frame (void)
629 {
630         static int old_aniso = 0;
631
632         // could do procedural texture animation here, if we keep track of which
633         // textures were accessed this frame...
634
635         // free the resize buffers
636         resizebuffersize = 0;
637         if (resizebuffer)
638         {
639                 Mem_Free(resizebuffer);
640                 resizebuffer = NULL;
641         }
642         if (colorconvertbuffer)
643         {
644                 Mem_Free(colorconvertbuffer);
645                 colorconvertbuffer = NULL;
646         }
647
648         if (old_aniso != gl_texture_anisotropy.integer)
649         {
650                 gltexture_t *glt;
651                 gltexturepool_t *pool;
652                 GLint oldbindtexnum;
653
654                 old_aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
655
656                 Cvar_SetValueQuick(&gl_texture_anisotropy, old_aniso);
657
658                 CHECKGLERROR
659                 GL_ActiveTexture(0);
660                 for (pool = gltexturepoolchain;pool;pool = pool->next)
661                 {
662                         for (glt = pool->gltchain;glt;glt = glt->chain)
663                         {
664                                 // only update already uploaded images
665                                 if ((glt->flags & (GLTEXF_UPLOAD | TEXF_MIPMAP)) == TEXF_MIPMAP)
666                                 {
667                                         oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
668
669                                         qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
670                                         qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAX_ANISOTROPY_EXT, old_aniso);CHECKGLERROR
671
672                                         qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
673                                 }
674                         }
675                 }
676         }
677 }
678
679 void R_MakeResizeBufferBigger(int size)
680 {
681         if (resizebuffersize < size)
682         {
683                 resizebuffersize = size;
684                 if (resizebuffer)
685                         Mem_Free(resizebuffer);
686                 if (colorconvertbuffer)
687                         Mem_Free(colorconvertbuffer);
688                 resizebuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
689                 colorconvertbuffer = (unsigned char *)Mem_Alloc(texturemempool, resizebuffersize);
690                 if (!resizebuffer || !colorconvertbuffer)
691                         Host_Error("R_Upload: out of memory");
692         }
693 }
694
695 static void GL_SetupTextureParameters(int flags, textype_t textype, int texturetype)
696 {
697         int textureenum = gltexturetypeenums[texturetype];
698         int wrapmode = (flags & TEXF_CLAMP) ? GL_CLAMP_TO_EDGE : GL_REPEAT;
699
700         CHECKGLERROR
701
702         if (vid.support.ext_texture_filter_anisotropic && (flags & TEXF_MIPMAP))
703         {
704                 int aniso = bound(1, gl_texture_anisotropy.integer, (int)vid.max_anisotropy);
705                 if (gl_texture_anisotropy.integer != aniso)
706                         Cvar_SetValueQuick(&gl_texture_anisotropy, aniso);
707                 qglTexParameteri(textureenum, GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso);CHECKGLERROR
708         }
709         qglTexParameteri(textureenum, GL_TEXTURE_WRAP_S, wrapmode);CHECKGLERROR
710         qglTexParameteri(textureenum, GL_TEXTURE_WRAP_T, wrapmode);CHECKGLERROR
711         if (gltexturetypedimensions[texturetype] >= 3)
712         {
713                 qglTexParameteri(textureenum, GL_TEXTURE_WRAP_R, wrapmode);CHECKGLERROR
714         }
715
716         CHECKGLERROR
717         if (flags & TEXF_FORCENEAREST)
718         {
719                 if (flags & TEXF_MIPMAP)
720                 {
721                         qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);CHECKGLERROR
722                 }
723                 else
724                 {
725                         qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_NEAREST);CHECKGLERROR
726                 }
727                 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_NEAREST);CHECKGLERROR
728         }
729         else if (flags & TEXF_FORCELINEAR)
730         {
731                 if (flags & TEXF_MIPMAP)
732                 {
733                         if (gl_filter_min == GL_NEAREST_MIPMAP_LINEAR || gl_filter_min == GL_LINEAR_MIPMAP_LINEAR)
734                         {
735                                 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);CHECKGLERROR
736                         }
737                         else
738                         {
739                                 qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);CHECKGLERROR
740                         }
741                 }
742                 else
743                 {
744                         qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, GL_LINEAR);CHECKGLERROR
745                 }
746                 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, GL_LINEAR);CHECKGLERROR
747         }
748         else
749         {
750                 if (flags & TEXF_MIPMAP)
751                 {
752                         qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
753                 }
754                 else
755                 {
756                         qglTexParameteri(textureenum, GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
757                 }
758                 qglTexParameteri(textureenum, GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
759         }
760
761         if (textype == TEXTYPE_SHADOWMAP)
762         {
763                 if (vid.support.arb_shadow)
764                 {
765                         if (flags & TEXF_COMPARE)
766                         {
767                                 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);CHECKGLERROR
768                         }
769                         else
770                         {
771                                 qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);CHECKGLERROR
772                         }
773                         qglTexParameteri(textureenum, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);CHECKGLERROR
774                 }
775                 qglTexParameteri(textureenum, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);CHECKGLERROR
776         }
777
778         CHECKGLERROR
779 }
780
781 static void R_Upload(gltexture_t *glt, const unsigned char *data, int fragx, int fragy, int fragz, int fragwidth, int fragheight, int fragdepth)
782 {
783         int i, mip, width, height, depth;
784         GLint oldbindtexnum;
785         const unsigned char *prevbuffer;
786         prevbuffer = data;
787
788         CHECKGLERROR
789
790         // we need to restore the texture binding after finishing the upload
791         GL_ActiveTexture(0);
792         oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
793         qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
794
795         // these are rounded up versions of the size to do better resampling
796         if (vid.support.arb_texture_non_power_of_two || glt->texturetype == GLTEXTURETYPE_RECTANGLE)
797         {
798                 width = glt->inputwidth;
799                 height = glt->inputheight;
800                 depth = glt->inputdepth;
801         }
802         else
803         {
804                 for (width  = 1;width  < glt->inputwidth ;width  <<= 1);
805                 for (height = 1;height < glt->inputheight;height <<= 1);
806                 for (depth  = 1;depth  < glt->inputdepth ;depth  <<= 1);
807         }
808
809         R_MakeResizeBufferBigger(width * height * depth * glt->sides * glt->bytesperpixel);
810         R_MakeResizeBufferBigger(fragwidth * fragheight * fragdepth * glt->sides * glt->bytesperpixel);
811
812         if (prevbuffer == NULL)
813         {
814                 memset(resizebuffer, 0, fragwidth * fragheight * fragdepth * glt->bytesperpixel);
815                 prevbuffer = resizebuffer;
816         }
817         else if (glt->textype->textype == TEXTYPE_PALETTE)
818         {
819                 // promote paletted to BGRA, so we only have to worry about BGRA in the rest of this code
820                 Image_Copy8bitBGRA(prevbuffer, colorconvertbuffer, fragwidth * fragheight * fragdepth * glt->sides, glt->palette);
821                 prevbuffer = colorconvertbuffer;
822         }
823
824         if ((glt->flags & (TEXF_MIPMAP | TEXF_PICMIP | GLTEXF_UPLOAD)) == 0 && glt->inputwidth == glt->tilewidth && glt->inputheight == glt->tileheight && glt->inputdepth == glt->tiledepth && (fragx != 0 || fragy != 0 || fragwidth != glt->tilewidth || fragheight != glt->tileheight))
825         {
826                 // update a portion of the image
827                 switch(glt->texturetype)
828                 {
829                 case GLTEXTURETYPE_2D:
830                         qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
831                         break;
832                 case GLTEXTURETYPE_3D:
833                         qglTexSubImage3D(GL_TEXTURE_3D, 0, fragx, fragy, fragz, fragwidth, fragheight, fragdepth, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
834                         break;
835                 default:
836                         Host_Error("R_Upload: partial update of type other than 2D");
837                         break;
838                 }
839         }
840         else
841         {
842                 if (fragx || fragy || fragz || glt->inputwidth != fragwidth || glt->inputheight != fragheight || glt->inputdepth != fragdepth)
843                         Host_Error("R_Upload: partial update not allowed on initial upload or in combination with PICMIP or MIPMAP\n");
844
845                 // upload the image for the first time
846                 glt->flags &= ~GLTEXF_UPLOAD;
847
848                 // cubemaps contain multiple images and thus get processed a bit differently
849                 if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
850                 {
851                         if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
852                         {
853                                 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
854                                 prevbuffer = resizebuffer;
855                         }
856                         // picmip/max_size
857                         while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
858                         {
859                                 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
860                                 prevbuffer = resizebuffer;
861                         }
862                 }
863                 mip = 0;
864                 if (qglGetCompressedTexImageARB)
865                 {
866                         if (gl_texturecompression.integer >= 2)
867                                 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
868                         else
869                                 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_FASTEST);
870                         CHECKGLERROR
871                 }
872                 switch(glt->texturetype)
873                 {
874                 case GLTEXTURETYPE_2D:
875                         qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
876                         if (glt->flags & TEXF_MIPMAP)
877                         {
878                                 while (width > 1 || height > 1 || depth > 1)
879                                 {
880                                         Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
881                                         prevbuffer = resizebuffer;
882                                         qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
883                                 }
884                         }
885                         break;
886                 case GLTEXTURETYPE_3D:
887                         qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
888                         if (glt->flags & TEXF_MIPMAP)
889                         {
890                                 while (width > 1 || height > 1 || depth > 1)
891                                 {
892                                         Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
893                                         prevbuffer = resizebuffer;
894                                         qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
895                                 }
896                         }
897                         break;
898                 case GLTEXTURETYPE_CUBEMAP:
899                         // convert and upload each side in turn,
900                         // from a continuous block of input texels
901                         texturebuffer = (unsigned char *)prevbuffer;
902                         for (i = 0;i < 6;i++)
903                         {
904                                 prevbuffer = texturebuffer;
905                                 texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
906                                 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
907                                 {
908                                         Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
909                                         prevbuffer = resizebuffer;
910                                 }
911                                 // picmip/max_size
912                                 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
913                                 {
914                                         Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
915                                         prevbuffer = resizebuffer;
916                                 }
917                                 mip = 0;
918                                 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
919                                 if (glt->flags & TEXF_MIPMAP)
920                                 {
921                                         while (width > 1 || height > 1 || depth > 1)
922                                         {
923                                                 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
924                                                 prevbuffer = resizebuffer;
925                                                 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
926                                         }
927                                 }
928                         }
929                         break;
930                 case GLTEXTURETYPE_RECTANGLE:
931                         qglTexImage2D(GL_TEXTURE_RECTANGLE_ARB, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, NULL);CHECKGLERROR
932                         break;
933                 }
934                 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
935         }
936         qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
937 }
938
939 int R_RealGetTexture(rtexture_t *rt)
940 {
941         if (rt)
942         {
943                 gltexture_t *glt;
944                 glt = (gltexture_t *)rt;
945                 if (glt->flags & GLTEXF_DYNAMIC)
946                         R_UpdateDynamicTexture(glt);
947                 if (glt->flags & GLTEXF_UPLOAD)
948                 {
949                         CHECKGLERROR
950                         qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
951                         R_Upload(glt, glt->inputtexels, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
952                         if (glt->inputtexels)
953                         {
954                                 Mem_Free(glt->inputtexels);
955                                 glt->inputtexels = NULL;
956                                 glt->flags |= GLTEXF_DESTROYED;
957                         }
958                         else if (glt->flags & GLTEXF_DESTROYED)
959                                 Con_Printf("R_GetTexture: Texture %s already uploaded and destroyed.  Can not upload original image again.  Uploaded blank texture.\n", glt->identifier);
960                 }
961
962                 return glt->texnum;
963         }
964         else
965                 return 0;
966 }
967
968 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)
969 {
970         int i, size;
971         gltexture_t *glt;
972         gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
973         textypeinfo_t *texinfo;
974
975         if (cls.state == ca_dedicated)
976                 return NULL;
977
978         if (texturetype == GLTEXTURETYPE_RECTANGLE && !vid.support.arb_texture_rectangle)
979         {
980                 Con_Printf ("R_LoadTexture: rectangle texture not supported by driver\n");
981                 return NULL;
982         }
983         if (texturetype == GLTEXTURETYPE_CUBEMAP && !vid.support.arb_texture_cube_map)
984         {
985                 Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
986                 return NULL;
987         }
988         if (texturetype == GLTEXTURETYPE_3D && !vid.support.ext_texture_3d)
989         {
990                 Con_Printf ("R_LoadTexture: 3d texture not supported by driver\n");
991                 return NULL;
992         }
993
994         texinfo = R_GetTexTypeInfo(textype, flags);
995         size = width * height * depth * sides * texinfo->inputbytesperpixel;
996         if (size < 1)
997         {
998                 Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides, size);
999                 return NULL;
1000         }
1001
1002         // clear the alpha flag if the texture has no transparent pixels
1003         switch(textype)
1004         {
1005         case TEXTYPE_PALETTE:
1006                 if (flags & TEXF_ALPHA)
1007                 {
1008                         flags &= ~TEXF_ALPHA;
1009                         if (data)
1010                         {
1011                                 for (i = 0;i < size;i++)
1012                                 {
1013                                         if (((unsigned char *)&palette[data[i]])[3] < 255)
1014                                         {
1015                                                 flags |= TEXF_ALPHA;
1016                                                 break;
1017                                         }
1018                                 }
1019                         }
1020                 }
1021                 break;
1022         case TEXTYPE_RGBA:
1023         case TEXTYPE_BGRA:
1024                 if (flags & TEXF_ALPHA)
1025                 {
1026                         flags &= ~TEXF_ALPHA;
1027                         if (data)
1028                         {
1029                                 for (i = 3;i < size;i += 4)
1030                                 {
1031                                         if (data[i] < 255)
1032                                         {
1033                                                 flags |= TEXF_ALPHA;
1034                                                 break;
1035                                         }
1036                                 }
1037                         }
1038                 }
1039                 break;
1040         case TEXTYPE_SHADOWMAP:
1041                 break;
1042         case TEXTYPE_ALPHA:
1043                 flags |= TEXF_ALPHA;
1044                 break;
1045         default:
1046                 Host_Error("R_LoadTexture: unknown texture type");
1047         }
1048
1049         glt = (gltexture_t *)Mem_Alloc(texturemempool, sizeof(gltexture_t));
1050         if (identifier)
1051                 strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
1052         glt->pool = pool;
1053         glt->chain = pool->gltchain;
1054         pool->gltchain = glt;
1055         glt->inputwidth = width;
1056         glt->inputheight = height;
1057         glt->inputdepth = depth;
1058         glt->flags = flags | GLTEXF_UPLOAD;
1059         glt->textype = texinfo;
1060         glt->texturetype = texturetype;
1061         glt->inputdatasize = size;
1062         glt->palette = palette;
1063         glt->glinternalformat = texinfo->glinternalformat;
1064         glt->glformat = texinfo->glformat;
1065         glt->gltype = texinfo->gltype;
1066         glt->bytesperpixel = texinfo->internalbytesperpixel;
1067         glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
1068         glt->texnum = 0;
1069         // init the dynamic texture attributes, too [11/22/2007 Black]
1070         glt->dirtytexnum = 0;
1071         glt->updatecallback = NULL;
1072         glt->updatacallback_data = NULL;
1073
1074         GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
1075
1076         // upload the texture
1077         // data may be NULL (blank texture for dynamic rendering)
1078         CHECKGLERROR
1079         qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1080         R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
1081         if ((glt->flags & TEXF_ALLOWUPDATES) && gl_nopartialtextureupdates.integer)
1082                 glt->bufferpixels = Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
1083
1084         // texture converting and uploading can take a while, so make sure we're sending keepalives
1085         CL_KeepaliveMessage(false);
1086
1087         return (rtexture_t *)glt;
1088 }
1089
1090 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)
1091 {
1092         return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_2D, data, palette);
1093 }
1094
1095 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)
1096 {
1097         return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, textype, GLTEXTURETYPE_3D, data, palette);
1098 }
1099
1100 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)
1101 {
1102         return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
1103 }
1104
1105 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)
1106 {
1107         return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_RECTANGLE, data, palette);
1108 }
1109
1110 static int R_ShadowMapTextureFlags(int precision, qboolean filter)
1111 {
1112         int flags = TEXF_CLAMP;
1113         if (filter)
1114                 flags |= TEXF_FORCELINEAR | TEXF_COMPARE;
1115         else
1116                 flags |= TEXF_FORCENEAREST;
1117         if (precision <= 16)
1118                 flags |= TEXF_LOWPRECISION;
1119         return flags;
1120 }
1121
1122 rtexture_t *R_LoadTextureShadowMapRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1123 {
1124         return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_RECTANGLE, NULL, NULL);
1125 }
1126
1127 rtexture_t *R_LoadTextureShadowMap2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1128 {
1129         return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_2D, NULL, NULL);
1130 }
1131
1132 rtexture_t *R_LoadTextureShadowMapCube(rtexturepool_t *rtexturepool, const char *identifier, int width, int precision, qboolean filter)
1133 {
1134     return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_CUBEMAP, NULL, NULL);
1135 }
1136
1137 int R_TextureWidth(rtexture_t *rt)
1138 {
1139         return rt ? ((gltexture_t *)rt)->inputwidth : 0;
1140 }
1141
1142 int R_TextureHeight(rtexture_t *rt)
1143 {
1144         return rt ? ((gltexture_t *)rt)->inputheight : 0;
1145 }
1146
1147 void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height)
1148 {
1149         gltexture_t *glt = (gltexture_t *)rt;
1150         if (data == NULL)
1151                 Host_Error("R_UpdateTexture: no data supplied");
1152         if (glt == NULL)
1153                 Host_Error("R_UpdateTexture: no texture supplied");
1154         if (!glt->texnum)
1155                 Host_Error("R_UpdateTexture: texture has not been uploaded yet");
1156         // update part of the texture
1157         if (glt->bufferpixels)
1158         {
1159                 int j;
1160                 int bpp = glt->bytesperpixel;
1161                 int inputskip = width*bpp;
1162                 int outputskip = glt->tilewidth*bpp;
1163                 const unsigned char *input = data;
1164                 unsigned char *output = glt->bufferpixels;
1165                 if (x < 0)
1166                 {
1167                         width += x;
1168                         input -= x*bpp;
1169                         x = 0;
1170                 }
1171                 if (y < 0)
1172                 {
1173                         height += y;
1174                         input -= y*inputskip;
1175                         y = 0;
1176                 }
1177                 if (width > glt->tilewidth - x)
1178                         width = glt->tilewidth - x;
1179                 if (height > glt->tileheight - y)
1180                         height = glt->tileheight - y;
1181                 if (width < 1 || height < 1)
1182                         return;
1183                 glt->buffermodified = true;
1184                 output += y*outputskip + x*bpp;
1185                 for (j = 0;j < height;j++, output += outputskip, input += inputskip)
1186                         memcpy(output, input, width*bpp);
1187                 if (!(glt->flags & TEXF_MANUALFLUSHUPDATES))
1188                         R_FlushTexture(rt);
1189         }
1190         else
1191                 R_Upload(glt, data, x, y, 0, width, height, 1);
1192 }
1193
1194 void R_FlushTexture(rtexture_t *rt)
1195 {
1196         gltexture_t *glt;
1197         if (rt == NULL)
1198                 Host_Error("R_FlushTexture: no texture supplied");
1199
1200         // update part of the texture
1201         glt = (gltexture_t *)rt;
1202
1203         if (!glt->buffermodified || !glt->bufferpixels)
1204                 return;
1205         glt->buffermodified = false;
1206         R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
1207 }
1208
1209 void R_ClearTexture (rtexture_t *rt)
1210 {
1211         gltexture_t *glt = (gltexture_t *)rt;
1212
1213         R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );
1214 }