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