]> git.xonotic.org Git - xonotic/darkplaces.git/blob - gl_textures.c
5ac0614f19908c6b9cc6ca3e2fb30d358af0474f
[xonotic/darkplaces.git] / gl_textures.c
1 #include "quakedef.h"
2
3 cvar_t          r_max_size = {0, "r_max_size", "2048"};
4 cvar_t          r_picmip = {0, "r_picmip", "0"};
5 cvar_t          r_lerpimages = {CVAR_SAVE, "r_lerpimages", "1"};
6 cvar_t          r_upload = {0, "r_upload", "1"};
7 cvar_t          r_precachetextures = {CVAR_SAVE, "r_precachetextures", "1"};
8
9 int             gl_filter_min = GL_LINEAR_MIPMAP_LINEAR; //NEAREST;
10 int             gl_filter_max = GL_LINEAR;
11
12
13 int             texels;
14
15 // 65536x65536
16 #define MAXMIPS 16
17
18 #define GLTEXF_LERPED 1
19 #define GLTEXF_UPLOADED 2
20
21 typedef struct
22 {
23         char    identifier[64];
24         int             texnum; // GL texture slot number
25         int             texeldatasize; // computed memory usage of this texture (including mipmaps, expansion to 32bit, etc)
26         byte    *inputtexels; // copy of the original texture supplied to the upload function, for re-uploading or deferred uploads (non-precached)
27         int             inputtexeldatasize; // size of the original texture
28         unsigned short width, height;
29 // LordHavoc: CRC to identify cache mismatchs
30         unsigned short crc;
31         int flags; // the requested flags when the texture was supplied to the upload function
32         int internalflags; // internal notes (lerped, etc)
33 } gltexture_t;
34
35 #define MAX_GLTEXTURES  4096
36 gltexture_t     *gltextures;
37 unsigned int numgltextures = 0, gl_texture_number = 1;
38
39 void GL_UploadTexture(gltexture_t *t);
40
41 int R_GetTexture(rtexture_t *rt)
42 {
43         gltexture_t *glt;
44         if (!rt)
45                 return 0;
46         glt = (gltexture_t *)rt;
47         if (!(glt->internalflags & GLTEXF_UPLOADED))
48         {
49                 GL_UploadTexture(glt);
50                 if (!(glt->internalflags & GLTEXF_UPLOADED))
51                         Host_Error("R_GetTexture: unable to upload texture\n");
52         }
53         return glt->texnum;
54 }
55
56 typedef struct
57 {
58         char *name;
59         int     minimize, maximize;
60 } glmode_t;
61
62 glmode_t modes[] =
63 {
64         {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
65         {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
66         {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
67         {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
68         {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
69         {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
70 };
71
72 /*
73 ===============
74 Draw_TextureMode_f
75 ===============
76 */
77 void Draw_TextureMode_f (void)
78 {
79         int             i;
80         gltexture_t     *glt;
81
82         if (Cmd_Argc() == 1)
83         {
84                 for (i=0 ; i< 6 ; i++)
85                         if (gl_filter_min == modes[i].minimize)
86                         {
87                                 Con_Printf ("%s\n", modes[i].name);
88                                 return;
89                         }
90                 Con_Printf ("current filter is unknown???\n");
91                 return;
92         }
93
94         for (i=0 ; i< 6 ; i++)
95         {
96                 if (!Q_strcasecmp (modes[i].name, Cmd_Argv(1) ) )
97                         break;
98         }
99         if (i == 6)
100         {
101                 Con_Printf ("bad filter name\n");
102                 return;
103         }
104
105         gl_filter_min = modes[i].minimize;
106         gl_filter_max = modes[i].maximize;
107
108         if (!r_upload.value)
109                 return;
110         // change all the existing mipmap texture objects
111         for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
112         {
113                 if (glt->flags & TEXF_MIPMAP)
114                 {
115                         glBindTexture(GL_TEXTURE_2D, glt->texnum);
116                         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
117                         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
118                 }
119         }
120 }
121
122 void GL_TextureStats_Print(char *name, int total, int total2, int loaded, int crc, int mip, int alpha, int total2valid)
123 {
124         if (!name[0])
125                 name = "<unnamed>";
126         Con_Printf("%5iK %c%5iK%c %04X %s %s %s %s\n", total, total2valid ? ' ' : '(', total2, total2valid ? ' ' : ')', crc, loaded ? "loaded" : "      ", mip ? "mip" : "   ", alpha ? "alpha" : "     ", name);
127 }
128
129 void GL_TextureStats_PrintTotal(void)
130 {
131         int i, t = 0, p = 0, loaded = 0, loadedt = 0, loadedp = 0;
132         gltexture_t *glt;
133         for (i = 0, glt = gltextures;i < numgltextures;i++, glt++)
134         {
135                 t += glt->texeldatasize;
136                 p += glt->inputtexeldatasize;
137                 if (glt->internalflags & GLTEXF_UPLOADED)
138                 {
139                         loaded++;
140                         loadedt += glt->texeldatasize;
141                         loadedp += glt->inputtexeldatasize;
142                 }
143         }
144         Con_Printf("total: %i (%.3fMB, %.3fMB original), uploaded %i (%.3fMB, %.3fMB original), upload on demand %i (%.3fMB, %.3fMB original)\n", numgltextures, t / 1048576.0, p / 1048576.0, loaded, loadedt / 1048576.0, loadedp / 1048576.0, numgltextures - loaded, (t - loadedt) / 1048576.0, (p - loadedp) / 1048576.0);
145 }
146
147 void GL_TextureStats_f(void)
148 {
149         int i;
150         gltexture_t *glt;
151         Con_Printf("kbytes original crc  loaded mip alpha name\n");
152         for (i = 0, glt = gltextures;i < numgltextures;i++, glt++)
153                 GL_TextureStats_Print(glt->identifier, (glt->texeldatasize + 1023) / 1024, (glt->inputtexeldatasize + 1023) / 1024, glt->internalflags & GLTEXF_UPLOADED, glt->crc, glt->flags & TEXF_MIPMAP, glt->flags & TEXF_ALPHA, glt->inputtexels != NULL);
154         GL_TextureStats_PrintTotal();
155 }
156
157 char engineversion[40];
158
159 //void GL_UploadTexture (gltexture_t *glt);
160 void r_textures_start(void)
161 {
162 //      int i;
163 //      gltexture_t *glt;
164 //      for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
165 //              GL_UploadTexture(glt);
166 }
167
168 void r_textures_shutdown(void)
169 {
170 }
171
172 void r_textures_newmap(void)
173 {
174 }
175
176 void R_Textures_Init (void)
177 {
178         Cmd_AddCommand("r_texturestats", GL_TextureStats_f);
179         Cvar_RegisterVariable (&r_max_size);
180         Cvar_RegisterVariable (&r_picmip);
181         Cvar_RegisterVariable (&r_lerpimages);
182         Cvar_RegisterVariable (&r_upload);
183         Cvar_RegisterVariable (&r_precachetextures);
184 #ifdef NORENDER
185         r_upload.value = 0;
186 #endif
187
188         // 3dfx can only handle 256 wide textures
189         if (!Q_strncasecmp ((char *)gl_renderer, "3dfx",4) || strstr((char *)gl_renderer, "Glide"))
190                 Cvar_Set ("r_max_size", "256");
191
192         gltextures = qmalloc(sizeof(gltexture_t) * MAX_GLTEXTURES);
193         memset(gltextures, 0, sizeof(gltexture_t) * MAX_GLTEXTURES);
194         Cmd_AddCommand ("gl_texturemode", &Draw_TextureMode_f);
195
196         R_RegisterModule("R_Textures", r_textures_start, r_textures_shutdown, r_textures_newmap);
197 }
198
199 /*
200 ================
201 R_FindTexture
202 ================
203 */
204 int R_FindTexture (char *identifier)
205 {
206         int             i;
207         gltexture_t     *glt;
208
209         for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
210         {
211                 if (!strcmp (identifier, glt->identifier))
212                         return gltextures[i].texnum;
213         }
214
215         return -1;
216 }
217
218 void R_ResampleTextureLerpLine (byte *in, byte *out, int inwidth, int outwidth)
219 {
220         int             j, xi, oldx = 0, f, fstep, endx;
221         fstep = (int) (inwidth*65536.0f/outwidth);
222         endx = (inwidth-1);
223         for (j = 0,f = 0;j < outwidth;j++, f += fstep)
224         {
225                 xi = (int) f >> 16;
226                 if (xi != oldx)
227                 {
228                         in += (xi - oldx) * 4;
229                         oldx = xi;
230                 }
231                 if (xi < endx)
232                 {
233                         int lerp = f & 0xFFFF;
234                         *out++ = (byte) ((((in[4] - in[0]) * lerp) >> 16) + in[0]);
235                         *out++ = (byte) ((((in[5] - in[1]) * lerp) >> 16) + in[1]);
236                         *out++ = (byte) ((((in[6] - in[2]) * lerp) >> 16) + in[2]);
237                         *out++ = (byte) ((((in[7] - in[3]) * lerp) >> 16) + in[3]);
238                 }
239                 else // last pixel of the line has no pixel to lerp to
240                 {
241                         *out++ = in[0];
242                         *out++ = in[1];
243                         *out++ = in[2];
244                         *out++ = in[3];
245                 }
246         }
247 }
248
249 /*
250 ================
251 R_ResampleTexture
252 ================
253 */
254 void R_ResampleTexture (void *indata, int inwidth, int inheight, void *outdata,  int outwidth, int outheight)
255 {
256         if (r_lerpimages.value)
257         {
258                 int             i, j, yi, oldy, f, fstep, endy = (inheight-1);
259                 byte    *inrow, *out, *row1, *row2;
260                 out = outdata;
261                 fstep = (int) (inheight*65536.0f/outheight);
262
263                 row1 = qmalloc(outwidth*4);
264                 row2 = qmalloc(outwidth*4);
265                 inrow = indata;
266                 oldy = 0;
267                 R_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
268                 R_ResampleTextureLerpLine (inrow + inwidth*4, row2, inwidth, outwidth);
269                 for (i = 0, f = 0;i < outheight;i++,f += fstep)
270                 {
271                         yi = f >> 16;
272                         if (yi < endy)
273                         {
274                                 int lerp = f & 0xFFFF;
275                                 if (yi != oldy)
276                                 {
277                                         inrow = (byte *)indata + inwidth*4*yi;
278                                         if (yi == oldy+1)
279                                                 memcpy(row1, row2, outwidth*4);
280                                         else
281                                                 R_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
282                                         R_ResampleTextureLerpLine (inrow + inwidth*4, row2, inwidth, outwidth);
283                                         oldy = yi;
284                                 }
285                                 j = outwidth - 4;
286                                 while(j >= 0)
287                                 {
288                                         out[ 0] = (byte) ((((row2[ 0] - row1[ 0]) * lerp) >> 16) + row1[ 0]);
289                                         out[ 1] = (byte) ((((row2[ 1] - row1[ 1]) * lerp) >> 16) + row1[ 1]);
290                                         out[ 2] = (byte) ((((row2[ 2] - row1[ 2]) * lerp) >> 16) + row1[ 2]);
291                                         out[ 3] = (byte) ((((row2[ 3] - row1[ 3]) * lerp) >> 16) + row1[ 3]);
292                                         out[ 4] = (byte) ((((row2[ 4] - row1[ 4]) * lerp) >> 16) + row1[ 4]);
293                                         out[ 5] = (byte) ((((row2[ 5] - row1[ 5]) * lerp) >> 16) + row1[ 5]);
294                                         out[ 6] = (byte) ((((row2[ 6] - row1[ 6]) * lerp) >> 16) + row1[ 6]);
295                                         out[ 7] = (byte) ((((row2[ 7] - row1[ 7]) * lerp) >> 16) + row1[ 7]);
296                                         out[ 8] = (byte) ((((row2[ 8] - row1[ 8]) * lerp) >> 16) + row1[ 8]);
297                                         out[ 9] = (byte) ((((row2[ 9] - row1[ 9]) * lerp) >> 16) + row1[ 9]);
298                                         out[10] = (byte) ((((row2[10] - row1[10]) * lerp) >> 16) + row1[10]);
299                                         out[11] = (byte) ((((row2[11] - row1[11]) * lerp) >> 16) + row1[11]);
300                                         out[12] = (byte) ((((row2[12] - row1[12]) * lerp) >> 16) + row1[12]);
301                                         out[13] = (byte) ((((row2[13] - row1[13]) * lerp) >> 16) + row1[13]);
302                                         out[14] = (byte) ((((row2[14] - row1[14]) * lerp) >> 16) + row1[14]);
303                                         out[15] = (byte) ((((row2[15] - row1[15]) * lerp) >> 16) + row1[15]);
304                                         out += 16;
305                                         row1 += 16;
306                                         row2 += 16;
307                                         j -= 4;
308                                 }
309                                 if (j & 2)
310                                 {
311                                         out[ 0] = (byte) ((((row2[ 0] - row1[ 0]) * lerp) >> 16) + row1[ 0]);
312                                         out[ 1] = (byte) ((((row2[ 1] - row1[ 1]) * lerp) >> 16) + row1[ 1]);
313                                         out[ 2] = (byte) ((((row2[ 2] - row1[ 2]) * lerp) >> 16) + row1[ 2]);
314                                         out[ 3] = (byte) ((((row2[ 3] - row1[ 3]) * lerp) >> 16) + row1[ 3]);
315                                         out[ 4] = (byte) ((((row2[ 4] - row1[ 4]) * lerp) >> 16) + row1[ 4]);
316                                         out[ 5] = (byte) ((((row2[ 5] - row1[ 5]) * lerp) >> 16) + row1[ 5]);
317                                         out[ 6] = (byte) ((((row2[ 6] - row1[ 6]) * lerp) >> 16) + row1[ 6]);
318                                         out[ 7] = (byte) ((((row2[ 7] - row1[ 7]) * lerp) >> 16) + row1[ 7]);
319                                         out += 8;
320                                         row1 += 8;
321                                         row2 += 8;
322                                 }
323                                 if (j & 1)
324                                 {
325                                         out[ 0] = (byte) ((((row2[ 0] - row1[ 0]) * lerp) >> 16) + row1[ 0]);
326                                         out[ 1] = (byte) ((((row2[ 1] - row1[ 1]) * lerp) >> 16) + row1[ 1]);
327                                         out[ 2] = (byte) ((((row2[ 2] - row1[ 2]) * lerp) >> 16) + row1[ 2]);
328                                         out[ 3] = (byte) ((((row2[ 3] - row1[ 3]) * lerp) >> 16) + row1[ 3]);
329                                         out += 4;
330                                         row1 += 4;
331                                         row2 += 4;
332                                 }
333                                 row1 -= outwidth*4;
334                                 row2 -= outwidth*4;
335                         }
336                         else
337                         {
338                                 if (yi != oldy)
339                                 {
340                                         inrow = (byte *)indata + inwidth*4*yi;
341                                         if (yi == oldy+1)
342                                                 memcpy(row1, row2, outwidth*4);
343                                         else
344                                                 R_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
345                                         oldy = yi;
346                                 }
347                                 memcpy(out, row1, outwidth * 4);
348                         }
349                 }
350                 qfree(row1);
351                 qfree(row2);
352         }
353         else
354         {
355                 int i, j;
356                 unsigned frac, fracstep;
357                 // relies on int being 4 bytes
358                 int *inrow, *out;
359                 out = outdata;
360
361                 fracstep = inwidth*0x10000/outwidth;
362                 for (i = 0;i < outheight;i++)
363                 {
364                         inrow = (int *)indata + inwidth*(i*inheight/outheight);
365                         frac = fracstep >> 1;
366                         j = outwidth - 4;
367                         while (j >= 0)
368                         {
369                                 out[0] = inrow[frac >> 16];frac += fracstep;
370                                 out[1] = inrow[frac >> 16];frac += fracstep;
371                                 out[2] = inrow[frac >> 16];frac += fracstep;
372                                 out[3] = inrow[frac >> 16];frac += fracstep;
373                                 out += 4;
374                                 j -= 4;
375                         }
376                         if (j & 2)
377                         {
378                                 out[0] = inrow[frac >> 16];frac += fracstep;
379                                 out[1] = inrow[frac >> 16];frac += fracstep;
380                                 out += 2;
381                         }
382                         if (j & 1)
383                         {
384                                 out[0] = inrow[frac >> 16];frac += fracstep;
385                                 out += 1;
386                         }
387                 }
388         }
389 }
390
391 // in can be the same as out
392 void GL_MipReduce(byte *in, byte *out, int width, int height, int destwidth, int destheight)
393 {
394         int x, y, width2, height2, nextrow;
395         if (width > destwidth)
396         {
397                 if (height > destheight)
398                 {
399                         // reduce both
400                         width2 = width >> 1;
401                         height2 = height >> 1;
402                         nextrow = width << 2;
403                         for (y = 0;y < height2;y++)
404                         {
405                                 for (x = 0;x < width2;x++)
406                                 {
407                                         out[0] = (byte) ((in[0] + in[4] + in[nextrow  ] + in[nextrow+4]) >> 2);
408                                         out[1] = (byte) ((in[1] + in[5] + in[nextrow+1] + in[nextrow+5]) >> 2);
409                                         out[2] = (byte) ((in[2] + in[6] + in[nextrow+2] + in[nextrow+6]) >> 2);
410                                         out[3] = (byte) ((in[3] + in[7] + in[nextrow+3] + in[nextrow+7]) >> 2);
411                                         out += 4;
412                                         in += 8;
413                                 }
414                                 in += nextrow; // skip a line
415                         }
416                 }
417                 else
418                 {
419                         // reduce width
420                         width2 = width >> 1;
421                         for (y = 0;y < height;y++)
422                         {
423                                 for (x = 0;x < width2;x++)
424                                 {
425                                         out[0] = (byte) ((in[0] + in[4]) >> 1);
426                                         out[1] = (byte) ((in[1] + in[5]) >> 1);
427                                         out[2] = (byte) ((in[2] + in[6]) >> 1);
428                                         out[3] = (byte) ((in[3] + in[7]) >> 1);
429                                         out += 4;
430                                         in += 8;
431                                 }
432                         }
433                 }
434         }
435         else
436         {
437                 if (height > destheight)
438                 {
439                         // reduce height
440                         height2 = height >> 1;
441                         nextrow = width << 2;
442                         for (y = 0;y < height2;y++)
443                         {
444                                 for (x = 0;x < width;x++)
445                                 {
446                                         out[0] = (byte) ((in[0] + in[nextrow  ]) >> 1);
447                                         out[1] = (byte) ((in[1] + in[nextrow+1]) >> 1);
448                                         out[2] = (byte) ((in[2] + in[nextrow+2]) >> 1);
449                                         out[3] = (byte) ((in[3] + in[nextrow+3]) >> 1);
450                                         out += 4;
451                                         in += 4;
452                                 }
453                                 in += nextrow; // skip a line
454                         }
455                 }
456                 else
457                         Sys_Error("GL_MipReduce: desired size already achieved\n");
458         }
459 }
460
461 void GL_Upload32(int glslot, byte *data, int width, int height, int flags)
462 {
463         int mip, width2, height2, width3, height3, internalformat;
464         byte *gammadata, *buffer;
465
466         if (!r_upload.value)
467                 return;
468
469         // 3 and 4 are converted by the driver to it's preferred format for the current display mode
470         internalformat = 3;
471         if (flags & TEXF_ALPHA)
472                 internalformat = 4;
473
474         // calculate power of 2 size
475         width2 = 1;while (width2 < width) width2 <<= 1;
476         height2 = 1;while (height2 < height) height2 <<= 1;
477         // calculate final size (mipmapped downward to this)
478         width3 = width2 >> (int) r_picmip.value;
479         height3 = height2 >> (int) r_picmip.value;
480         while (width3 > (int) r_max_size.value) width3 >>= 1;
481         while (height3 > (int) r_max_size.value) height3 >>= 1;
482         if (width3 < 1) width3 = 1;
483         if (height3 < 1) height3 = 1;
484
485         gammadata = qmalloc(width*height*4);
486         buffer = qmalloc(width2*height2*4);
487         if (!gammadata || !buffer)
488                 Host_Error("GL_Upload32: out of memory\n");
489
490         Image_CopyRGBAGamma(data, gammadata, width*height);
491
492         R_ResampleTexture(gammadata, width, height, buffer, width2, height2);
493
494         qfree(gammadata);
495
496         while (width2 > width3 || height2 > height3)
497         {
498                 GL_MipReduce(buffer, buffer, width2, height2, width3, height3);
499
500                 if (width2 > width3)
501                         width2 >>= 1;
502                 if (height2 > height3)
503                         height2 >>= 1;
504         }
505
506         glBindTexture(GL_TEXTURE_2D, glslot);
507         mip = 0;
508         glTexImage2D(GL_TEXTURE_2D, mip++, internalformat, width2, height2, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
509         if (flags & TEXF_MIPMAP)
510         {
511                 while (width2 > 1 || height2 > 1)
512                 {
513                         GL_MipReduce(buffer, buffer, width2, height2, 1, 1);
514
515                         if (width2 > 1)
516                                 width2 >>= 1;
517                         if (height2 > 1)
518                                 height2 >>= 1;
519
520                         glTexImage2D(GL_TEXTURE_2D, mip++, internalformat, width2, height2, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
521                 }
522
523                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
524                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
525         }
526         else
527         {
528                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
529                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
530         }
531         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
532
533         qfree(buffer);
534 }
535
536 void GL_Upload8 (int glslot, byte *data, int width, int height, int flags)
537 {
538         byte *data32;
539         data32 = qmalloc(width*height*4);
540         Image_Copy8bitRGBA(data, data32, width*height, d_8to24table);
541         GL_Upload32(glslot, data32, width, height, flags);
542         qfree(data32);
543 }
544
545 void GL_UploadTexture (gltexture_t *glt)
546 {
547         if (glt->inputtexels == NULL)
548                 return;
549         if (glt->flags & TEXF_RGBA)
550                 GL_Upload32(glt->texnum, glt->inputtexels, glt->width, glt->height, glt->flags);
551         else // 8bit
552                 GL_Upload8(glt->texnum, glt->inputtexels, glt->width, glt->height, glt->flags);
553         glt->internalflags |= GLTEXF_UPLOADED;
554         qfree(glt->inputtexels);
555         glt->inputtexels = NULL;
556 }
557
558 int R_CalcTexelDataSize (int width, int height, int mipmapped)
559 {
560         int width2, height2, size;
561         width2 = 1;while (width2 < width) width2 <<= 1;
562         height2 = 1;while (height2 < height) height2 <<= 1;
563         // calculate final size (mipmapped downward to this)
564         width2 >>= (int) r_picmip.value;
565         height2 >>= (int) r_picmip.value;
566         while (width2 > (int) r_max_size.value) width2 >>= 1;
567         while (height2 > (int) r_max_size.value) height2 >>= 1;
568         if (width2 < 1) width2 = 1;
569         if (height2 < 1) height2 = 1;
570
571         size = 0;
572         if (mipmapped)
573         {
574                 while (width2 > 1 || height2 > 1)
575                 {
576                         size += width2 * height2;
577                         if (width2 > 1)
578                                 width2 >>= 1;
579                         if (height2 > 1)
580                                 height2 >>= 1;
581                 }
582                 size++; // count the last 1x1 mipmap
583         }
584         else
585                 size = width2*height2;
586
587         size *= 4; // RGBA
588
589         return size;
590 }
591
592 /*
593 ================
594 GL_LoadTexture
595 ================
596 */
597 rtexture_t *R_LoadTexture (char *identifier, int width, int height, byte *data, int flags)
598 {
599         int                             i, bytesperpixel, internalflags, precache;
600         gltexture_t             *glt;
601         unsigned short  crc;
602
603         if (cls.state == ca_dedicated)
604                 return NULL;
605
606         if (!identifier[0])
607                 Host_Error("R_LoadTexture: no identifier\n");
608         if (data == NULL)
609                 Host_Error("R_LoadTexture: \"%s\" has no data\n", identifier);
610
611         // clear the alpha flag if the texture has no transparent pixels
612         if (flags & TEXF_ALPHA)
613         {
614                 int alpha = false;
615                 if (flags & TEXF_RGBA)
616                 {
617                         for (i = 0;i < width * height;i++)
618                         {
619                                 if (data[i * 4 + 3] < 255)
620                                 {
621                                         alpha = true;
622                                         break;
623                                 }
624                         }
625                 }
626                 else
627                 {
628                         for (i = 0;i < width * height;i++)
629                         {
630                                 if (data[i] == 255)
631                                 {
632                                         alpha = true;
633                                         break;
634                                 }
635                         }
636                 }
637                 if (!alpha)
638                         flags &= ~TEXF_ALPHA;
639         }
640
641         if (flags & TEXF_RGBA)
642                 bytesperpixel = 4;
643         else
644                 bytesperpixel = 1;
645
646         internalflags = 0;
647         if (r_lerpimages.value != 0)
648                 internalflags |= GLTEXF_LERPED;
649
650         // LordHavoc: do a CRC to confirm the data really is the same as previous occurances.
651         crc = CRC_Block(data, width*height*bytesperpixel);
652         // see if the texture is already present
653         for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
654         {
655                 if (!strcmp (identifier, glt->identifier))
656                 {
657                         // LordHavoc: everyone hates cache mismatchs, so I fixed it
658                         if (crc != glt->crc || width != glt->width || height != glt->height || flags != glt->flags)
659                         {
660                                 Con_DPrintf("GL_LoadTexture: cache mismatch, replacing old texture\n");
661                                 goto GL_LoadTexture_setup; // drop out with glt pointing to the texture to replace
662                         }
663                         if (internalflags != glt->internalflags)
664                                 goto GL_LoadTexture_setup; // drop out with glt pointing to the texture to replace
665                         return (rtexture_t *)glt;
666                 }
667         }
668
669 /*
670         if (freeglt)
671         {
672                 glt = freeglt;
673                 strcpy (glt->identifier, identifier);
674         }
675         else
676         {
677 */
678                 // LordHavoc: check if there are still slots available
679                 if (numgltextures >= MAX_GLTEXTURES)
680                         Sys_Error ("GL_LoadTexture: ran out of texture slots (%d)\n", MAX_GLTEXTURES);
681                 glt = &gltextures[numgltextures++];
682                 glt->texnum = gl_texture_number++;
683                 strcpy (glt->identifier, identifier);
684 //      }
685
686 // LordHavoc: label to drop out of the loop into the setup code
687 GL_LoadTexture_setup:
688         glt->crc = crc; // LordHavoc: used to verify textures are identical
689         glt->width = width;
690         glt->height = height;
691         glt->flags = flags;
692         glt->internalflags = internalflags;
693
694         if (glt->inputtexels)
695                 qfree(glt->inputtexels);
696         glt->inputtexeldatasize = width*height*bytesperpixel;
697         glt->inputtexels = qmalloc(glt->inputtexeldatasize);
698
699         memcpy(glt->inputtexels, data, glt->inputtexeldatasize);
700
701         glt->texeldatasize = R_CalcTexelDataSize(width, height, flags & TEXF_MIPMAP);
702
703         precache = false;
704         if (flags & TEXF_ALWAYSPRECACHE)
705                 precache = true;
706         else if (r_precachetextures.value >= 1)
707         {
708                 if (flags & TEXF_PRECACHE)
709                         precache = true;
710                 if (r_precachetextures.value >= 2)
711                         precache = true;
712         }
713
714         if (precache)
715                 GL_UploadTexture(glt);
716
717         return (rtexture_t *)glt;
718 }
719
720 // only used for lightmaps
721 int R_GetTextureSlots(int count)
722 {
723         int i;
724         i = gl_texture_number;
725         gl_texture_number += count;
726         return i;
727 }