]> icculus.org git repositories - divverent/darkplaces.git/blob - gl_textures.c
implemented r_render and r_upload cvar options for CPU profiling (not hardware bound...
[divverent/darkplaces.git] / gl_textures.c
1 #include "quakedef.h"
2
3 cvar_t          gl_max_size = {"gl_max_size", "1024"};
4 cvar_t          gl_picmip = {"gl_picmip", "0"};
5 cvar_t          gl_lerpimages = {"gl_lerpimages", "1"};
6 cvar_t          r_upload = {"r_upload", "1"};
7
8 int             gl_filter_min = GL_LINEAR_MIPMAP_LINEAR; //NEAREST;
9 int             gl_filter_max = GL_LINEAR;
10
11
12 int             texels;
13
14 // 4096x4096
15 #define MAXMIPS 12
16
17 typedef struct
18 {
19         int             texnum;
20         byte    *texels[MAXMIPS];
21         unsigned short texelsize[MAXMIPS][2];
22         char    identifier[64];
23         short   width, height;
24 // LordHavoc: CRC to identify cache mismatchs
25         unsigned short crc;
26         char    mipmap;
27         char    alpha;
28         char    bytesperpixel;
29         char    lerped; // whether this texture was uploaded with or without interpolation
30 } gltexture_t;
31
32 #define MAX_GLTEXTURES  4096
33 gltexture_t     gltextures[MAX_GLTEXTURES];
34 int                     numgltextures;
35
36 typedef struct
37 {
38         char *name;
39         int     minimize, maximize;
40 } glmode_t;
41
42 glmode_t modes[] = {
43         {"GL_NEAREST", GL_NEAREST, GL_NEAREST},
44         {"GL_LINEAR", GL_LINEAR, GL_LINEAR},
45         {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST},
46         {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR},
47         {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST},
48         {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR}
49 };
50
51 /*
52 ===============
53 Draw_TextureMode_f
54 ===============
55 */
56 void Draw_TextureMode_f (void)
57 {
58         int             i;
59         gltexture_t     *glt;
60
61         if (Cmd_Argc() == 1)
62         {
63                 for (i=0 ; i< 6 ; i++)
64                         if (gl_filter_min == modes[i].minimize)
65                         {
66                                 Con_Printf ("%s\n", modes[i].name);
67                                 return;
68                         }
69                 Con_Printf ("current filter is unknown???\n");
70                 return;
71         }
72
73         for (i=0 ; i< 6 ; i++)
74         {
75                 if (!Q_strcasecmp (modes[i].name, Cmd_Argv(1) ) )
76                         break;
77         }
78         if (i == 6)
79         {
80                 Con_Printf ("bad filter name\n");
81                 return;
82         }
83
84         gl_filter_min = modes[i].minimize;
85         gl_filter_max = modes[i].maximize;
86
87         if (!r_upload.value)
88                 return;
89         // change all the existing mipmap texture objects
90         for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
91         {
92                 if (glt->mipmap)
93                 {
94                         glBindTexture(GL_TEXTURE_2D, glt->texnum);
95                         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
96                         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
97                 }
98         }
99 }
100
101 extern int buildnumber;
102
103 char engineversion[40];
104
105 void GL_UploadTexture (gltexture_t *glt);
106 void gl_textures_start()
107 {
108         int i;
109         gltexture_t *glt;
110         for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
111                 GL_UploadTexture(glt);
112 }
113
114 void gl_textures_shutdown()
115 {
116 }
117
118 void GL_Textures_Init (void)
119 {
120         Cvar_RegisterVariable (&gl_max_size);
121         Cvar_RegisterVariable (&gl_picmip);
122         Cvar_RegisterVariable (&gl_lerpimages);
123         Cvar_RegisterVariable (&r_upload);
124 #ifdef NORENDER
125         r_upload.value = 0;
126 #endif
127
128         // 3dfx can only handle 256 wide textures
129         if (!Q_strncasecmp ((char *)gl_renderer, "3dfx",4) || strstr((char *)gl_renderer, "Glide"))
130                 Cvar_Set ("gl_max_size", "256");
131
132         Cmd_AddCommand ("gl_texturemode", &Draw_TextureMode_f);
133
134         R_RegisterModule("GL_Textures", gl_textures_start, gl_textures_shutdown);
135 }
136
137 /*
138 ================
139 GL_FindTexture
140 ================
141 */
142 int GL_FindTexture (char *identifier)
143 {
144         int             i;
145         gltexture_t     *glt;
146
147         for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
148         {
149                 if (!strcmp (identifier, glt->identifier))
150                         return gltextures[i].texnum;
151         }
152
153         return -1;
154 }
155
156 extern byte qgamma[];
157
158 // LordHavoc: gamma correction and improved resampling
159 void GL_ResampleTextureLerpLine (byte *in, byte *out, int inwidth, int outwidth)
160 {
161         int             j, xi, oldx = 0, f, fstep, l1, l2, endx;
162         fstep = (int) (inwidth*65536.0f/outwidth);
163         endx = (inwidth-1);
164         for (j = 0,f = 0;j < outwidth;j++, f += fstep)
165         {
166                 xi = (int) f >> 16;
167                 if (xi != oldx)
168                 {
169                         in += (xi - oldx) * 4;
170                         oldx = xi;
171                 }
172                 if (xi < endx)
173                 {
174                         l2 = f & 0xFFFF;
175                         l1 = 0x10000 - l2;
176                         *out++ = qgamma[(byte) ((in[0] * l1 + in[4] * l2) >> 16)];
177                         *out++ = qgamma[(byte) ((in[1] * l1 + in[5] * l2) >> 16)];
178                         *out++ = qgamma[(byte) ((in[2] * l1 + in[6] * l2) >> 16)];
179                         *out++ =        (byte) ((in[3] * l1 + in[7] * l2) >> 16) ;
180                 }
181                 else // last pixel of the line has no pixel to lerp to
182                 {
183                         *out++ = qgamma[in[0]];
184                         *out++ = qgamma[in[1]];
185                         *out++ = qgamma[in[2]];
186                         *out++ =        in[3] ;
187                 }
188         }
189 }
190
191 /*
192 ================
193 GL_ResampleTexture
194 ================
195 */
196 void GL_ResampleTexture (void *indata, int inwidth, int inheight, void *outdata,  int outwidth, int outheight)
197 {
198         // LordHavoc: gamma correction and greatly improved resampling
199         if (gl_lerpimages.value)
200         {
201                 int             i, j, yi, oldy, f, fstep, l1, l2, endy = (inheight-1);
202                 byte    *inrow, *out, *row1, *row2;
203                 out = outdata;
204                 fstep = (int) (inheight*65536.0f/outheight);
205
206                 row1 = malloc(outwidth*4);
207                 row2 = malloc(outwidth*4);
208                 inrow = indata;
209                 oldy = 0;
210                 GL_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
211                 GL_ResampleTextureLerpLine (inrow + inwidth*4, row2, inwidth, outwidth);
212                 for (i = 0, f = 0;i < outheight;i++,f += fstep)
213                 {
214                         yi = f >> 16;
215                         if (yi != oldy)
216                         {
217                                 inrow = (byte *)indata + inwidth*4*yi;
218                                 if (yi == oldy+1)
219                                         memcpy(row1, row2, outwidth*4);
220                                 else
221                                         GL_ResampleTextureLerpLine (inrow, row1, inwidth, outwidth);
222                                 if (yi < endy)
223                                         GL_ResampleTextureLerpLine (inrow + inwidth*4, row2, inwidth, outwidth);
224                                 else
225                                         memcpy(row2, row1, outwidth*4);
226                                 oldy = yi;
227                         }
228                         if (yi < endy)
229                         {
230                                 l2 = f & 0xFFFF;
231                                 l1 = 0x10000 - l2;
232                                 for (j = 0;j < outwidth;j++)
233                                 {
234                                         *out++ = (byte) ((*row1++ * l1 + *row2++ * l2) >> 16);
235                                         *out++ = (byte) ((*row1++ * l1 + *row2++ * l2) >> 16);
236                                         *out++ = (byte) ((*row1++ * l1 + *row2++ * l2) >> 16);
237                                         *out++ = (byte) ((*row1++ * l1 + *row2++ * l2) >> 16);
238                                 }
239                                 row1 -= outwidth*4;
240                                 row2 -= outwidth*4;
241                         }
242                         else // last line has no pixels to lerp to
243                         {
244                                 for (j = 0;j < outwidth;j++)
245                                 {
246                                         *out++ = *row1++;
247                                         *out++ = *row1++;
248                                         *out++ = *row1++;
249                                         *out++ = *row1++;
250                                 }
251                                 row1 -= outwidth*4;
252                         }
253                 }
254                 free(row1);
255                 free(row2);
256         }
257         else
258         {
259                 int             i, j;
260                 unsigned        frac, fracstep;
261                 byte    *inrow, *out, *inpix;
262                 out = outdata;
263
264                 fracstep = inwidth*0x10000/outwidth;
265                 for (i=0 ; i<outheight ; i++)
266                 {
267                         inrow = (byte *)indata + inwidth*(i*inheight/outheight)*4;
268                         frac = fracstep >> 1;
269                         for (j=0 ; j<outwidth ; j+=4)
270                         {
271                                 inpix = inrow + ((frac >> 14) & ~3);*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ =       *inpix++ ;frac += fracstep;
272                                 inpix = inrow + ((frac >> 14) & ~3);*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ =       *inpix++ ;frac += fracstep;
273                                 inpix = inrow + ((frac >> 14) & ~3);*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ =       *inpix++ ;frac += fracstep;
274                                 inpix = inrow + ((frac >> 14) & ~3);*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ = qgamma[*inpix++];*out++ =       *inpix++ ;frac += fracstep;
275                         }
276                 }
277         }
278 }
279
280 /*
281 ================
282 GL_Resample8BitTexture -- JACK
283 ================
284 */
285 /*
286 void GL_Resample8BitTexture (unsigned char *in, int inwidth, int inheight, unsigned char *out,  int outwidth, int outheight)
287 {
288         int             i, j;
289         unsigned        char *inrow;
290         unsigned        frac, fracstep;
291
292         fracstep = inwidth*0x10000/outwidth;
293         for (i=0 ; i<outheight ; i++, out += outwidth)
294         {
295                 inrow = in + inwidth*(i*inheight/outheight);
296                 frac = fracstep >> 1;
297                 for (j=0 ; j<outwidth ; j+=4)
298                 {
299                         out[j  ] = inrow[frac>>16];frac += fracstep;
300                         out[j+1] = inrow[frac>>16];frac += fracstep;
301                         out[j+2] = inrow[frac>>16];frac += fracstep;
302                         out[j+3] = inrow[frac>>16];frac += fracstep;
303                 }
304         }
305 }
306 */
307
308
309 /*
310 ================
311 GL_MipMap
312
313 Operates in place, quartering the size of the texture
314 ================
315 */
316 /*
317 void GL_MipMap (byte *in, int width, int height)
318 {
319         int             i, j;
320         byte    *out;
321
322         width <<=2;
323         height >>= 1;
324         out = in;
325         for (i=0 ; i<height ; i++, in+=width)
326         {
327                 for (j=0 ; j<width ; j+=8, out+=4, in+=8)
328                 {
329                         out[0] = (in[0] + in[4] + in[width+0] + in[width+4])>>2;
330                         out[1] = (in[1] + in[5] + in[width+1] + in[width+5])>>2;
331                         out[2] = (in[2] + in[6] + in[width+2] + in[width+6])>>2;
332                         out[3] = (in[3] + in[7] + in[width+3] + in[width+7])>>2;
333                 }
334         }
335 }
336 */
337
338 /*
339 ================
340 GL_MipMap8Bit
341
342 Mipping for 8 bit textures
343 ================
344 */
345 /*
346 void GL_MipMap8Bit (byte *in, int width, int height)
347 {
348         int             i, j;
349         unsigned short     r,g,b;
350         byte    *out, *at1, *at2, *at3, *at4;
351
352         height >>= 1;
353         out = in;
354         for (i=0 ; i<height ; i++, in+=width)
355         {
356                 for (j=0 ; j<width ; j+=2, out+=1, in+=2)
357                 {
358                         at1 = (byte *) (d_8to24table + in[0]);
359                         at2 = (byte *) (d_8to24table + in[1]);
360                         at3 = (byte *) (d_8to24table + in[width+0]);
361                         at4 = (byte *) (d_8to24table + in[width+1]);
362
363                         r = (at1[0]+at2[0]+at3[0]+at4[0]); r>>=5;
364                         g = (at1[1]+at2[1]+at3[1]+at4[1]); g>>=5;
365                         b = (at1[2]+at2[2]+at3[2]+at4[2]); b>>=5;
366
367                         out[0] = d_15to8table[(r<<0) + (g<<5) + (b<<10)];
368                 }
369         }
370 }
371 */
372
373 /*
374 ===============
375 GL_Upload32
376 ===============
377 */
378 /*
379 void GL_Upload32 (void *data, int width, int height,  qboolean mipmap, qboolean alpha)
380 {
381         int samples, scaled_width, scaled_height, i;
382         byte *in, *out, *scaled;
383
384         for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1)
385                 ;
386         for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1)
387                 ;
388
389         scaled_width >>= (int)gl_picmip.value;
390         scaled_height >>= (int)gl_picmip.value;
391
392         if (scaled_width > gl_max_size.value)
393                 scaled_width = gl_max_size.value;
394         if (scaled_height > gl_max_size.value)
395                 scaled_height = gl_max_size.value;
396
397         if (alpha)
398         {
399                 alpha = false;
400                 in = data;
401                 for (i = 3;i < width*height*4;i += 4)
402                         if (in[i] != 255)
403                         {
404                                 alpha = true;
405                                 break;
406                         }
407         }
408
409         samples = alpha ? gl_alpha_format : gl_solid_format;
410
411         texels += scaled_width * scaled_height;
412
413         scaled = malloc(scaled_width*scaled_height*4);
414         if (scaled_width == width && scaled_height == height)
415         {
416                 // LordHavoc: gamma correct while copying
417                 in = (byte *)data;
418                 out = (byte *)scaled;
419                 for (i = 0;i < width*height;i++)
420                 {
421                         *out++ = qgamma[*in++];
422                         *out++ = qgamma[*in++];
423                         *out++ = qgamma[*in++];
424                         *out++ = *in++;
425                 }
426         }
427         else
428                 GL_ResampleTexture (data, width, height, scaled, scaled_width, scaled_height);
429
430         glTexImage2D (GL_TEXTURE_2D, 0, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled);
431         if (mipmap)
432         {
433                 int             miplevel;
434
435                 miplevel = 0;
436                 while (scaled_width > 1 || scaled_height > 1)
437                 {
438                         GL_MipMap ((byte *)scaled, scaled_width, scaled_height);
439                         scaled_width >>= 1;
440                         scaled_height >>= 1;
441                         if (scaled_width < 1)
442                                 scaled_width = 1;
443                         if (scaled_height < 1)
444                                 scaled_height = 1;
445                         miplevel++;
446                         glTexImage2D (GL_TEXTURE_2D, miplevel, samples, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaled);
447                 }
448         }
449
450         if (mipmap)
451         {
452                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
453                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
454         }
455         else
456         {
457                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
458                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
459         }
460         free(scaled);
461 }
462
463 void GL_Upload8_EXT (byte *data, int width, int height,  qboolean mipmap)
464 {
465         int             scaled_width, scaled_height;
466         byte    *scaled = NULL;
467
468         for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1)
469                 ;
470         for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1)
471                 ;
472
473         scaled_width >>= (int)gl_picmip.value;
474         scaled_height >>= (int)gl_picmip.value;
475
476         if (scaled_width > gl_max_size.value)
477                 scaled_width = gl_max_size.value;
478         if (scaled_height > gl_max_size.value)
479                 scaled_height = gl_max_size.value;
480
481         texels += scaled_width * scaled_height;
482
483         if (scaled_width == width && scaled_height == height)
484         {
485                 if (!mipmap)
486                 {
487                         glTexImage2D (GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX , GL_UNSIGNED_BYTE, data);
488                         goto done;
489                 }
490                 scaled = malloc(scaled_width*scaled_height*4);
491                 memcpy (scaled, data, width*height);
492         }
493         else
494         {
495                 scaled = malloc(scaled_width*scaled_height*4);
496                 GL_Resample8BitTexture (data, width, height, (void*) scaled, scaled_width, scaled_height);
497         }
498
499         glTexImage2D (GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, scaled);
500         if (mipmap)
501         {
502                 int             miplevel;
503
504                 miplevel = 0;
505                 while (scaled_width > 1 || scaled_height > 1)
506                 {
507                         GL_MipMap8Bit ((byte *)scaled, scaled_width, scaled_height);
508                         scaled_width >>= 1;
509                         scaled_height >>= 1;
510                         if (scaled_width < 1)
511                                 scaled_width = 1;
512                         if (scaled_height < 1)
513                                 scaled_height = 1;
514                         miplevel++;
515                         glTexImage2D (GL_TEXTURE_2D, miplevel, GL_COLOR_INDEX8_EXT, scaled_width, scaled_height, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, scaled);
516                 }
517         }
518 done: ;
519
520
521         if (mipmap)
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         free(scaled);
532 }
533 */
534
535 extern qboolean VID_Is8bit();
536
537 /*
538 ===============
539 GL_Upload8
540 ===============
541 */
542 /*
543 void GL_Upload8 (byte *data, int width, int height,  qboolean mipmap, qboolean alpha)
544 {
545         static  unsigned *trans;
546         int                     i, s;
547         qboolean        noalpha;
548         int                     p;
549         byte    *indata;
550         int             *outdata;
551
552         s = width*height;
553         trans = malloc(s*4);
554         // if there are no transparent pixels, make it a 3 component
555         // texture even if it was specified as otherwise
556         if (alpha)
557         {
558                 noalpha = true;
559                 for (i=0 ; i<s ; i++)
560                 {
561                         p = data[i];
562                         if (p != 255)
563                                 trans[i] = d_8to24table[p];
564                         else
565                         {
566                                 trans[i] = 0; // force to black
567                                 noalpha = false;
568                         }
569                 }
570
571                 if (noalpha)
572                 {
573                         if (VID_Is8bit() && (data!=scrap_texels[0]))
574                         {
575                                 GL_Upload8_EXT (data, width, height, mipmap);
576                                 free(trans);
577                                 return;
578                         }
579                         alpha = false;
580                 }
581         }
582         else
583         {
584                 // LordHavoc: dodge the copy if it will be uploaded as 8bit
585                 if (VID_Is8bit() && (data!=scrap_texels[0]))
586                 {
587                         GL_Upload8_EXT (data, width, height, mipmap);
588                         free(trans);
589                         return;
590                 }
591                 //if (s&3)
592                 //      Sys_Error ("GL_Upload8: s&3");
593                 indata = data;
594                 outdata = trans;
595                 if (s&1)
596                         *outdata++ = d_8to24table[*indata++];
597                 if (s&2)
598                 {
599                         *outdata++ = d_8to24table[*indata++];
600                         *outdata++ = d_8to24table[*indata++];
601                 }
602                 for (i = 0;i < s;i+=4)
603                 {
604                         *outdata++ = d_8to24table[*indata++];
605                         *outdata++ = d_8to24table[*indata++];
606                         *outdata++ = d_8to24table[*indata++];
607                         *outdata++ = d_8to24table[*indata++];
608                 }
609         }
610
611         GL_Upload32 (trans, width, height, mipmap, alpha);
612         free(trans);
613 }
614 */
615
616 void GL_AllocTexels(gltexture_t *glt, int width, int height, int mipmapped)
617 {
618         int i, w, h, size, done;
619         if (glt->texels[0])
620                 free(glt->texels[0]);
621         glt->texelsize[0][0] = width;
622         glt->texelsize[0][1] = height;
623         if (mipmapped)
624         {
625                 size = 0;
626                 w = width;h = height;
627                 i = 0;
628                 done = false;
629                 for (i = 0;i < MAXMIPS;i++)
630                 {
631                         glt->texelsize[i][0] = w;
632                         glt->texelsize[i][1] = h;
633                         glt->texels[i] = (void *)size;
634                         size += w*h*4;
635                         if (w > 1)
636                         {
637                                 w >>= 1;
638                                 if (h > 1)
639                                         h >>= 1;
640                         }
641                         else if (h > 1)
642                                 h >>= 1;
643                         else
644                         {
645                                 i++;
646                                 break;
647                         }
648                 }
649                 while (i < MAXMIPS)
650                         glt->texels[i++] = NULL;
651                 glt->texels[0] = malloc(size);
652                 for (i = 1;i < MAXMIPS && glt->texels[i];i++)
653                         glt->texels[i] += (int) glt->texels[0];
654         }
655         else
656         {
657                 glt->texels[0] = malloc(width*height*4);
658                 for (i = 1;i < MAXMIPS;i++)
659                         glt->texels[i] = NULL;
660         }
661         if (!glt->texels[0])
662                 Sys_Error("GL_AllocTexels: out of memory\n");
663 }
664
665 // in can be the same as out
666 void GL_MipReduce(byte *in, byte *out, int width, int height, int destwidth, int destheight)
667 {
668         int x, y, width2, height2, nextrow;
669         if (width > destwidth)
670         {
671                 if (height > destheight)
672                 {
673                         // reduce both
674                         width2 = width >> 1;
675                         height2 = height >> 1;
676                         nextrow = width << 2;
677                         for (y = 0;y < height2;y++)
678                         {
679                                 for (x = 0;x < width2;x++)
680                                 {
681                                         out[0] = (byte) ((in[0] + in[4] + in[nextrow  ] + in[nextrow+4]) >> 2);
682                                         out[1] = (byte) ((in[1] + in[5] + in[nextrow+1] + in[nextrow+5]) >> 2);
683                                         out[2] = (byte) ((in[2] + in[6] + in[nextrow+2] + in[nextrow+6]) >> 2);
684                                         out[3] = (byte) ((in[3] + in[7] + in[nextrow+3] + in[nextrow+7]) >> 2);
685                                         out += 4;
686                                         in += 8;
687                                 }
688                                 in += nextrow; // skip a line
689                         }
690                 }
691                 else
692                 {
693                         // reduce width
694                         width2 = width >> 1;
695                         for (y = 0;y < height;y++)
696                         {
697                                 for (x = 0;x < width2;x++)
698                                 {
699                                         out[0] = (byte) ((in[0] + in[4]) >> 1);
700                                         out[1] = (byte) ((in[1] + in[5]) >> 1);
701                                         out[2] = (byte) ((in[2] + in[6]) >> 1);
702                                         out[3] = (byte) ((in[3] + in[7]) >> 1);
703                                         out += 4;
704                                         in += 8;
705                                 }
706                         }
707                 }
708         }
709         else
710         {
711                 if (height > destheight)
712                 {
713                         // reduce height
714                         height2 = height >> 1;
715                         nextrow = width << 2;
716                         for (y = 0;y < height2;y++)
717                         {
718                                 for (x = 0;x < width;x++)
719                                 {
720                                         out[0] = (byte) ((in[0] + in[nextrow  ]) >> 1);
721                                         out[1] = (byte) ((in[1] + in[nextrow+1]) >> 1);
722                                         out[2] = (byte) ((in[2] + in[nextrow+2]) >> 1);
723                                         out[3] = (byte) ((in[3] + in[nextrow+3]) >> 1);
724                                         out += 4;
725                                         in += 4;
726                                 }
727                                 in += nextrow; // skip a line
728                         }
729                 }
730                 else
731                         Sys_Error("GL_MipReduce: desired size already achieved\n");
732         }
733 }
734
735 void GL_UploadTexture (gltexture_t *glt)
736 {
737         int mip, width, height;
738         if (!r_upload.value)
739                 return;
740         glBindTexture(GL_TEXTURE_2D, glt->texnum);
741         width = glt->width;
742         height = glt->height;
743         for (mip = 0;mip < MAXMIPS && glt->texels[mip];mip++)
744                 glTexImage2D(GL_TEXTURE_2D, mip, glt->alpha ? 4 : 3, glt->texelsize[mip][0], glt->texelsize[mip][1], 0, GL_RGBA, GL_UNSIGNED_BYTE, glt->texels[mip]);
745         if (glt->mipmap)
746         {
747                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
748                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
749         }
750         else
751         {
752                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
753                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
754         }
755         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
756 }
757
758 /*
759 ================
760 GL_LoadTexture
761 ================
762 */
763 int GL_LoadTexture (char *identifier, int width, int height, byte *data, qboolean mipmap, qboolean alpha, int bytesperpixel)
764 {
765         unsigned short  crc;
766         int                             i, width2, height2, width3, height3, w, h, mip;
767         gltexture_t             *glt;
768
769         if (isDedicated)
770                 return 1;
771
772         // LordHavoc: do a CRC to confirm the data really is the same as previous occurances.
773         crc = CRC_Block(data, width*height*bytesperpixel);
774         // see if the texture is already present
775         if (identifier[0])
776         {
777                 for (i=0, glt=gltextures ; i<numgltextures ; i++, glt++)
778                 {
779                         if (!strcmp (identifier, glt->identifier))
780                         {
781                                 // LordHavoc: everyone hates cache mismatchs, so I fixed it
782                                 if (crc != glt->crc || width != glt->width || height != glt->height)
783                                 {
784                                         Con_DPrintf("GL_LoadTexture: cache mismatch, replacing old texture\n");
785                                         goto GL_LoadTexture_setup; // drop out with glt pointing to the texture to replace
786                                         //Sys_Error ("GL_LoadTexture: cache mismatch");
787                                 }
788                                 if ((gl_lerpimages.value != 0) != glt->lerped)
789                                         goto GL_LoadTexture_setup; // drop out with glt pointing to the texture to replace
790                                 return glt->texnum;
791                         }
792                 }
793         }
794         // LordHavoc: although this could be an else condition as it was in the original id code,
795         //            it is more clear this way
796         // LordHavoc: check if there are still slots available
797         if (numgltextures >= MAX_GLTEXTURES)
798                 Sys_Error ("GL_LoadTexture: ran out of texture slots (%d)\n", MAX_GLTEXTURES);
799         glt = &gltextures[numgltextures++];
800
801         strcpy (glt->identifier, identifier);
802         glt->texnum = texture_extension_number;
803         texture_extension_number++;
804 // LordHavoc: label to drop out of the loop into the setup code
805 GL_LoadTexture_setup:
806         // calculate power of 2 size
807         width2 = 1;while (width2 < width) width2 <<= 1;
808         height2 = 1;while (height2 < height) height2 <<= 1;
809         // calculate final size (mipmapped downward to this)
810         width3 = width2 >> (int) gl_picmip.value;
811         height3 = height2 >> (int) gl_picmip.value;
812         while (width3 > (int) gl_max_size.value) width3 >>= 1;
813         while (height3 > (int) gl_max_size.value) height3 >>= 1;
814         if (width3 < 1) width3 = 1;
815         if (height3 < 1) height3 = 1;
816
817         // final storage
818         GL_AllocTexels(glt, width3, height3, mipmap);
819         glt->crc = crc; // LordHavoc: used to verify textures are identical
820         glt->width = width;
821         glt->height = height;
822         glt->mipmap = mipmap;
823         glt->bytesperpixel = bytesperpixel;
824         glt->lerped = gl_lerpimages.value != 0;
825         glt->alpha = false; // updated later
826         if (width == width3 && height == height3) // perfect match
827         {
828                 if (bytesperpixel == 1) // 8bit
829                         Image_Copy8bitRGBA(data, glt->texels[0], width*height, d_8to24table);
830                 else
831                         Image_CopyRGBAGamma(data, glt->texels[0], width*height);
832         }
833         else if (width == width2 && height == height2) // perfect match for top level, but needs to be reduced
834         {
835                 byte *temptexels2;
836                 temptexels2 = malloc(width2*height2*4); // scaleup buffer
837                 if (bytesperpixel == 1) // 8bit
838                         Image_Copy8bitRGBA(data, temptexels2, width*height, d_8to24table);
839                 else
840                         Image_CopyRGBAGamma(data, temptexels2, width*height);
841                 while (width2 > width3 || height2 > height3)
842                 {
843                         w = width2;h = height2;
844                         if (width2 > width3) width2 >>= 1;
845                         if (height2 > height3) height2 >>= 1;
846                         if (width2 <= width3 && height2 <= height3) // size achieved
847                                 GL_MipReduce(temptexels2, glt->texels[0], w, h, width3, height3);
848                         else
849                                 GL_MipReduce(temptexels2, temptexels2, w, h, width3, height3);
850                 }
851                 free(temptexels2);
852         }
853         else // scaling...
854         {
855                 byte *temptexels;
856                 // pre-scaleup buffer
857                 temptexels = malloc(width*height*4);
858                 if (bytesperpixel == 1) // 8bit
859                         Image_Copy8bitRGBA(data, temptexels, width*height, d_8to24table);
860                 else
861                         Image_CopyRGBAGamma(data, temptexels, width*height);
862                 if (width2 != width3 || height2 != height3) // reduced by gl_pic_mip or gl_max_size
863                 {
864                         byte *temptexels2;
865                         temptexels2 = malloc(width2*height2*4); // scaleup buffer
866                         GL_ResampleTexture(temptexels, width, height, temptexels2, width2, height2);
867                         while (width2 > width3 || height2 > height3)
868                         {
869                                 w = width2;h = height2;
870                                 if (width2 > width3) width2 >>= 1;
871                                 if (height2 > height3) height2 >>= 1;
872                                 if (width2 <= width3 && height2 <= height3) // size achieved
873                                         GL_MipReduce(temptexels2, glt->texels[0], w, h, width3, height3);
874                                 else
875                                         GL_MipReduce(temptexels2, temptexels2, w, h, width3, height3);
876                         }
877                         free(temptexels2);
878                 }
879                 else // copy directly
880                         GL_ResampleTexture(temptexels, width, height, glt->texels[0], width2, height2);
881                 free(temptexels);
882         }
883         if (alpha)
884         {
885                 byte    *in = glt->texels[0] + 3;
886                 for (i = 0;i < width*height;i++, in += 4)
887                         if (*in < 255)
888                         {
889                                 glt->alpha = true;
890                                 break;
891                         }
892         }
893         // this loop is skipped if there are no mipmaps to generate
894         for (mip = 1;mip < MAXMIPS && glt->texels[mip];mip++)
895                 GL_MipReduce(glt->texels[mip-1], glt->texels[mip], glt->texelsize[mip-1][0], glt->texelsize[mip-1][1], 1, 1);
896         GL_UploadTexture(glt);
897
898 //      if (bytesperpixel == 1) // 8bit
899 //              GL_Upload8 (data, width, height, mipmap, alpha);
900 //      else // 32bit
901 //              GL_Upload32 (data, width, height, mipmap, true);
902
903         return glt->texnum;
904 }
905
906 /*
907 ================
908 GL_LoadPicTexture
909 ================
910 */
911 int GL_LoadPicTexture (qpic_t *pic)
912 {
913         return GL_LoadTexture ("", pic->width, pic->height, pic->data, false, true, 1);
914 }
915
916 int GL_GetTextureSlots (int count)
917 {
918         gltexture_t             *glt, *first;
919
920         first = glt = &gltextures[numgltextures];
921         while (count--)
922         {
923                 glt->identifier[0] = 0;
924                 glt->texnum = texture_extension_number++;
925                 glt->crc = 0;
926                 glt->width = 0;
927                 glt->height = 0;
928                 glt->bytesperpixel = 0;
929                 glt++;
930                 numgltextures++;
931         }
932         return first->texnum;
933 }