]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - gl_textures.c
removed procedural texture support because it was inefficient where it was implemente...
[xonotic/darkplaces.git] / gl_textures.c
1
2 #include "quakedef.h"
3
4 cvar_t  r_max_size = {CVAR_SAVE, "r_max_size", "2048"};
5 cvar_t  r_max_scrapsize = {CVAR_SAVE, "r_max_scrapsize", "256"};
6 cvar_t  r_picmip = {CVAR_SAVE, "r_picmip", "0"};
7 cvar_t  r_lerpimages = {CVAR_SAVE, "r_lerpimages", "1"};
8 cvar_t  r_precachetextures = {CVAR_SAVE, "r_precachetextures", "1"};
9
10 int             gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
11 int             gl_filter_mag = GL_LINEAR;
12
13
14 static mempool_t *texturemempool;
15 static mempool_t *texturedatamempool;
16 static mempool_t *textureprocessingmempool;
17
18 // note: this must not conflict with TEXF_ flags in r_textures.h
19 // cleared when a texture is uploaded
20 #define GLTEXF_UPLOAD 0x00010000
21 // bitmask for mismatch checking
22 #define GLTEXF_IMPORTANTBITS (0)
23 // set when image is uploaded and freed
24 #define GLTEXF_DESTROYED 0x00040000
25
26 // size of images which hold fragment textures, ignores picmip and max_size
27 static int block_size;
28
29 // really this number only governs gltexnuminuse
30 #define MAX_GLTEXTURES 65536
31
32 // since there is only one set of GL texture numbers, we have to track them
33 // globally, everything else is per texture pool
34 static qbyte *gltexnuminuse;
35
36 typedef struct
37 {
38         int textype;
39         int inputbytesperpixel;
40         int internalbytesperpixel;
41         int glformat;
42         int glinternalformat;
43         int align;
44 }
45 textypeinfo_t;
46
47 static textypeinfo_t textype_qpalette       = {TEXTYPE_QPALETTE, 1, 4, GL_RGBA, 3, 1};
48 static textypeinfo_t textype_rgb            = {TEXTYPE_RGB     , 3, 3, GL_RGB , 3, 3};
49 static textypeinfo_t textype_rgba           = {TEXTYPE_RGBA    , 4, 4, GL_RGBA, 3, 1};
50 static textypeinfo_t textype_qpalette_alpha = {TEXTYPE_QPALETTE, 1, 4, GL_RGBA, 4, 1};
51 static textypeinfo_t textype_rgba_alpha     = {TEXTYPE_RGBA    , 4, 4, GL_RGBA, 4, 1};
52
53 // a tiling texture (most common type)
54 #define GLIMAGETYPE_TILE 0
55 // a fragments texture (contains one or more fragment textures)
56 #define GLIMAGETYPE_FRAGMENTS 1
57
58 // a gltextureimage can have one (or more if fragments) gltextures inside
59 typedef struct gltextureimage_s
60 {
61         struct gltextureimage_s *imagechain;
62         int texturecount;
63         int type; // one of the GLIMAGETYPE_ values
64         int texnum; // GL texture slot number
65         int width, height;
66         int bytesperpixel; // bytes per pixel
67         int glformat; // GL_RGB or GL_RGBA
68         int glinternalformat; // 3 or 4
69         int flags;
70         short *blockallocation; // fragment allocation
71 }
72 gltextureimage_t;
73
74 typedef struct gltexture_s
75 {
76         // pointer to texturepool (check this to see if the texture is allocated)
77         struct gltexturepool_s *pool;
78         // pointer to next texture in texturepool chain
79         struct gltexture_s *chain;
80         // pointer into gltextureimage array
81         gltextureimage_t *image;
82         // name of the texture (this might be removed someday), no duplicates
83         char *identifier;
84         // location in the image, and size
85         int x, y, width, height;
86         // copy of the original texture supplied to the upload function, for re-uploading or deferred uploads (non-precached)
87         qbyte *inputtexels;
88         // to identify cache mismatchs (this might be removed someday)
89         int crc;
90         // flags supplied to the LoadTexture function
91         // (might be altered to remove TEXF_ALPHA), and GLTEXF_ private flags
92         int flags;
93         // pointer to one of the textype_ structs
94         textypeinfo_t *textype;
95 }
96 gltexture_t;
97
98 #define TEXTUREPOOL_SENTINEL 0xC0DEDBAD
99
100 typedef struct gltexturepool_s
101 {
102         int sentinel;
103         struct gltextureimage_s *imagechain;
104         struct gltexture_s *gltchain;
105         struct gltexturepool_s *next;
106 }
107 gltexturepool_t;
108
109 static gltexturepool_t *gltexturepoolchain = NULL;
110
111 static qbyte *resizebuffer = NULL, *colorconvertbuffer;
112 static int resizebuffersize = 0;
113 static qbyte *texturebuffer;
114 static int texturebuffersize = 0;
115
116 static int realmaxsize = 0;
117
118 static textypeinfo_t *R_GetTexTypeInfo(int textype, int flags)
119 {
120         if (flags & TEXF_ALPHA)
121         {
122                 switch(textype)
123                 {
124                 case TEXTYPE_QPALETTE:
125                         return &textype_qpalette_alpha;
126                 case TEXTYPE_RGB:
127                         Host_Error("R_GetTexTypeInfo: RGB format has no alpha, TEXF_ALPHA not allowed\n");
128                         return NULL;
129                 case TEXTYPE_RGBA:
130                         return &textype_rgba_alpha;
131                 default:
132                         Host_Error("R_GetTexTypeInfo: unknown texture format\n");
133                         return NULL;
134                 }
135         }
136         else
137         {
138                 switch(textype)
139                 {
140                 case TEXTYPE_QPALETTE:
141                         return &textype_qpalette;
142                 case TEXTYPE_RGB:
143                         return &textype_rgb;
144                 case TEXTYPE_RGBA:
145                         return &textype_rgba;
146                 default:
147                         Host_Error("R_GetTexTypeInfo: unknown texture format\n");
148                         return NULL;
149                 }
150         }
151 }
152
153 static void R_UploadTexture(gltexture_t *t);
154
155 static void R_PrecacheTexture(gltexture_t *glt)
156 {
157         int precache;
158         precache = false;
159         if (glt->flags & TEXF_ALWAYSPRECACHE)
160                 precache = true;
161         else if (r_precachetextures.integer >= 2)
162                 precache = true;
163         else if (r_precachetextures.integer >= 1)
164                 if (glt->flags & TEXF_PRECACHE)
165                         precache = true;
166
167         if (precache)
168                 R_UploadTexture(glt);
169 }
170
171 int R_GetTexture(rtexture_t *rt)
172 {
173         gltexture_t *glt;
174         if (!rt)
175                 return 0;
176         glt = (gltexture_t *)rt;
177         if (glt->flags & GLTEXF_UPLOAD)
178                 R_UploadTexture(glt);
179         return glt->image->texnum;
180 }
181
182 void R_FreeTexture(rtexture_t *rt)
183 {
184         gltexture_t *glt, **gltpointer;
185         gltextureimage_t *image, **gltimagepointer;
186         GLuint texnum;
187
188         glt = (gltexture_t *)rt;
189         if (glt == NULL)
190                 Host_Error("R_FreeTexture: texture == NULL\n");
191
192         for (gltpointer = &glt->pool->gltchain;*gltpointer && *gltpointer != glt;gltpointer = &(*gltpointer)->chain);
193         if (*gltpointer == glt)
194                 *gltpointer = glt->chain;
195         else
196                 Host_Error("R_FreeTexture: texture \"%s\" not linked in pool\n", glt->identifier);
197
198         // note: if freeing a fragment texture, this will not make the claimed
199         // space available for new textures unless all other fragments in the
200         // image are also freed
201         image = glt->image;
202         image->texturecount--;
203         if (image->texturecount < 1)
204         {
205                 for (gltimagepointer = &glt->pool->imagechain;*gltimagepointer && *gltimagepointer != image;gltimagepointer = &(*gltimagepointer)->imagechain);
206                 if (*gltimagepointer == image)
207                         *gltimagepointer = image->imagechain;
208                 else
209                         Host_Error("R_FreeTexture: image not linked in pool\n");
210                 if (image->texnum)
211                 {
212                         texnum = image->texnum;
213                         gltexnuminuse[image->texnum] = 0;
214                         qglDeleteTextures(1, &texnum);
215                 }
216                 if (image->blockallocation)
217                         Mem_Free(image->blockallocation);
218                 Mem_Free(image);
219         }
220
221         if (glt->identifier)
222                 Mem_Free(glt->identifier);
223         if (glt->inputtexels)
224                 Mem_Free(glt->inputtexels);
225         Mem_Free(glt);
226 }
227
228 static gltexture_t *R_FindTexture (gltexturepool_t *pool, char *identifier)
229 {
230         gltexture_t     *glt;
231
232         if (!identifier)
233                 return NULL;
234
235         for (glt = pool->gltchain;glt;glt = glt->chain)
236                 if (glt->identifier && !strcmp (identifier, glt->identifier))
237                         return glt;
238
239         return NULL;
240 }
241
242 rtexturepool_t *R_AllocTexturePool(void)
243 {
244         gltexturepool_t *pool;
245         if (texturemempool == NULL)
246                 return NULL;
247         pool = Mem_Alloc(texturemempool, sizeof(gltexturepool_t));
248         if (pool == NULL)
249                 return NULL;
250         pool->next = gltexturepoolchain;
251         gltexturepoolchain = pool;
252         pool->sentinel = TEXTUREPOOL_SENTINEL;
253         return (rtexturepool_t *)pool;
254 }
255
256 void R_FreeTexturePool(rtexturepool_t **rtexturepool)
257 {
258         gltexturepool_t *pool, **poolpointer;
259         if (rtexturepool == NULL)
260                 return;
261         if (*rtexturepool == NULL)
262                 return;
263         pool = (gltexturepool_t *)(*rtexturepool);
264         *rtexturepool = NULL;
265         if (pool->sentinel != TEXTUREPOOL_SENTINEL)
266                 Host_Error("R_FreeTexturePool: pool already freed\n");
267         for (poolpointer = &gltexturepoolchain;*poolpointer && *poolpointer != pool;poolpointer = &(*poolpointer)->next);
268         if (*poolpointer == pool)
269                 *poolpointer = pool->next;
270         else
271                 Host_Error("R_FreeTexturePool: pool not linked\n");
272         while (pool->gltchain)
273                 R_FreeTexture((rtexture_t *)pool->gltchain);
274         if (pool->imagechain)
275                 Sys_Error("R_FreeTexturePool: not all images freed\n");
276         Mem_Free(pool);
277 }
278
279
280 typedef struct
281 {
282         char *name;
283         int minification, magnification;
284 }
285 glmode_t;
286
287 static glmode_t modes[] =
288 {
289         {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
290         {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
291         {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
292         {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
293         {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
294         {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
295 };
296
297 extern int gl_backend_rebindtextures;
298
299 static void GL_TextureMode_f (void)
300 {
301         int i;
302         gltextureimage_t *image;
303         gltexturepool_t *pool;
304
305         if (Cmd_Argc() == 1)
306         {
307                 for (i = 0;i < 6;i++)
308                 {
309                         if (gl_filter_min == modes[i].minification)
310                         {
311                                 Con_Printf ("%s\n", modes[i].name);
312                                 return;
313                         }
314                 }
315                 Con_Printf ("current filter is unknown???\n");
316                 return;
317         }
318
319         for (i = 0;i < 6;i++)
320                 if (!Q_strcasecmp (modes[i].name, Cmd_Argv(1) ) )
321                         break;
322         if (i == 6)
323         {
324                 Con_Printf ("bad filter name\n");
325                 return;
326         }
327
328         gl_filter_min = modes[i].minification;
329         gl_filter_mag = modes[i].magnification;
330
331         // change all the existing mipmap texture objects
332         // FIXME: force renderer(/client/something?) restart instead?
333         for (pool = gltexturepoolchain;pool;pool = pool->next)
334         {
335                 for (image = pool->imagechain;image;image = image->imagechain)
336                 {
337                         // only update already uploaded images
338                         if (!(image->flags & GLTEXF_UPLOAD))
339                         {
340                                 qglBindTexture(GL_TEXTURE_2D, image->texnum);
341                                 if (image->flags & TEXF_MIPMAP)
342                                         qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
343                                 else
344                                         qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_mag);
345                                 qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_mag);
346                         }
347                 }
348         }
349         gl_backend_rebindtextures = true;
350 }
351
352 static int R_CalcTexelDataSize (gltexture_t *glt)
353 {
354         int width2, height2, size;
355         if (glt->flags & TEXF_FRAGMENT)
356                 size = glt->width * glt->height;
357         else
358         {
359                 if (r_max_size.integer > realmaxsize)
360                         Cvar_SetValue("r_max_size", realmaxsize);
361                 // calculate final size
362                 for (width2 = 1;width2 < glt->width;width2 <<= 1);
363                 for (height2 = 1;height2 < glt->height;height2 <<= 1);
364                 for (width2 >>= r_picmip.integer;width2 > r_max_size.integer;width2 >>= 1);
365                 for (height2 >>= r_picmip.integer;height2 > r_max_size.integer;height2 >>= 1);
366                 if (width2 < 1) width2 = 1;
367                 if (height2 < 1) height2 = 1;
368
369                 size = 0;
370                 if (glt->flags & TEXF_MIPMAP)
371                 {
372                         while (width2 > 1 || height2 > 1)
373                         {
374                                 size += width2 * height2;
375                                 if (width2 > 1)
376                                         width2 >>= 1;
377                                 if (height2 > 1)
378                                         height2 >>= 1;
379                         }
380                         size++; // count the last 1x1 mipmap
381                 }
382                 else
383                         size = width2*height2;
384         }
385         size *= glt->textype->internalbytesperpixel;
386
387         return size;
388 }
389
390 void R_TextureStats_PrintTotal(void)
391 {
392         int glsize, inputsize, total = 0, totalt = 0, totalp = 0, loaded = 0, loadedt = 0, loadedp = 0;
393         gltexture_t *glt;
394         gltexturepool_t *pool;
395         for (pool = gltexturepoolchain;pool;pool = pool->next)
396         {
397                 for (glt = pool->gltchain;glt;glt = glt->chain)
398                 {
399                         glsize = R_CalcTexelDataSize(glt);
400                         inputsize = glt->width * glt->height * glt->textype->inputbytesperpixel;
401
402                         total++;
403                         totalt += glsize;
404                         totalp += inputsize;
405                         if (!(glt->flags & GLTEXF_UPLOAD))
406                         {
407                                 loaded++;
408                                 loadedt += glsize;
409                                 loadedp += inputsize;
410                         }
411                 }
412         }
413         Con_Printf("total: %i (%.3fMB, %.3fMB original), uploaded %i (%.3fMB, %.3fMB original), upload on demand %i (%.3fMB, %.3fMB original)\n", total, totalt / 1048576.0, totalp / 1048576.0, loaded, loadedt / 1048576.0, loadedp / 1048576.0, total - loaded, (totalt - loadedt) / 1048576.0, (totalp - loadedp) / 1048576.0);
414 }
415
416 static void R_TextureStats_f(void)
417 {
418         int loaded;
419         gltexture_t *glt;
420         gltexturepool_t *pool;
421         Con_Printf("glsize input crc  loaded mip alpha name\n");
422         for (pool = gltexturepoolchain;pool;pool = pool->next)
423         {
424                 for (glt = pool->gltchain;glt;glt = glt->chain)
425                 {
426                         loaded = !(glt->flags & GLTEXF_UPLOAD);
427                         Con_Printf("%c%4i%c%c%4i%c %04X %s %s %s %s\n", loaded ? '[' : ' ', (R_CalcTexelDataSize(glt) + 1023) / 1024, loaded ? ']' : ' ', glt->inputtexels ? '[' : ' ', (glt->width * glt->height * glt->textype->inputbytesperpixel + 1023) / 1024, glt->inputtexels ? ']' : ' ', glt->crc, loaded ? "loaded" : "      ", (glt->flags & TEXF_MIPMAP) ? "mip" : "   ", (glt->flags & TEXF_ALPHA) ? "alpha" : "     ", glt->identifier ? glt->identifier : "<unnamed>");
428                 }
429                 Con_Printf("pool %10p\n", pool);
430         }
431         R_TextureStats_PrintTotal();
432 }
433
434 char engineversion[40];
435
436 static void r_textures_start(void)
437 {
438         // deal with size limits of various drivers (3dfx in particular)
439         qglGetIntegerv(GL_MAX_TEXTURE_SIZE, &realmaxsize);
440         CHECKGLERROR
441
442         // use the largest scrap texture size we can (not sure if this is really a good idea)
443         for (block_size = 1;block_size < realmaxsize && block_size < r_max_scrapsize.integer;block_size <<= 1);
444
445         texturemempool = Mem_AllocPool("Texture Info");
446         texturedatamempool = Mem_AllocPool("Texture Storage (not yet uploaded)");
447         textureprocessingmempool = Mem_AllocPool("Texture Processing Buffers");
448         gltexnuminuse = Mem_Alloc(texturemempool, MAX_GLTEXTURES);
449 }
450
451 static void r_textures_shutdown(void)
452 {
453         rtexturepool_t *temp;
454         while(gltexturepoolchain)
455         {
456                 temp = (rtexturepool_t *) gltexturepoolchain;
457                 R_FreeTexturePool(&temp);
458         }
459
460         resizebuffersize = 0;
461         texturebuffersize = 0;
462         resizebuffer = NULL;
463         colorconvertbuffer = NULL;
464         texturebuffer = NULL;
465         gltexnuminuse = NULL;
466         Mem_FreePool(&texturemempool);
467         Mem_FreePool(&texturedatamempool);
468         Mem_FreePool(&textureprocessingmempool);
469 }
470
471 static void r_textures_newmap(void)
472 {
473 }
474
475 void R_Textures_Init (void)
476 {
477         Cmd_AddCommand ("gl_texturemode", &GL_TextureMode_f);
478         Cmd_AddCommand("r_texturestats", R_TextureStats_f);
479         Cvar_RegisterVariable (&r_max_scrapsize);
480         Cvar_RegisterVariable (&r_max_size);
481         Cvar_RegisterVariable (&r_picmip);
482         Cvar_RegisterVariable (&r_lerpimages);
483         Cvar_RegisterVariable (&r_precachetextures);
484         gltexnuminuse = NULL;
485
486         R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
487 }
488
489 static void R_Upload(gltexture_t *glt, qbyte *data)
490 {
491         int mip, width, height, internalformat;
492         qbyte *prevbuffer;
493         prevbuffer = data;
494
495         qglBindTexture(GL_TEXTURE_2D, glt->image->texnum);
496         CHECKGLERROR
497
498         gl_backend_rebindtextures = true;
499
500         glt->flags &= ~GLTEXF_UPLOAD;
501
502         if (glt->flags & TEXF_FRAGMENT)
503         {
504                 if (resizebuffersize < glt->image->width * glt->image->height * glt->image->bytesperpixel)
505                 {
506                         resizebuffersize = glt->image->width * glt->image->height * glt->image->bytesperpixel;
507                         if (resizebuffer)
508                                 Mem_Free(resizebuffer);
509                         if (colorconvertbuffer)
510                                 Mem_Free(colorconvertbuffer);
511                         resizebuffer = Mem_Alloc(textureprocessingmempool, resizebuffersize);
512                         colorconvertbuffer = Mem_Alloc(textureprocessingmempool, resizebuffersize);
513                         if (!resizebuffer || !colorconvertbuffer)
514                                 Host_Error("R_Upload: out of memory\n");
515                 }
516
517                 if (glt->image->flags & GLTEXF_UPLOAD)
518                 {
519                         Con_DPrintf("uploaded new fragments image\n");
520                         glt->image->flags &= ~GLTEXF_UPLOAD;
521                         memset(resizebuffer, 255, glt->image->width * glt->image->height * glt->image->bytesperpixel);
522                         qglTexImage2D (GL_TEXTURE_2D, 0, glt->image->glinternalformat, glt->image->width, glt->image->height, 0, glt->image->glformat, GL_UNSIGNED_BYTE, resizebuffer);
523                         CHECKGLERROR
524                         qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_mag);
525                         CHECKGLERROR
526                         qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_mag);
527                         CHECKGLERROR
528                 }
529
530                 if (prevbuffer == NULL)
531                 {
532                         memset(resizebuffer, 255, glt->width * glt->height * glt->image->bytesperpixel);
533                         prevbuffer = resizebuffer;
534                 }
535                 else if (glt->textype->textype == TEXTYPE_QPALETTE)
536                 {
537                         // promote paletted to RGBA, so we only have to worry about RGB and
538                         // RGBA in the rest of this code
539                         Image_Copy8bitRGBA(prevbuffer, colorconvertbuffer, glt->width * glt->height, d_8to24table);
540                         prevbuffer = colorconvertbuffer;
541                 }
542
543                 qglTexSubImage2D(GL_TEXTURE_2D, 0, glt->x, glt->y, glt->width, glt->height, glt->image->glformat, GL_UNSIGNED_BYTE, prevbuffer);
544                 CHECKGLERROR
545                 return;
546         }
547
548         glt->image->flags &= ~GLTEXF_UPLOAD;
549
550         // these are rounded up versions of the size to do better resampling
551         for (width = 1;width < glt->width;width <<= 1);
552         for (height = 1;height < glt->height;height <<= 1);
553
554         if (resizebuffersize < width * height * glt->image->bytesperpixel)
555         {
556                 resizebuffersize = width * height * glt->image->bytesperpixel;
557                 if (resizebuffer)
558                         Mem_Free(resizebuffer);
559                 if (colorconvertbuffer)
560                         Mem_Free(colorconvertbuffer);
561                 resizebuffer = Mem_Alloc(textureprocessingmempool, resizebuffersize);
562                 colorconvertbuffer = Mem_Alloc(textureprocessingmempool, resizebuffersize);
563                 if (!resizebuffer || !colorconvertbuffer)
564                         Host_Error("R_Upload: out of memory\n");
565         }
566
567         if (prevbuffer == NULL)
568         {
569                 width = glt->image->width;
570                 height = glt->image->height;
571                 memset(resizebuffer, 255, width * height * glt->image->bytesperpixel);
572                 prevbuffer = resizebuffer;
573         }
574         else
575         {
576                 if (glt->textype->textype == TEXTYPE_QPALETTE)
577                 {
578                         // promote paletted to RGBA, so we only have to worry about RGB and
579                         // RGBA in the rest of this code
580                         Image_Copy8bitRGBA(prevbuffer, colorconvertbuffer, glt->width * glt->height, d_8to24table);
581                         prevbuffer = colorconvertbuffer;
582                 }
583
584                 if (glt->width != width || glt->height != height)
585                 {
586                         Image_Resample(prevbuffer, glt->width, glt->height, resizebuffer, width, height, glt->image->bytesperpixel, r_lerpimages.integer);
587                         prevbuffer = resizebuffer;
588                 }
589
590                 // apply picmip/max_size limitations
591                 while (width > glt->image->width || height > glt->image->height)
592                 {
593                         Image_MipReduce(prevbuffer, resizebuffer, &width, &height, glt->image->width, glt->image->height, glt->image->bytesperpixel);
594                         prevbuffer = resizebuffer;
595                 }
596         }
597
598         // 3 and 4 are converted by the driver to it's preferred format for the current display mode
599         internalformat = 3;
600         if (glt->flags & TEXF_ALPHA)
601                 internalformat = 4;
602
603         mip = 0;
604         qglTexImage2D(GL_TEXTURE_2D, mip++, internalformat, width, height, 0, glt->image->glformat, GL_UNSIGNED_BYTE, prevbuffer);
605         CHECKGLERROR
606         if (glt->flags & TEXF_MIPMAP)
607         {
608                 while (width > 1 || height > 1)
609                 {
610                         Image_MipReduce(prevbuffer, resizebuffer, &width, &height, 1, 1, glt->image->bytesperpixel);
611                         prevbuffer = resizebuffer;
612
613                         qglTexImage2D(GL_TEXTURE_2D, mip++, internalformat, width, height, 0, glt->image->glformat, GL_UNSIGNED_BYTE, prevbuffer);
614                         CHECKGLERROR
615                 }
616
617                 qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
618                 CHECKGLERROR
619                 qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_mag);
620                 CHECKGLERROR
621         }
622         else
623         {
624                 qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_mag);
625                 CHECKGLERROR
626                 qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_mag);
627                 CHECKGLERROR
628         }
629 }
630
631 static void R_FindImageForTexture(gltexture_t *glt)
632 {
633         int i, j, best, best2, x, y, w, h;
634         textypeinfo_t *texinfo;
635         gltexturepool_t *pool;
636         gltextureimage_t *image, **imagechainpointer;
637         texinfo = glt->textype;
638         pool = glt->pool;
639
640         x = 0;
641         y = 0;
642         w = glt->width;
643         h = glt->height;
644         if (glt->flags & TEXF_FRAGMENT)
645         {
646                 for (imagechainpointer = &pool->imagechain;*imagechainpointer;imagechainpointer = &(*imagechainpointer)->imagechain)
647                 {
648                         image = *imagechainpointer;
649                         if (image->type != GLIMAGETYPE_FRAGMENTS)
650                                 continue;
651                         if (image->glformat != texinfo->glformat || image->glinternalformat != texinfo->glinternalformat)
652                                 continue;
653                         if (glt->width > image->width || glt->height > image->height)
654                                 continue;
655
656                         // got a fragments texture, find a place in it if we can
657                         for (best = image->width, i = 0;i < image->width - w;i += texinfo->align)
658                         {
659                                 for (best2 = 0, j = 0;j < w;j++)
660                                 {
661                                         if (image->blockallocation[i+j] >= best)
662                                                 break;
663                                         if (best2 < image->blockallocation[i+j])
664                                                 best2 = image->blockallocation[i+j];
665                                 }
666                                 if (j == w)
667                                 {
668                                         // this is a valid spot
669                                         x = i;
670                                         y = best = best2;
671                                 }
672                         }
673
674                         if (best + h > image->height)
675                                 continue;
676
677                         for (i = 0;i < w;i++)
678                                 image->blockallocation[x + i] = best + h;
679
680                         glt->x = x;
681                         glt->y = y;
682                         glt->image = image;
683                         image->texturecount++;
684                         return;
685                 }
686
687                 image = Mem_Alloc(texturemempool, sizeof(gltextureimage_t));
688                 if (image == NULL)
689                         Sys_Error("R_FindImageForTexture: ran out of memory\n");
690                 image->type = GLIMAGETYPE_FRAGMENTS;
691                 // make sure the created image is big enough for the fragment
692                 for (image->width = block_size;image->width < glt->width;image->width <<= 1);
693                 for (image->height = block_size;image->height < glt->height;image->height <<= 1);
694                 image->blockallocation = Mem_Alloc(texturemempool, image->width * sizeof(short));
695                 memset(image->blockallocation, 0, image->width * sizeof(short));
696
697                 x = 0;
698                 y = 0;
699                 for (i = 0;i < w;i++)
700                         image->blockallocation[x + i] = y + h;
701         }
702         else
703         {
704                 for (imagechainpointer = &pool->imagechain;*imagechainpointer;imagechainpointer = &(*imagechainpointer)->imagechain);
705
706                 image = Mem_Alloc(texturemempool, sizeof(gltextureimage_t));
707                 if (image == NULL)
708                         Sys_Error("R_FindImageForTexture: ran out of memory\n");
709                 image->type = GLIMAGETYPE_TILE;
710                 image->blockallocation = NULL;
711
712                 // calculate final size
713                 if (r_max_size.integer > realmaxsize)
714                         Cvar_SetValue("r_max_size", realmaxsize);
715                 for (image->width = 1;image->width < glt->width;image->width <<= 1);
716                 for (image->height = 1;image->height < glt->height;image->height <<= 1);
717                 for (image->width >>= r_picmip.integer;image->width > r_max_size.integer;image->width >>= 1);
718                 for (image->height >>= r_picmip.integer;image->height > r_max_size.integer;image->height >>= 1);
719                 if (image->width < 1) image->width = 1;
720                 if (image->height < 1) image->height = 1;
721         }
722         image->glinternalformat = texinfo->glinternalformat;
723         image->glformat = texinfo->glformat;
724         image->flags = (glt->flags & (TEXF_MIPMAP | TEXF_ALPHA)) | GLTEXF_UPLOAD;
725         image->bytesperpixel = texinfo->internalbytesperpixel;
726         for (i = 1;i < MAX_GLTEXTURES;i++)
727                 if (!gltexnuminuse[i])
728                         break;
729         if (i < MAX_GLTEXTURES)
730                 gltexnuminuse[image->texnum = i] = true;
731         else
732                 Sys_Error("R_FindImageForTexture: ran out of GL textures\n");
733         *imagechainpointer = image;
734         image->texturecount++;
735
736         glt->x = x;
737         glt->y = y;
738         glt->image = image;
739 }
740
741 // note: R_FindImageForTexture must be called before this
742 static void R_UploadTexture (gltexture_t *glt)
743 {
744         if (!(glt->flags & GLTEXF_UPLOAD))
745                 return;
746
747         R_Upload(glt, glt->inputtexels);
748         if (glt->inputtexels)
749         {
750                 Mem_Free(glt->inputtexels);
751                 glt->inputtexels = NULL;
752                 glt->flags |= GLTEXF_DESTROYED;
753         }
754         else if (glt->flags & GLTEXF_DESTROYED)
755                 Con_Printf("R_UploadTexture: Texture %s already uploaded and destroyed.  Can not upload original image again.  Uploaded blank texture.\n", glt->identifier);
756 }
757
758 static gltexture_t *R_SetupTexture(gltexturepool_t *pool, char *identifier, int crc, int width, int height, int flags, textypeinfo_t *texinfo, qbyte *data)
759 {
760         gltexture_t *glt;
761         glt = Mem_Alloc(texturemempool, sizeof(gltexture_t));
762         if (identifier)
763         {
764                 glt->identifier = Mem_Alloc(texturemempool, strlen(identifier)+1);
765                 strcpy (glt->identifier, identifier);
766         }
767         else
768                 glt->identifier = NULL;
769         glt->pool = pool;
770         glt->chain = pool->gltchain;
771         pool->gltchain = glt;
772         glt->crc = crc;
773         glt->width = width;
774         glt->height = height;
775         glt->flags = flags;
776         glt->textype = texinfo;
777
778         if (data)
779         {
780                 glt->inputtexels = Mem_Alloc(texturedatamempool, glt->width * glt->height * texinfo->inputbytesperpixel);
781                 if (glt->inputtexels == NULL)
782                         Sys_Error("R_SetupTexture: out of memory\n");
783                 memcpy(glt->inputtexels, data, glt->width * glt->height * texinfo->inputbytesperpixel);
784         }
785         else
786                 glt->inputtexels = NULL;
787
788         R_FindImageForTexture(glt);
789         R_PrecacheTexture(glt);
790
791         return glt;
792 }
793
794 /*
795 ================
796 R_LoadTexture
797 ================
798 */
799 rtexture_t *R_LoadTexture (rtexturepool_t *rtexturepool, char *identifier, int width, int height, qbyte *data, int textype, int flags)
800 {
801         int i;
802         gltexture_t *glt;
803         gltexturepool_t *pool = (gltexturepool_t *)rtexturepool;
804         textypeinfo_t *texinfo;
805         unsigned short crc;
806
807         if (cls.state == ca_dedicated)
808                 return NULL;
809
810         texinfo = R_GetTexTypeInfo(textype, flags);
811
812         if (flags & TEXF_FRAGMENT)
813                 if ((width * texinfo->internalbytesperpixel) & 3)
814                         Host_Error("R_LoadTexture: incompatible width for fragment");
815
816         // clear the alpha flag if the texture has no transparent pixels
817         switch(textype)
818         {
819         case TEXTYPE_QPALETTE:
820                 if (flags & TEXF_ALPHA)
821                 {
822                         flags &= ~TEXF_ALPHA;
823                         for (i = 0;i < width * height;i++)
824                         {
825                                 if (data[i] == 255)
826                                 {
827                                         flags |= TEXF_ALPHA;
828                                         break;
829                                 }
830                         }
831                 }
832                 break;
833         case TEXTYPE_RGB:
834                 if (flags & TEXF_ALPHA)
835                         Host_Error("R_LoadTexture: RGB has no alpha, don't specify TEXF_ALPHA\n");
836                 break;
837         case TEXTYPE_RGBA:
838                 if (flags & TEXF_ALPHA)
839                 {
840                         flags &= ~TEXF_ALPHA;
841                         for (i = 0;i < width * height;i++)
842                         {
843                                 if (data[i * 4 + 3] < 255)
844                                 {
845                                         flags |= TEXF_ALPHA;
846                                         break;
847                                 }
848                         }
849                 }
850                 break;
851         default:
852                 Host_Error("R_LoadTexture: unknown texture type\n");
853         }
854
855         // LordHavoc: do a CRC to confirm the data really is the same as previous occurances.
856         if (data == NULL)
857                 crc = 0;
858         else
859                 crc = CRC_Block(data, width*height*texinfo->inputbytesperpixel);
860
861         // see if the texture is already present
862         if (identifier && (glt = R_FindTexture(pool, identifier)))
863         {
864                 if (crc == glt->crc && width == glt->width && height == glt->height && texinfo == glt->textype && ((flags ^ glt->flags) & TEXF_IMPORTANTBITS) == 0 && ((flags ^ glt->flags) & GLTEXF_IMPORTANTBITS) == 0)
865                 {
866                         Con_Printf("R_LoadTexture: exact match with existing texture %s\n", identifier);
867                         return (rtexture_t *)glt; // exact match, use existing
868                 }
869                 Con_Printf("R_LoadTexture: cache mismatch on %s, replacing old texture\n", identifier);
870                 R_FreeTexture((rtexture_t *)glt);
871         }
872
873         return (rtexture_t *)R_SetupTexture(pool, identifier, crc, width, height, flags | GLTEXF_UPLOAD, texinfo, data);
874 }
875
876 int R_TextureHasAlpha(rtexture_t *rt)
877 {
878         gltexture_t *glt;
879         if (!rt)
880                 return false;
881         glt = (gltexture_t *)rt;
882         return (glt->flags & TEXF_ALPHA) != 0;
883 }
884
885 int R_TextureWidth(rtexture_t *rt)
886 {
887         if (!rt)
888                 return false;
889         return ((gltexture_t *)rt)->width;
890 }
891
892 int R_TextureHeight(rtexture_t *rt)
893 {
894         if (!rt)
895                 return false;
896         return ((gltexture_t *)rt)->height;
897 }
898
899 void R_FragmentLocation(rtexture_t *rt, int *x, int *y, float *fx1, float *fy1, float *fx2, float *fy2)
900 {
901         gltexture_t *glt;
902         float iwidth, iheight;
903         if (cls.state == ca_dedicated)
904         {
905                 if (x)
906                         *x = 0;
907                 if (y)
908                         *y = 0;
909                 if (fx1 || fy1 || fx2 || fy2)
910                 {
911                         if (fx1)
912                                 *fx1 = 0;
913                         if (fy1)
914                                 *fy1 = 0;
915                         if (fx2)
916                                 *fx2 = 1;
917                         if (fy2)
918                                 *fy2 = 1;
919                 }
920                 return;
921         }
922         if (!rt)
923                 Host_Error("R_FragmentLocation: no texture supplied\n");
924         glt = (gltexture_t *)rt;
925         if (glt->flags & TEXF_FRAGMENT)
926         {
927                 if (x)
928                         *x = glt->x;
929                 if (y)
930                         *y = glt->y;
931                 if (fx1 || fy1 || fx2 || fy2)
932                 {
933                         iwidth = 1.0f / glt->image->width;
934                         iheight = 1.0f / glt->image->height;
935                         if (fx1)
936                                 *fx1 = glt->x * iwidth;
937                         if (fy1)
938                                 *fy1 = glt->y * iheight;
939                         if (fx2)
940                                 *fx2 = (glt->x + glt->width) * iwidth;
941                         if (fy2)
942                                 *fy2 = (glt->y + glt->height) * iheight;
943                 }
944         }
945         else
946         {
947                 if (x)
948                         *x = 0;
949                 if (y)
950                         *y = 0;
951                 if (fx1 || fy1 || fx2 || fy2)
952                 {
953                         if (fx1)
954                                 *fx1 = 0;
955                         if (fy1)
956                                 *fy1 = 0;
957                         if (fx2)
958                                 *fx2 = 1;
959                         if (fy2)
960                                 *fy2 = 1;
961                 }
962         }
963 }
964
965 int R_CompatibleFragmentWidth(int width, int textype, int flags)
966 {
967         textypeinfo_t *texinfo = R_GetTexTypeInfo(textype, flags);
968         while ((width * texinfo->internalbytesperpixel) & 3)
969                 width++;
970         return width;
971 }
972
973 void R_UpdateTexture(rtexture_t *rt, qbyte *data)
974 {
975         gltexture_t *glt;
976         if (rt == NULL)
977                 Host_Error("R_UpdateTexture: no texture supplied\n");
978         if (data == NULL)
979                 Host_Error("R_UpdateTexture: no data supplied\n");
980         glt = (gltexture_t *)rt;
981
982         // if it has not been uploaded yet, update the data that will be used when it is
983         if (glt->inputtexels)
984                 memcpy(glt->inputtexels, data, glt->width * glt->height * glt->textype->inputbytesperpixel);
985         else
986                 R_Upload(glt, data);
987 }
988