]> icculus.org git repositories - divverent/darkplaces.git/blob - gl_textures.c
fix a NULL free attempt
[divverent/darkplaces.git] / gl_textures.c
1
2 #include "quakedef.h"
3 #include "image.h"
4 #include "jpeg.h"
5 #include "image_png.h"
6
7 cvar_t gl_max_size = {CVAR_SAVE, "gl_max_size", "2048", "maximum allowed texture size, can be used to reduce video memory usage, limited by hardware capabilities (typically 2048, 4096, or 8192)"};
8 cvar_t gl_max_lightmapsize = {CVAR_SAVE, "gl_max_lightmapsize", "1024", "maximum allowed texture size for lightmap textures, use larger values to improve rendering speed, as long as there is enough video memory available (setting it too high for the hardware will cause very bad performance)"};
9 cvar_t gl_picmip = {CVAR_SAVE, "gl_picmip", "0", "reduces resolution of textures by powers of 2, for example 1 will halve width/height, reducing texture memory usage by 75%"};
10 cvar_t r_lerpimages = {CVAR_SAVE, "r_lerpimages", "1", "bilinear filters images when scaling them up to power of 2 size (mode 1), looks better than glquake (mode 0)"};
11 cvar_t gl_texture_anisotropy = {CVAR_SAVE, "gl_texture_anisotropy", "1", "anisotropic filtering quality (if supported by hardware), 1 sample (no anisotropy) and 8 sample (8 tap anisotropy) are recommended values"};
12 cvar_t gl_texturecompression = {CVAR_SAVE, "gl_texturecompression", "0", "whether to compress textures, a value of 0 disables compression (even if the individual cvars are 1), 1 enables fast (low quality) compression at startup, 2 enables slow (high quality) compression at startup"};
13 cvar_t gl_texturecompression_color = {CVAR_SAVE, "gl_texturecompression_color", "1", "whether to compress colormap (diffuse) textures"};
14 cvar_t gl_texturecompression_normal = {CVAR_SAVE, "gl_texturecompression_normal", "0", "whether to compress normalmap (normalmap) textures"};
15 cvar_t gl_texturecompression_gloss = {CVAR_SAVE, "gl_texturecompression_gloss", "1", "whether to compress glossmap (specular) textures"};
16 cvar_t gl_texturecompression_glow = {CVAR_SAVE, "gl_texturecompression_glow", "1", "whether to compress glowmap (luma) textures"};
17 cvar_t gl_texturecompression_2d = {CVAR_SAVE, "gl_texturecompression_2d", "0", "whether to compress 2d (hud/menu) textures other than the font"};
18 cvar_t gl_texturecompression_q3bsplightmaps = {CVAR_SAVE, "gl_texturecompression_q3bsplightmaps", "0", "whether to compress lightmaps in q3bsp format levels"};
19 cvar_t gl_texturecompression_q3bspdeluxemaps = {CVAR_SAVE, "gl_texturecompression_q3bspdeluxemaps", "0", "whether to compress deluxemaps in q3bsp format levels (only levels compiled with q3map2 -deluxe have these)"};
20 cvar_t gl_texturecompression_sky = {CVAR_SAVE, "gl_texturecompression_sky", "0", "whether to compress sky textures"};
21 cvar_t gl_texturecompression_lightcubemaps = {CVAR_SAVE, "gl_texturecompression_lightcubemaps", "1", "whether to compress light cubemaps (spotlights and other light projection images)"};
22 cvar_t gl_nopartialtextureupdates = {CVAR_SAVE, "gl_nopartialtextureupdates", "1", "use alternate path for dynamic lightmap updates that avoids a possibly slow code path in the driver"};
23
24 int             gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
25 int             gl_filter_mag = GL_LINEAR;
26
27
28 static mempool_t *texturemempool;
29
30 // note: this must not conflict with TEXF_ flags in r_textures.h
31 // bitmask for mismatch checking
32 #define GLTEXF_IMPORTANTBITS (0)
33 // dynamic texture (treat texnum == 0 differently)
34 #define GLTEXF_DYNAMIC          0x00080000
35
36 typedef struct textypeinfo_s
37 {
38         textype_t textype;
39         int inputbytesperpixel;
40         int internalbytesperpixel;
41         float glinternalbytesperpixel;
42         int glformat;
43         int glinternalformat;
44         int gltype;
45 }
46 textypeinfo_t;
47
48 static textypeinfo_t textype_palette                = {TEXTYPE_PALETTE, 1, 4, 4.0f, GL_BGRA   , 3, GL_UNSIGNED_BYTE};
49 static textypeinfo_t textype_palette_alpha          = {TEXTYPE_PALETTE, 1, 4, 4.0f, GL_BGRA   , 4, GL_UNSIGNED_BYTE};
50 static textypeinfo_t textype_palette_compress       = {TEXTYPE_PALETTE, 1, 4, 0.5f, GL_BGRA   , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
51 static textypeinfo_t textype_palette_alpha_compress = {TEXTYPE_PALETTE, 1, 4, 1.0f, GL_BGRA   , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
52 static textypeinfo_t textype_rgba                   = {TEXTYPE_RGBA   , 4, 4, 4.0f, GL_RGBA   , 3, GL_UNSIGNED_BYTE};
53 static textypeinfo_t textype_rgba_alpha             = {TEXTYPE_RGBA   , 4, 4, 4.0f, GL_RGBA   , 4, GL_UNSIGNED_BYTE};
54 static textypeinfo_t textype_rgba_compress          = {TEXTYPE_RGBA   , 4, 4, 0.5f, GL_RGBA   , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
55 static textypeinfo_t textype_rgba_alpha_compress    = {TEXTYPE_RGBA   , 4, 4, 1.0f, GL_RGBA   , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
56 static textypeinfo_t textype_bgra                   = {TEXTYPE_BGRA   , 4, 4, 4.0f, GL_BGRA   , 3, GL_UNSIGNED_BYTE};
57 static textypeinfo_t textype_bgra_alpha             = {TEXTYPE_BGRA   , 4, 4, 4.0f, GL_BGRA   , 4, GL_UNSIGNED_BYTE};
58 static textypeinfo_t textype_bgra_compress          = {TEXTYPE_BGRA   , 4, 4, 0.5f, GL_BGRA   , GL_COMPRESSED_RGB_ARB, GL_UNSIGNED_BYTE};
59 static textypeinfo_t textype_bgra_alpha_compress    = {TEXTYPE_BGRA   , 4, 4, 1.0f, GL_BGRA   , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
60 static textypeinfo_t textype_shadowmap16            = {TEXTYPE_SHADOWMAP,2,2, 2.0f, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT16_ARB, GL_UNSIGNED_SHORT};
61 static textypeinfo_t textype_shadowmap24            = {TEXTYPE_SHADOWMAP,4,4, 4.0f, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT24_ARB, GL_UNSIGNED_INT};
62 static textypeinfo_t textype_alpha                  = {TEXTYPE_ALPHA  , 1, 4, 4.0f, GL_ALPHA  , 4, GL_UNSIGNED_BYTE};
63 static textypeinfo_t textype_alpha_compress         = {TEXTYPE_ALPHA  , 1, 4, 1.0f, GL_ALPHA  , GL_COMPRESSED_RGBA_ARB, GL_UNSIGNED_BYTE};
64
65 typedef enum gltexturetype_e
66 {
67         GLTEXTURETYPE_2D,
68         GLTEXTURETYPE_3D,
69         GLTEXTURETYPE_CUBEMAP,
70         GLTEXTURETYPE_RECTANGLE,
71         GLTEXTURETYPE_TOTAL
72 }
73 gltexturetype_t;
74
75 static int gltexturetypeenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_RECTANGLE_ARB};
76 static int gltexturetypebindingenums[GLTEXTURETYPE_TOTAL] = {GL_TEXTURE_BINDING_2D, GL_TEXTURE_BINDING_3D, GL_TEXTURE_BINDING_CUBE_MAP_ARB, GL_TEXTURE_BINDING_RECTANGLE_ARB};
77 static int gltexturetypedimensions[GLTEXTURETYPE_TOTAL] = {2, 3, 2, 2};
78 static int cubemapside[6] =
79 {
80         GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
81         GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
82         GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
83         GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
84         GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
85         GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
86 };
87
88 typedef struct gltexture_s
89 {
90         // this portion of the struct is exposed to the R_GetTexture macro for
91         // speed reasons, must be identical in rtexture_t!
92         int texnum; // GL texture slot number
93         qboolean dirty; // indicates that R_RealGetTexture should be called
94         int gltexturetypeenum; // used by R_Mesh_TexBind
95
96         // dynamic texture stuff [11/22/2007 Black]
97         updatecallback_t updatecallback;
98         void *updatacallback_data;
99         // --- [11/22/2007 Black]
100
101         // stores backup copy of texture for deferred texture updates (gl_nopartialtextureupdates cvar)
102         unsigned char *bufferpixels;
103         qboolean buffermodified;
104
105         // pointer to texturepool (check this to see if the texture is allocated)
106         struct gltexturepool_s *pool;
107         // pointer to next texture in texturepool chain
108         struct gltexture_s *chain;
109         // name of the texture (this might be removed someday), no duplicates
110         char identifier[MAX_QPATH + 32];
111         // original data size in *inputtexels
112         int inputwidth, inputheight, inputdepth;
113         // copy of the original texture(s) supplied to the upload function, for
114         // delayed uploads (non-precached)
115         unsigned char *inputtexels;
116         // original data size in *inputtexels
117         int inputdatasize;
118         // flags supplied to the LoadTexture function
119         // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
120         int flags;
121         // pointer to one of the textype_ structs
122         textypeinfo_t *textype;
123         // one of the GLTEXTURETYPE_ values
124         int texturetype;
125         // palette if the texture is TEXTYPE_PALETTE
126         const unsigned int *palette;
127         // actual stored texture size after gl_picmip and gl_max_size are applied
128         // (power of 2 if vid.support.arb_texture_non_power_of_two is not supported)
129         int tilewidth, tileheight, tiledepth;
130         // 1 or 6 depending on texturetype
131         int sides;
132         // bytes per pixel
133         int bytesperpixel;
134         // GL_RGB or GL_RGBA or GL_DEPTH_COMPONENT
135         int glformat;
136         // 3 or 4
137         int glinternalformat;
138         // GL_UNSIGNED_BYTE or GL_UNSIGNED_INT or GL_UNSIGNED_SHORT or GL_FLOAT
139         int gltype;
140 }
141 gltexture_t;
142
143 #define TEXTUREPOOL_SENTINEL 0xC0DEDBAD
144
145 typedef struct gltexturepool_s
146 {
147         unsigned int sentinel;
148         struct gltexture_s *gltchain;
149         struct gltexturepool_s *next;
150 }
151 gltexturepool_t;
152
153 static gltexturepool_t *gltexturepoolchain = NULL;
154
155 static unsigned char *resizebuffer = NULL, *colorconvertbuffer;
156 static int resizebuffersize = 0;
157 static const unsigned char *texturebuffer;
158 static int texturebuffersize = 0;
159
160 static textypeinfo_t *R_GetTexTypeInfo(textype_t textype, int flags)
161 {
162         if ((flags & TEXF_COMPRESS) && gl_texturecompression.integer >= 1 && vid.support.arb_texture_compression)
163         {
164                 if (flags & TEXF_ALPHA)
165                 {
166                         switch(textype)
167                         {
168                         case TEXTYPE_PALETTE:
169                                 return &textype_palette_alpha_compress;
170                         case TEXTYPE_RGBA:
171                                 return &textype_rgba_alpha_compress;
172                         case TEXTYPE_BGRA:
173                                 return &textype_bgra_alpha_compress;
174                         case TEXTYPE_ALPHA:
175                                 return &textype_alpha_compress;
176                         default:
177                                 Host_Error("R_GetTexTypeInfo: unknown texture format");
178                                 return NULL;
179                         }
180                 }
181                 else
182                 {
183                         switch(textype)
184                         {
185                         case TEXTYPE_PALETTE:
186                                 return &textype_palette_compress;
187                         case TEXTYPE_RGBA:
188                                 return &textype_rgba_compress;
189                         case TEXTYPE_BGRA:
190                                 return &textype_bgra_compress;
191                         case TEXTYPE_ALPHA:
192                                 return &textype_alpha_compress;
193                         default:
194                                 Host_Error("R_GetTexTypeInfo: unknown texture format");
195                                 return NULL;
196                         }
197                 }
198         }
199         else
200         {
201                 if (flags & TEXF_ALPHA)
202                 {
203                         switch(textype)
204                         {
205                         case TEXTYPE_PALETTE:
206                                 return &textype_palette_alpha;
207                         case TEXTYPE_RGBA:
208                                 return &textype_rgba_alpha;
209                         case TEXTYPE_BGRA:
210                                 return &textype_bgra_alpha;
211                         case TEXTYPE_ALPHA:
212                                 return &textype_alpha;
213                         default:
214                                 Host_Error("R_GetTexTypeInfo: unknown texture format");
215                                 return NULL;
216                         }
217                 }
218                 else
219                 {
220                         switch(textype)
221                         {
222                         case TEXTYPE_PALETTE:
223                                 return &textype_palette;
224                         case TEXTYPE_RGBA:
225                                 return &textype_rgba;
226                         case TEXTYPE_BGRA:
227                                 return &textype_bgra;
228                         case TEXTYPE_SHADOWMAP:
229                                 return (flags & TEXF_LOWPRECISION) ? &textype_shadowmap16 : &textype_shadowmap24;
230                         case TEXTYPE_ALPHA:
231                                 return &textype_alpha;
232                         default:
233                                 Host_Error("R_GetTexTypeInfo: unknown texture format");
234                                 return NULL;
235                         }
236                 }
237         }
238         return NULL; // this line only to hush compiler warnings
239 }
240
241 // dynamic texture code [11/22/2007 Black]
242 void R_MarkDirtyTexture(rtexture_t *rt) {
243         gltexture_t *glt = (gltexture_t*) rt;
244         if( !glt ) {
245                 return;
246         }
247
248         // dont do anything if the texture is already dirty (and make sure this *is* a dynamic texture after all!)
249         if (glt->flags & GLTEXF_DYNAMIC)
250         {
251                 // mark it as dirty, so R_RealGetTexture gets called
252                 glt->dirty = true;
253         }
254 }
255
256 void R_MakeTextureDynamic(rtexture_t *rt, updatecallback_t updatecallback, void *data) {
257         gltexture_t *glt = (gltexture_t*) rt;
258         if( !glt ) {
259                 return;
260         }
261
262         glt->flags |= GLTEXF_DYNAMIC;
263         glt->updatecallback = updatecallback;
264         glt->updatacallback_data = data;
265 }
266
267 static void R_UpdateDynamicTexture(gltexture_t *glt) {
268         glt->dirty = false;
269         if( glt->updatecallback ) {
270                 glt->updatecallback( (rtexture_t*) glt, glt->updatacallback_data );
271         }
272 }
273
274 void R_PurgeTexture(rtexture_t *rt)
275 {
276         if(rt && !(((gltexture_t*) rt)->flags & TEXF_PERSISTENT)) {
277                 R_FreeTexture(rt);
278         }
279 }
280
281 void R_FreeTexture(rtexture_t *rt)
282 {
283         gltexture_t *glt, **gltpointer;
284
285         glt = (gltexture_t *)rt;
286         if (glt == NULL)
287                 Host_Error("R_FreeTexture: texture == NULL");
288
289         for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
290         if (*gltpointer == glt)
291                 *gltpointer = glt->chain;
292         else
293                 Host_Error("R_FreeTexture: texture \"%s\" not linked in pool", glt->identifier);
294
295         if (glt->texnum)
296         {
297                 CHECKGLERROR
298                 qglDeleteTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
299         }
300
301         if (glt->inputtexels)
302                 Mem_Free(glt->inputtexels);
303         Mem_Free(glt);
304 }
305
306 rtexturepool_t *R_AllocTexturePool(void)
307 {
308         gltexturepool_t *pool;
309         if (texturemempool == NULL)
310                 return NULL;
311         pool = (gltexturepool_t *)Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
312         if (pool == NULL)
313                 return NULL;
314         pool->next = gltexturepoolchain;
315         gltexturepoolchain = pool;
316         pool->sentinel = TEXTUREPOOL_SENTINEL;
317         return (rtexturepool_t *)pool;
318 }
319
320 void R_FreeTexturePool(rtexturepool_t **rtexturepool)
321 {
322         gltexturepool_t *pool, **poolpointer;
323         if (rtexturepool == NULL)
324                 return;
325         if (*rtexturepool == NULL)
326                 return;
327         pool = (gltexturepool_t *)(*rtexturepool);
328         *rtexturepool = NULL;
329         if (pool->sentinel != TEXTUREPOOL_SENTINEL)
330                 Host_Error("R_FreeTexturePool: pool already freed");
331         for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
332         if (*poolpointer == pool)
333                 *poolpointer = pool->next;
334         else
335                 Host_Error("R_FreeTexturePool: pool not linked");
336         while (pool->gltchain)
337                 R_FreeTexture((rtexture_t *)pool->gltchain);
338         Mem_Free(pool);
339 }
340
341
342 typedef struct glmode_s
343 {
344         char *name;
345         int minification, magnification;
346 }
347 glmode_t;
348
349 static glmode_t modes[6] =
350 {
351         {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
352         {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
353         {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
354         {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
355         {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
356         {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
357 };
358
359 static void GL_TextureMode_f (void)
360 {
361         int i;
362         GLint oldbindtexnum;
363         gltexture_t *glt;
364         gltexturepool_t *pool;
365
366         if (Cmd_Argc() == 1)
367         {
368                 for (i = 0;i < 6;i++)
369                 {
370                         if (gl_filter_min == modes[i].minification)
371                         {
372                                 Con_Printf("%s\n", modes[i].name);
373                                 return;
374                         }
375                 }
376                 Con_Print("current filter is unknown???\n");
377                 return;
378         }
379
380         for (i = 0;i < 6;i++)
381                 if (!strcasecmp (modes[i].name, Cmd_Argv(1) ) )
382                         break;
383         if (i == 6)
384         {
385                 Con_Print("bad filter name\n");
386                 return;
387         }
388
389         gl_filter_min = modes[i].minification;
390         gl_filter_mag = modes[i].magnification;
391
392         // change all the existing mipmap texture objects
393         // FIXME: force renderer(/client/something?) restart instead?
394         CHECKGLERROR
395         GL_ActiveTexture(0);
396         for (pool = gltexturepoolchain;pool;pool = pool->next)
397         {
398                 for (glt = pool->gltchain;glt;glt = glt->chain)
399                 {
400                         // only update already uploaded images
401                         if (glt->texnum && !(glt->flags & (TEXF_FORCENEAREST | TEXF_FORCELINEAR)))
402                         {
403                                 oldbindtexnum = R_Mesh_TexBound(0, gltexturetypebindingenums[glt->texturetype]);
404                                 qglBindTexture(gltexturetypeenums[glt->texturetype], glt->texnum);CHECKGLERROR
405                                 if (glt->flags & TEXF_MIPMAP)
406                                 {
407                                         qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_min);CHECKGLERROR
408                                 }
409                                 else
410                                 {
411                                         qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MIN_FILTER, gl_filter_mag);CHECKGLERROR
412                                 }
413                                 qglTexParameteri(gltexturetypeenums[glt->texturetype], GL_TEXTURE_MAG_FILTER, gl_filter_mag);CHECKGLERROR
414                                 qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
415                         }
416                 }
417         }
418 }
419
420 static void GL_Texture_CalcImageSize(int texturetype, int flags, int inwidth, int inheight, int indepth, int *outwidth, int *outheight, int *outdepth)
421 {
422         int picmip = 0, maxsize = 0, width2 = 1, height2 = 1, depth2 = 1;
423
424         switch (texturetype)
425         {
426         default:
427         case GLTEXTURETYPE_2D:
428                 maxsize = vid.maxtexturesize_2d;
429                 if (flags & TEXF_PICMIP)
430                 {
431                         maxsize = bound(1, gl_max_size.integer, maxsize);
432                         picmip = gl_picmip.integer;
433                 }
434                 break;
435         case GLTEXTURETYPE_3D:
436                 maxsize = vid.maxtexturesize_3d;
437                 break;
438         case GLTEXTURETYPE_CUBEMAP:
439                 maxsize = vid.maxtexturesize_cubemap;
440                 break;
441         }
442
443         if (outwidth)
444         {
445                 if (vid.support.arb_texture_non_power_of_two)
446                         width2 = min(inwidth >> picmip, maxsize);
447                 else
448                 {
449                         for (width2 = 1;width2 < inwidth;width2 <<= 1);
450                         for (width2 >>= picmip;width2 > maxsize;width2 >>= 1);
451                 }
452                 *outwidth = max(1, width2);
453         }
454         if (outheight)
455         {
456                 if (vid.support.arb_texture_non_power_of_two)
457                         height2 = min(inheight >> picmip, maxsize);
458                 else
459                 {
460                         for (height2 = 1;height2 < inheight;height2 <<= 1);
461                         for (height2 >>= picmip;height2 > maxsize;height2 >>= 1);
462                 }
463                 *outheight = max(1, height2);
464         }
465         if (outdepth)
466         {
467                 if (vid.support.arb_texture_non_power_of_two)
468                         depth2 = min(indepth >> picmip, maxsize);
469                 else
470                 {
471                         for (depth2 = 1;depth2 < indepth;depth2 <<= 1);
472                         for (depth2 >>= picmip;depth2 > maxsize;depth2 >>= 1);
473                 }
474                 *outdepth = max(1, depth2);
475         }
476 }
477
478
479 static int R_CalcTexelDataSize (gltexture_t *glt)
480 {
481         int width2, height2, depth2, size;
482
483         GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &width2, &height2, &depth2);
484
485         size = width2 * height2 * depth2;
486
487         if (glt->flags & TEXF_MIPMAP)
488         {
489                 while (width2 > 1 || height2 > 1 || depth2 > 1)
490                 {
491                         if (width2 > 1)
492                                 width2 >>= 1;
493                         if (height2 > 1)
494                                 height2 >>= 1;
495                         if (depth2 > 1)
496                                 depth2 >>= 1;
497                         size += width2 * height2 * depth2;
498                 }
499         }
500
501         return (int)(size * glt->textype->glinternalbytesperpixel) * glt->sides;
502 }
503
504 void R_TextureStats_Print(qboolean printeach, qboolean printpool, qboolean printtotal)
505 {
506         int glsize;
507         int isloaded;
508         int pooltotal = 0, pooltotalt = 0, pooltotalp = 0, poolloaded = 0, poolloadedt = 0, poolloadedp = 0;
509         int sumtotal = 0, sumtotalt = 0, sumtotalp = 0, sumloaded = 0, sumloadedt = 0, sumloadedp = 0;
510         gltexture_t *glt;
511         gltexturepool_t *pool;
512         if (printeach)
513                 Con_Print("glsize input loaded mip alpha name\n");
514         for (pool = gltexturepoolchain;pool;pool = pool->next)
515         {
516                 pooltotal = 0;
517                 pooltotalt = 0;
518                 pooltotalp = 0;
519                 poolloaded = 0;
520                 poolloadedt = 0;
521                 poolloadedp = 0;
522                 for (glt = pool->gltchain;glt;glt = glt->chain)
523                 {
524                         glsize = R_CalcTexelDataSize(glt);
525                         isloaded = glt->texnum != 0;
526                         pooltotal++;
527                         pooltotalt += glsize;
528                         pooltotalp += glt->inputdatasize;
529                         if (isloaded)
530                         {
531                                 poolloaded++;
532                                 poolloadedt += glsize;
533                                 poolloadedp += glt->inputdatasize;
534                         }
535                         if (printeach)
536                                 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);
537                 }
538                 if (printpool)
539                         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);
540                 sumtotal += pooltotal;
541                 sumtotalt += pooltotalt;
542                 sumtotalp += pooltotalp;
543                 sumloaded += poolloaded;
544                 sumloadedt += poolloadedt;
545                 sumloadedp += poolloadedp;
546         }
547         if (printtotal)
548                 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);
549 }
550
551 static void R_TextureStats_f(void)
552 {
553         R_TextureStats_Print(true, true, true);
554 }
555
556 static void r_textures_start(void)
557 {
558         // LordHavoc: allow any alignment
559         CHECKGLERROR
560         qglPixelStorei(GL_UNPACK_ALIGNMENT, 1);CHECKGLERROR
561         qglPixelStorei(GL_PACK_ALIGNMENT, 1);CHECKGLERROR
562
563         texturemempool = Mem_AllocPool("texture management", 0, NULL);
564
565         // Disable JPEG screenshots if the DLL isn't loaded
566         if (! JPEG_OpenLibrary ())
567                 Cvar_SetValueQuick (&scr_screenshot_jpeg, 0);
568         // TODO: support png screenshots?
569         PNG_OpenLibrary ();
570 }
571
572 static void r_textures_shutdown(void)
573 {
574         rtexturepool_t *temp;
575
576         JPEG_CloseLibrary ();
577
578         while(gltexturepoolchain)
579         {
580                 temp = (rtexturepool_t *) gltexturepoolchain;
581                 R_FreeTexturePool(&temp);
582         }
583
584         resizebuffersize = 0;
585         texturebuffersize = 0;
586         resizebuffer = NULL;
587         colorconvertbuffer = NULL;
588         texturebuffer = NULL;
589         Mem_FreePool(&texturemempool);
590 }
591
592 static void r_textures_newmap(void)
593 {
594 }
595
596 void R_Textures_Init (void)
597 {
598         Cmd_AddCommand("gl_texturemode", &GL_TextureMode_f, "set texture filtering mode (GL_NEAREST, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, etc)");
599         Cmd_AddCommand("r_texturestats", R_TextureStats_f, "print information about all loaded textures and some statistics");
600         Cvar_RegisterVariable (&gl_max_size);
601         Cvar_RegisterVariable (&gl_picmip);
602         Cvar_RegisterVariable (&gl_max_lightmapsize);
603         Cvar_RegisterVariable (&r_lerpimages);
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->texnum && (glt->flags & 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         // upload the image - preferring to do only complete uploads (drivers do not really like partial updates)
817
818         if ((glt->flags & (TEXF_MIPMAP | TEXF_PICMIP)) == 0 && glt->inputwidth == glt->tilewidth && glt->inputheight == glt->tileheight && glt->inputdepth == glt->tiledepth && (fragx != 0 || fragy != 0 || fragwidth != glt->tilewidth || fragheight != glt->tileheight))
819         {
820                 // update a portion of the image
821                 switch(glt->texturetype)
822                 {
823                 case GLTEXTURETYPE_2D:
824                         qglTexSubImage2D(GL_TEXTURE_2D, 0, fragx, fragy, fragwidth, fragheight, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
825                         break;
826                 case GLTEXTURETYPE_3D:
827                         qglTexSubImage3D(GL_TEXTURE_3D, 0, fragx, fragy, fragz, fragwidth, fragheight, fragdepth, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
828                         break;
829                 default:
830                         Host_Error("R_Upload: partial update of type other than 2D");
831                         break;
832                 }
833         }
834         else
835         {
836                 if (fragx || fragy || fragz || glt->inputwidth != fragwidth || glt->inputheight != fragheight || glt->inputdepth != fragdepth)
837                         Host_Error("R_Upload: partial update not allowed on initial upload or in combination with PICMIP or MIPMAP\n");
838
839                 // cubemaps contain multiple images and thus get processed a bit differently
840                 if (glt->texturetype != GLTEXTURETYPE_CUBEMAP)
841                 {
842                         if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
843                         {
844                                 Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
845                                 prevbuffer = resizebuffer;
846                         }
847                         // picmip/max_size
848                         while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
849                         {
850                                 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
851                                 prevbuffer = resizebuffer;
852                         }
853                 }
854                 mip = 0;
855                 if (qglGetCompressedTexImageARB)
856                 {
857                         if (gl_texturecompression.integer >= 2)
858                                 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_NICEST);
859                         else
860                                 qglHint(GL_TEXTURE_COMPRESSION_HINT_ARB, GL_FASTEST);
861                         CHECKGLERROR
862                 }
863                 switch(glt->texturetype)
864                 {
865                 case GLTEXTURETYPE_2D:
866                         qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
867                         if (glt->flags & TEXF_MIPMAP)
868                         {
869                                 while (width > 1 || height > 1 || depth > 1)
870                                 {
871                                         Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
872                                         prevbuffer = resizebuffer;
873                                         qglTexImage2D(GL_TEXTURE_2D, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
874                                 }
875                         }
876                         break;
877                 case GLTEXTURETYPE_3D:
878                         qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
879                         if (glt->flags & TEXF_MIPMAP)
880                         {
881                                 while (width > 1 || height > 1 || depth > 1)
882                                 {
883                                         Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
884                                         prevbuffer = resizebuffer;
885                                         qglTexImage3D(GL_TEXTURE_3D, mip++, glt->glinternalformat, width, height, depth, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
886                                 }
887                         }
888                         break;
889                 case GLTEXTURETYPE_CUBEMAP:
890                         // convert and upload each side in turn,
891                         // from a continuous block of input texels
892                         texturebuffer = (unsigned char *)prevbuffer;
893                         for (i = 0;i < 6;i++)
894                         {
895                                 prevbuffer = texturebuffer;
896                                 texturebuffer += glt->inputwidth * glt->inputheight * glt->inputdepth * glt->textype->inputbytesperpixel;
897                                 if (glt->inputwidth != width || glt->inputheight != height || glt->inputdepth != depth)
898                                 {
899                                         Image_Resample32(prevbuffer, glt->inputwidth, glt->inputheight, glt->inputdepth, resizebuffer, width, height, depth, r_lerpimages.integer);
900                                         prevbuffer = resizebuffer;
901                                 }
902                                 // picmip/max_size
903                                 while (width > glt->tilewidth || height > glt->tileheight || depth > glt->tiledepth)
904                                 {
905                                         Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, glt->tilewidth, glt->tileheight, glt->tiledepth);
906                                         prevbuffer = resizebuffer;
907                                 }
908                                 mip = 0;
909                                 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
910                                 if (glt->flags & TEXF_MIPMAP)
911                                 {
912                                         while (width > 1 || height > 1 || depth > 1)
913                                         {
914                                                 Image_MipReduce32(prevbuffer, resizebuffer, &width, &height, &depth, 1, 1, 1);
915                                                 prevbuffer = resizebuffer;
916                                                 qglTexImage2D(cubemapside[i], mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, prevbuffer);CHECKGLERROR
917                                         }
918                                 }
919                         }
920                         break;
921                 case GLTEXTURETYPE_RECTANGLE:
922                         qglTexImage2D(GL_TEXTURE_RECTANGLE_ARB, mip++, glt->glinternalformat, width, height, 0, glt->glformat, glt->gltype, NULL);CHECKGLERROR
923                         break;
924                 }
925                 GL_SetupTextureParameters(glt->flags, glt->textype->textype, glt->texturetype);
926         }
927         qglBindTexture(gltexturetypeenums[glt->texturetype], oldbindtexnum);CHECKGLERROR
928 }
929
930 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)
931 {
932         int i, size;
933         gltexture_t *glt;
934         gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
935         textypeinfo_t *texinfo;
936
937         if (cls.state == ca_dedicated)
938                 return NULL;
939
940         if (texturetype == GLTEXTURETYPE_RECTANGLE && !vid.support.arb_texture_rectangle)
941         {
942                 Con_Printf ("R_LoadTexture: rectangle texture not supported by driver\n");
943                 return NULL;
944         }
945         if (texturetype == GLTEXTURETYPE_CUBEMAP && !vid.support.arb_texture_cube_map)
946         {
947                 Con_Printf ("R_LoadTexture: cubemap texture not supported by driver\n");
948                 return NULL;
949         }
950         if (texturetype == GLTEXTURETYPE_3D && !vid.support.ext_texture_3d)
951         {
952                 Con_Printf ("R_LoadTexture: 3d texture not supported by driver\n");
953                 return NULL;
954         }
955
956         texinfo = R_GetTexTypeInfo(textype, flags);
957         size = width * height * depth * sides * texinfo->inputbytesperpixel;
958         if (size < 1)
959         {
960                 Con_Printf ("R_LoadTexture: bogus texture size (%dx%dx%dx%dbppx%dsides = %d bytes)\n", width, height, depth, texinfo->inputbytesperpixel * 8, sides, size);
961                 return NULL;
962         }
963
964         // clear the alpha flag if the texture has no transparent pixels
965         switch(textype)
966         {
967         case TEXTYPE_PALETTE:
968                 if (flags & TEXF_ALPHA)
969                 {
970                         flags &= ~TEXF_ALPHA;
971                         if (data)
972                         {
973                                 for (i = 0;i < size;i++)
974                                 {
975                                         if (((unsigned char *)&palette[data[i]])[3] < 255)
976                                         {
977                                                 flags |= TEXF_ALPHA;
978                                                 break;
979                                         }
980                                 }
981                         }
982                 }
983                 break;
984         case TEXTYPE_RGBA:
985         case TEXTYPE_BGRA:
986                 if (flags & TEXF_ALPHA)
987                 {
988                         flags &= ~TEXF_ALPHA;
989                         if (data)
990                         {
991                                 for (i = 3;i < size;i += 4)
992                                 {
993                                         if (data[i] < 255)
994                                         {
995                                                 flags |= TEXF_ALPHA;
996                                                 break;
997                                         }
998                                 }
999                         }
1000                 }
1001                 break;
1002         case TEXTYPE_SHADOWMAP:
1003                 break;
1004         case TEXTYPE_ALPHA:
1005                 flags |= TEXF_ALPHA;
1006                 break;
1007         default:
1008                 Host_Error("R_LoadTexture: unknown texture type");
1009         }
1010
1011         glt = (gltexture_t *)Mem_Alloc(texturemempool, sizeof(gltexture_t));
1012         if (identifier)
1013                 strlcpy (glt->identifier, identifier, sizeof(glt->identifier));
1014         glt->pool = pool;
1015         glt->chain = pool->gltchain;
1016         pool->gltchain = glt;
1017         glt->inputwidth = width;
1018         glt->inputheight = height;
1019         glt->inputdepth = depth;
1020         glt->flags = flags;
1021         glt->textype = texinfo;
1022         glt->texturetype = texturetype;
1023         glt->inputdatasize = size;
1024         glt->palette = palette;
1025         glt->glinternalformat = texinfo->glinternalformat;
1026         glt->glformat = texinfo->glformat;
1027         glt->gltype = texinfo->gltype;
1028         glt->bytesperpixel = texinfo->internalbytesperpixel;
1029         glt->sides = glt->texturetype == GLTEXTURETYPE_CUBEMAP ? 6 : 1;
1030         glt->texnum = 0;
1031         glt->dirty = false;
1032         glt->gltexturetypeenum = gltexturetypeenums[glt->texturetype];
1033         // init the dynamic texture attributes, too [11/22/2007 Black]
1034         glt->updatecallback = NULL;
1035         glt->updatacallback_data = NULL;
1036
1037         GL_Texture_CalcImageSize(glt->texturetype, glt->flags, glt->inputwidth, glt->inputheight, glt->inputdepth, &glt->tilewidth, &glt->tileheight, &glt->tiledepth);
1038
1039         // upload the texture
1040         // data may be NULL (blank texture for dynamic rendering)
1041         CHECKGLERROR
1042         qglGenTextures(1, (GLuint *)&glt->texnum);CHECKGLERROR
1043         R_Upload(glt, data, 0, 0, 0, glt->inputwidth, glt->inputheight, glt->inputdepth);
1044         if ((glt->flags & TEXF_ALLOWUPDATES) && gl_nopartialtextureupdates.integer)
1045                 glt->bufferpixels = Mem_Alloc(texturemempool, glt->tilewidth*glt->tileheight*glt->tiledepth*glt->sides*glt->bytesperpixel);
1046
1047         // texture converting and uploading can take a while, so make sure we're sending keepalives
1048         CL_KeepaliveMessage(false);
1049
1050         return (rtexture_t *)glt;
1051 }
1052
1053 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)
1054 {
1055         return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_2D, data, palette);
1056 }
1057
1058 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)
1059 {
1060         return R_SetupTexture(rtexturepool, identifier, width, height, depth, 1, flags, textype, GLTEXTURETYPE_3D, data, palette);
1061 }
1062
1063 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)
1064 {
1065         return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, flags, textype, GLTEXTURETYPE_CUBEMAP, data, palette);
1066 }
1067
1068 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)
1069 {
1070         return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, flags, textype, GLTEXTURETYPE_RECTANGLE, data, palette);
1071 }
1072
1073 static int R_ShadowMapTextureFlags(int precision, qboolean filter)
1074 {
1075         int flags = TEXF_CLAMP;
1076         if (filter)
1077                 flags |= TEXF_FORCELINEAR | TEXF_COMPARE;
1078         else
1079                 flags |= TEXF_FORCENEAREST;
1080         if (precision <= 16)
1081                 flags |= TEXF_LOWPRECISION;
1082         return flags;
1083 }
1084
1085 rtexture_t *R_LoadTextureShadowMapRectangle(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1086 {
1087         return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_RECTANGLE, NULL, NULL);
1088 }
1089
1090 rtexture_t *R_LoadTextureShadowMap2D(rtexturepool_t *rtexturepool, const char *identifier, int width, int height, int precision, qboolean filter)
1091 {
1092         return R_SetupTexture(rtexturepool, identifier, width, height, 1, 1, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_2D, NULL, NULL);
1093 }
1094
1095 rtexture_t *R_LoadTextureShadowMapCube(rtexturepool_t *rtexturepool, const char *identifier, int width, int precision, qboolean filter)
1096 {
1097     return R_SetupTexture(rtexturepool, identifier, width, width, 1, 6, R_ShadowMapTextureFlags(precision, filter), TEXTYPE_SHADOWMAP, GLTEXTURETYPE_CUBEMAP, NULL, NULL);
1098 }
1099
1100 int R_TextureWidth(rtexture_t *rt)
1101 {
1102         return rt ? ((gltexture_t *)rt)->inputwidth : 0;
1103 }
1104
1105 int R_TextureHeight(rtexture_t *rt)
1106 {
1107         return rt ? ((gltexture_t *)rt)->inputheight : 0;
1108 }
1109
1110 void R_UpdateTexture(rtexture_t *rt, const unsigned char *data, int x, int y, int width, int height)
1111 {
1112         gltexture_t *glt = (gltexture_t *)rt;
1113         if (data == NULL)
1114                 Host_Error("R_UpdateTexture: no data supplied");
1115         if (glt == NULL)
1116                 Host_Error("R_UpdateTexture: no texture supplied");
1117         if (!glt->texnum)
1118                 Host_Error("R_UpdateTexture: texture has not been uploaded yet");
1119         // update part of the texture
1120         if (glt->bufferpixels)
1121         {
1122                 int j;
1123                 int bpp = glt->bytesperpixel;
1124                 int inputskip = width*bpp;
1125                 int outputskip = glt->tilewidth*bpp;
1126                 const unsigned char *input = data;
1127                 unsigned char *output = glt->bufferpixels;
1128                 if (x < 0)
1129                 {
1130                         width += x;
1131                         input -= x*bpp;
1132                         x = 0;
1133                 }
1134                 if (y < 0)
1135                 {
1136                         height += y;
1137                         input -= y*inputskip;
1138                         y = 0;
1139                 }
1140                 if (width > glt->tilewidth - x)
1141                         width = glt->tilewidth - x;
1142                 if (height > glt->tileheight - y)
1143                         height = glt->tileheight - y;
1144                 if (width < 1 || height < 1)
1145                         return;
1146                 glt->dirty = true;
1147                 glt->buffermodified = true;
1148                 output += y*outputskip + x*bpp;
1149                 for (j = 0;j < height;j++, output += outputskip, input += inputskip)
1150                         memcpy(output, input, width*bpp);
1151         }
1152         else
1153                 R_Upload(glt, data, x, y, 0, width, height, 1);
1154 }
1155
1156 int R_RealGetTexture(rtexture_t *rt)
1157 {
1158         if (rt)
1159         {
1160                 gltexture_t *glt;
1161                 glt = (gltexture_t *)rt;
1162                 if (glt->flags & GLTEXF_DYNAMIC)
1163                         R_UpdateDynamicTexture(glt);
1164                 if (glt->buffermodified && glt->bufferpixels)
1165                 {
1166                         glt->buffermodified = false;
1167                         R_Upload(glt, glt->bufferpixels, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth);
1168                 }
1169                 glt->dirty = false;
1170                 return glt->texnum;
1171         }
1172         else
1173                 return 0;
1174 }
1175
1176 void R_ClearTexture (rtexture_t *rt)
1177 {
1178         gltexture_t *glt = (gltexture_t *)rt;
1179
1180         R_Upload( glt, NULL, 0, 0, 0, glt->tilewidth, glt->tileheight, glt->tiledepth );
1181 }