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