]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/heretic2/h2data/images.c
tools/heretic2: move heretic2 stuff to its own directory
[xonotic/netradiant.git] / tools / heretic2 / h2data / images.c
1 /*
2    Copyright (C) 1999-2007 id Software, Inc. and contributors.
3    For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #include "qdata.h"
23
24 #if GDEF_OS_WINDOWS
25  #include <windows.h>
26 #endif
27
28 #if GDEF_OS_MACOS && !defined( XWINDOWS )
29 #include <OpenGL/gl.h>
30 #else
31 #include <GL/gl.h>
32 #endif
33
34 #if 1
35 extern char     *g_outputDir;
36 #endif // _QDATA
37
38 char mip_prefix[1024];              // directory to dump the textures in
39
40 qboolean colormap_issued;
41 byte colormap_palette[768];
42
43 unsigned total_x = 0;
44 unsigned total_y = 0;
45 unsigned total_textures = 0;
46
47 #define MAX_IMAGE_SIZE 512
48
49 #if 0
50 /*
51    ==============
52    RemapZero
53
54    Replaces all 0 bytes in an image with the closest palette entry.
55    This is because NT won't let us change index 0, so any palette
56    animation leaves those pixels untouched.
57    ==============
58  */
59 void RemapZero( byte *pixels, byte *palette, int width, int height ){
60         int i, c;
61         int alt_zero;
62         int value, best;
63
64         alt_zero = 0;
65         best = 9999999;
66         for ( i = 1 ; i < 255 ; i++ )
67         {
68                 value = palette[i * 3 + 0] + palette[i * 3 + 1] + palette[i * 3 + 2];
69                 if ( value < best ) {
70                         best = value;
71                         alt_zero = i;
72                 }
73         }
74
75         c = width * height;
76         for ( i = 0 ; i < c ; i++ )
77                 if ( pixels[i] == 0 ) {
78                         pixels[i] = alt_zero;
79                 }
80 }
81
82 #endif
83
84
85 // ********************************************************************
86 // **  Mip Map Pre-Processing Routines
87 // ********************************************************************
88
89 #define intensity_value 1
90
91 static unsigned image_pal[256];
92
93 #define MAX_LAST 25
94
95 long palette_r[256], palette_g[256], palette_b[256];
96 long last_r[MAX_LAST],last_g[MAX_LAST],last_b[MAX_LAST], last_i[MAX_LAST], last_place;
97
98 long cached;
99
100 void PrepareConvert( unsigned *palette ){
101         int i;
102
103         for ( i = 0; i < 256; i++ )
104         {
105                 palette_r[i] = ( palette[i] & 0x00ff0000 ) >> 16;
106                 palette_g[i] = ( palette[i] & 0x0000ff00 ) >> 8;
107                 palette_b[i] = ( palette[i] & 0x000000ff );
108         }
109
110         for ( i = 0; i < MAX_LAST; i++ )
111                 last_r[i] = -1;
112
113         last_place = -1;
114 }
115
116 int ConvertTrueColorToPal( unsigned r, unsigned g, unsigned b ){
117         int i;
118         long min_dist;
119         int min_index;
120         long dist;
121         long dr, dg, db, biggest_delta;
122
123         for ( i = 0; i < MAX_LAST; i++ )
124                 if ( r == last_r[i] && g == last_g[i] && b == last_b[i] ) {
125                         cached++;
126                         return last_i[i];
127                 }
128
129         min_dist = 256 * 256 + 256 * 256 + 256 * 256;
130         biggest_delta = 256 * 256;
131         min_index = 0;
132
133         for ( i = 0; i < 256; i++ )
134         {
135                 dr = abs( palette_r[i] - r );
136                 if ( dr > biggest_delta ) {
137                         continue;
138                 }
139                 dg = abs( palette_g[i] - g );
140                 if ( dg > biggest_delta ) {
141                         continue;
142                 }
143                 db = abs( palette_b[i] - b );
144                 if ( db > biggest_delta ) {
145                         continue;
146                 }
147
148                 dist = dr * dr + dg * dg + db * db;
149                 if ( dist < min_dist ) {
150                         min_dist = dist;
151                         min_index = i;
152                         if ( min_dist == 0 ) {
153                                 break;
154                         }
155
156                         dist = dr;
157                         if ( dg > dist ) {
158                                 dist = dg;
159                         }
160                         if ( db > dist ) {
161                                 dist = db;
162                         }
163                         if ( dist < biggest_delta ) {
164                                 biggest_delta = dist;
165                         }
166                 }
167         }
168
169         last_place++;
170         if ( last_place >= MAX_LAST ) {
171                 last_place = 0;
172         }
173
174         last_r[last_place] = r;
175         last_g[last_place] = g;
176         last_b[last_place] = b;
177         last_i[last_place] = min_index;
178
179         return min_index;
180 }
181
182
183 void GL_ResampleTexture8P( byte *in, int inwidth, int inheight, byte *out,
184                                                    int outwidth, int outheight, palette_t *palette ){
185         int i, j;
186         byte    *inrow, *inrow2;
187         unsigned frac, fracstep;
188         unsigned p1[1024], p2[1024], *p1p, *p2p;
189         palette_t   *c1,*c2,*c3,*c4;
190         unsigned r,g,b;
191
192         fracstep = inwidth * 0x10000 / outwidth;
193
194         frac = fracstep >> 2;
195         for ( i = 0 ; i < outwidth ; i++ )
196         {
197                 p1[i] = frac >> 16;
198                 frac += fracstep;
199         }
200         frac = 3 * ( fracstep >> 2 );
201         for ( i = 0 ; i < outwidth ; i++ )
202         {
203                 p2[i] = frac >> 16;
204                 frac += fracstep;
205         }
206
207         cached = 0;
208
209         for ( i = 0 ; i < outheight ; i++ ) //, out += outwidth)
210         {
211                 inrow = in + inwidth * (int)( ( i + 0.25 ) * inheight / outheight );
212                 inrow2 = in + inwidth * (int)( ( i + 0.75 ) * inheight / outheight );
213
214                 p1p = p1;
215                 p2p = p2;
216                 for ( j = 0 ; j < outwidth ; j++ )
217                 {
218                         c1 = &palette[*( (byte *)inrow + ( *p1p ) )];
219                         c2 = &palette[*( (byte *)inrow + ( *p2p ) )];
220                         c3 = &palette[*( (byte *)inrow2 + ( *p1p++ ) )];
221                         c4 = &palette[*( (byte *)inrow2 + ( *p2p++ ) )];
222
223                         r = ( (unsigned)c1->r + (unsigned)c2->r + (unsigned)c3->r + (unsigned)c4->r ) >> 2;
224                         g = ( (unsigned)c1->g + (unsigned)c2->g + (unsigned)c3->g + (unsigned)c4->g ) >> 2;
225                         b = ( (unsigned)c1->b + (unsigned)c2->b + (unsigned)c3->b + (unsigned)c4->b ) >> 2;
226
227                         *out++ = ConvertTrueColorToPal( r,g,b );
228                 }
229         }
230 }
231
232 void GL_MipMap8P( byte *out, byte *in, int width, int height, palette_t *palette ){
233         int i, j;
234         palette_t   *c1,*c2,*c3,*c4;
235         unsigned r,g,b;
236
237         cached = 0;
238         memset( out, 0, 256 * 256 );
239         width <<= 1;
240         height <<= 1;
241
242         for ( i = 0; i < height; i += 2, in += width )
243         {
244                 for ( j = 0; j < width; j += 2 )
245                 {
246                         c1 = &palette[in[0]];
247                         c3 = &palette[in[width]];
248                         in++;
249                         c2 = &palette[in[0]];
250                         c4 = &palette[in[width]];
251                         in++;
252
253                         r = ( (unsigned)c1->r + (unsigned)c2->r + (unsigned)c3->r + (unsigned)c4->r ) >> 2;
254                         g = ( (unsigned)c1->g + (unsigned)c2->g + (unsigned)c3->g + (unsigned)c4->g ) >> 2;
255                         b = ( (unsigned)c1->b + (unsigned)c2->b + (unsigned)c3->b + (unsigned)c4->b ) >> 2;
256
257                         *out++ = ConvertTrueColorToPal( r, g, b );
258                 }
259         }
260 }
261
262
263 miptex_t *CreateMip( byte *data, unsigned width, unsigned height, byte *palette, int *FinalSize, qboolean mip ){
264         int scaled_width, scaled_height;
265         int i,j,r,g,b;
266         byte intensitytable[256];
267         byte scaled[256 * 256];
268         byte out[256 * 256];
269         int miplevel;
270         miptex_t    *mp;
271         byte        *pos;
272         int size;
273
274         for ( i = 0 ; i < 256 ; i++ )
275         {
276                 j = i * intensity_value;
277                 if ( j > 255 ) {
278                         j = 255;
279                 }
280                 intensitytable[i] = j;
281         }
282
283         for ( scaled_width = 1 ; scaled_width < width ; scaled_width <<= 1 )
284                 ;
285         if ( 1 && scaled_width > width && 1 ) {
286                 scaled_width >>= 1;
287         }
288         for ( scaled_height = 1 ; scaled_height < height ; scaled_height <<= 1 )
289                 ;
290         if ( 1 && scaled_height > height && 1 ) {
291                 scaled_height >>= 1;
292         }
293
294         // don't ever bother with >256 textures
295         if ( scaled_width > 256 ) {
296                 scaled_width = 256;
297         }
298         if ( scaled_height > 256 ) {
299                 scaled_height = 256;
300         }
301
302         if ( scaled_width < 1 ) {
303                 scaled_width = 1;
304         }
305         if ( scaled_height < 1 ) {
306                 scaled_height = 1;
307         }
308
309         size = sizeof( *mp ) + ( scaled_width * scaled_height * 3 );
310         mp = (miptex_t *)SafeMalloc( size, "CreateMip" );
311         memset( mp,0,size );
312
313         mp->version = MIP_VERSION;
314
315         for ( i = j = 0; i < 256; i++,j += 3 )
316         {
317                 mp->palette[i].r = r = intensitytable[palette[j]];
318                 mp->palette[i].g = g = intensitytable[palette[j + 1]];
319                 mp->palette[i].b = b = intensitytable[palette[j + 2]];
320                 image_pal[i] = 0xff000000 | ( r << 16 ) | ( g << 8 ) | ( b );
321         }
322
323         PrepareConvert( image_pal );
324
325         if ( scaled_width == width && scaled_height == height ) {
326                 memcpy( scaled, data, width * height );
327         }
328         else{
329                 GL_ResampleTexture8P( data, width, height, scaled, scaled_width, scaled_height, mp->palette );
330         }
331
332         pos = (byte *)( mp + 1 );
333         miplevel = 0;
334
335         while ( ( scaled_width >= 1 || scaled_height >= 1 ) && ( miplevel <= MIPLEVELS - 1 ) && ( !miplevel || mip ) )
336         {
337                 if ( scaled_width < 1 ) {
338                         scaled_width = 1;
339                 }
340                 if ( scaled_height < 1 ) {
341                         scaled_height = 1;
342                 }
343
344                 if ( miplevel > 0 ) {
345                         GL_MipMap8P( out, (byte *)scaled, scaled_width, scaled_height, mp->palette );
346                 }
347                 else{
348                         memcpy( out, scaled, 256 * 256 );
349                 }
350
351                 mp->width[miplevel] = scaled_width;
352                 mp->height[miplevel] = scaled_height;
353                 mp->offsets[miplevel] = pos - ( (byte *)( mp ) );
354                 memcpy( pos, out, scaled_width * scaled_height );
355                 memcpy( scaled, out, 256 * 256 );
356                 pos += scaled_width * scaled_height;
357
358                 scaled_width >>= 1;
359                 scaled_height >>= 1;
360
361                 miplevel++;
362         }
363
364         *FinalSize = pos - ( (byte *)( mp ) );
365
366         return mp;
367 }
368
369
370 void GL_ResampleTexture( unsigned *in, int inwidth, int inheight, unsigned *out,  int outwidth, int outheight ){
371         int i, j;
372         unsigned    *inrow, *inrow2;
373         unsigned frac, fracstep;
374         unsigned p1[1024], p2[1024];
375         byte        *pix1, *pix2, *pix3, *pix4;
376
377         fracstep = inwidth * 0x10000 / outwidth;
378
379         frac = fracstep >> 2;
380         for ( i = 0 ; i < outwidth ; i++ )
381         {
382                 p1[i] = 4 * ( frac >> 16 );
383                 frac += fracstep;
384         }
385         frac = 3 * ( fracstep >> 2 );
386         for ( i = 0 ; i < outwidth ; i++ )
387         {
388                 p2[i] = 4 * ( frac >> 16 );
389                 frac += fracstep;
390         }
391
392         for ( i = 0 ; i < outheight ; i++, out += outwidth )
393         {
394                 inrow = in + inwidth * (int)( ( i + 0.25 ) * inheight / outheight );
395                 inrow2 = in + inwidth * (int)( ( i + 0.75 ) * inheight / outheight );
396                 frac = fracstep >> 1;
397                 for ( j = 0 ; j < outwidth ; j++ )
398                 {
399                         pix1 = (byte *)inrow + p1[j];
400                         pix2 = (byte *)inrow + p2[j];
401                         pix3 = (byte *)inrow2 + p1[j];
402                         pix4 = (byte *)inrow2 + p2[j];
403                         ( (byte *)( out + j ) )[0] = ( pix1[0] + pix2[0] + pix3[0] + pix4[0] ) >> 2;
404                         ( (byte *)( out + j ) )[1] = ( pix1[1] + pix2[1] + pix3[1] + pix4[1] ) >> 2;
405                         ( (byte *)( out + j ) )[2] = ( pix1[2] + pix2[2] + pix3[2] + pix4[2] ) >> 2;
406                         ( (byte *)( out + j ) )[3] = ( pix1[3] + pix2[3] + pix3[3] + pix4[3] ) >> 2;
407                 }
408         }
409 }
410
411 void GL_MipMap( byte *out, byte *in, int width, int height ){
412         int i, j;
413
414         width <<= 3;
415         height <<= 1;
416         for ( i = 0 ; i < height ; i++, in += width )
417         {
418                 for ( j = 0 ; j < width ; j += 8, out += 4, in += 8 )
419                 {
420                         out[0] = ( in[0] + in[4] + in[width + 0] + in[width + 4] ) >> 2;
421                         out[1] = ( in[1] + in[5] + in[width + 1] + in[width + 5] ) >> 2;
422                         out[2] = ( in[2] + in[6] + in[width + 2] + in[width + 6] ) >> 2;
423                         out[3] = ( in[3] + in[7] + in[width + 3] + in[width + 7] ) >> 2;
424                 }
425         }
426 }
427
428 miptex32_t *CreateMip32( unsigned *data, unsigned width, unsigned height, int *FinalSize, qboolean mip ){
429         int scaled_width, scaled_height;
430         unsigned scaled[MAX_IMAGE_SIZE * MAX_IMAGE_SIZE];
431         unsigned out[MAX_IMAGE_SIZE * MAX_IMAGE_SIZE];
432         int miplevel;
433         miptex32_t      *mp;
434         byte            *pos;
435         int size;
436         paletteRGBA_t   *test;
437
438         for ( scaled_width = 1 ; scaled_width < width ; scaled_width <<= 1 )
439                 ;
440         if ( 1 && scaled_width > width && 1 ) {
441                 scaled_width >>= 1;
442         }
443         for ( scaled_height = 1 ; scaled_height < height ; scaled_height <<= 1 )
444                 ;
445         if ( 1 && scaled_height > height && 1 ) {
446                 scaled_height >>= 1;
447         }
448
449         // don't ever bother with >256 textures
450         if ( scaled_width > MAX_IMAGE_SIZE ) {
451                 scaled_width = MAX_IMAGE_SIZE;
452         }
453         if ( scaled_height > MAX_IMAGE_SIZE ) {
454                 scaled_height = MAX_IMAGE_SIZE;
455         }
456
457         if ( scaled_width < 1 ) {
458                 scaled_width = 1;
459         }
460         if ( scaled_height < 1 ) {
461                 scaled_height = 1;
462         }
463
464         size = sizeof( *mp ) + ( scaled_width * scaled_height * 3 * 4 );
465         mp = (miptex32_t *)SafeMalloc( size, "CreateMip" );
466         memset( mp,0,size );
467
468         mp->version = MIP32_VERSION;
469
470         size = width * height;
471         test = (paletteRGBA_t *)data;
472         while ( size )
473         {
474                 if ( test->a != 255 ) {
475                         mp->flags |= LittleLong( SURF_ALPHA_TEXTURE );
476                         break;
477                 }
478
479                 size--;
480                 test++;
481         }
482
483         if ( scaled_width == width && scaled_height == height ) {
484                 memcpy( scaled, data, width * height * 4 );
485         }
486         else{
487                 GL_ResampleTexture( data, width, height, scaled, scaled_width, scaled_height );
488         }
489
490         pos = (byte *)( mp + 1 );
491         miplevel = 0;
492
493         while ( ( scaled_width >= 1 || scaled_height >= 1 ) && ( miplevel <= MIPLEVELS - 1 ) && ( !miplevel || mip ) )
494         {
495                 if ( scaled_width < 1 ) {
496                         scaled_width = 1;
497                 }
498                 if ( scaled_height < 1 ) {
499                         scaled_height = 1;
500                 }
501
502                 if ( miplevel > 0 ) {
503                         GL_MipMap( (byte *)out, (byte *)scaled, scaled_width, scaled_height );
504                 }
505                 else
506                 {
507                         memcpy( out, scaled, MAX_IMAGE_SIZE * MAX_IMAGE_SIZE * 4 );
508                 }
509
510                 mp->width[miplevel] = scaled_width;
511                 mp->height[miplevel] = scaled_height;
512                 mp->offsets[miplevel] = pos - ( (byte *)( mp ) );
513                 memcpy( pos, out, scaled_width * scaled_height * 4 );
514                 memcpy( scaled, out, MAX_IMAGE_SIZE * MAX_IMAGE_SIZE * 4 );
515                 pos += scaled_width * scaled_height * 4;
516
517                 scaled_width >>= 1;
518                 scaled_height >>= 1;
519
520                 miplevel++;
521         }
522
523         *FinalSize = pos - ( (byte *)( mp ) );
524
525         return mp;
526 }
527
528 /*
529    ==============
530    Cmd_Grab
531
532    $grab filename x y width height
533    ==============
534  */
535 void Cmd_Grab( void ){
536         int xl,yl,w,h,y;
537         byte            *cropped;
538         char savename[1024];
539         char dest[1024];
540
541         GetScriptToken( false );
542
543         if ( token[0] == '/' || token[0] == '\\' ) {
544                 sprintf( savename, "%s%s.pcx", gamedir, token + 1 );
545         }
546         else{
547                 sprintf( savename, "%spics/%s.pcx", gamedir, token );
548         }
549
550         if ( g_release ) {
551                 if ( token[0] == '/' || token[0] == '\\' ) {
552                         sprintf( dest, "%s.pcx", token + 1 );
553                 }
554                 else{
555                         sprintf( dest, "pics/%s.pcx", token );
556                 }
557
558                 ReleaseFile( dest );
559                 return;
560         }
561
562         GetScriptToken( false );
563         xl = atoi( token );
564         GetScriptToken( false );
565         yl = atoi( token );
566         GetScriptToken( false );
567         w = atoi( token );
568         GetScriptToken( false );
569         h = atoi( token );
570
571         if ( xl < 0 || yl < 0 || w < 0 || h < 0 || xl + w > byteimagewidth || yl + h > byteimageheight ) {
572                 Error( "GrabPic: Bad size: %i, %i, %i, %i",xl,yl,w,h );
573         }
574
575         // crop it to the proper size
576         cropped = (byte *) SafeMalloc( w * h, "Cmd_Grab" );
577         for ( y = 0 ; y < h ; y++ )
578         {
579                 memcpy( cropped + y * w, byteimage + ( y + yl ) * byteimagewidth + xl, w );
580         }
581
582         // save off the new image
583         printf( "saving %s\n", savename );
584         CreatePath( savename );
585         WritePCXfile( savename, cropped, w, h, lbmpalette );
586
587         free( cropped );
588 }
589
590 /*
591    ==============
592    Cmd_Raw
593
594    $grab filename x y width height
595    ==============
596  */
597 void Cmd_Raw( void ){
598         int xl,yl,w,h,y;
599         byte            *cropped;
600         char savename[1024];
601         char dest[1024];
602
603         GetScriptToken( false );
604
605         sprintf( savename, "%s%s.lmp", gamedir, token );
606
607         if ( g_release ) {
608                 sprintf( dest, "%s.lmp", token );
609                 ReleaseFile( dest );
610                 return;
611         }
612
613         GetScriptToken( false );
614         xl = atoi( token );
615         GetScriptToken( false );
616         yl = atoi( token );
617         GetScriptToken( false );
618         w = atoi( token );
619         GetScriptToken( false );
620         h = atoi( token );
621
622         if ( xl < 0 || yl < 0 || w < 0 || h < 0 || xl + w > byteimagewidth || yl + h > byteimageheight ) {
623                 Error( "GrabPic: Bad size: %i, %i, %i, %i",xl,yl,w,h );
624         }
625
626         // crop it to the proper size
627         cropped = (byte *) SafeMalloc( w * h, "Cmd_Raw" );
628         for ( y = 0 ; y < h ; y++ )
629         {
630                 memcpy( cropped + y * w, byteimage + ( y + yl ) * byteimagewidth + xl, w );
631         }
632
633         // save off the new image
634         printf( "saving %s\n", savename );
635         CreatePath( savename );
636
637         SaveFile( savename, cropped, w * h );
638
639         free( cropped );
640 }
641
642 /*
643    =============================================================================
644
645    COLORMAP GRABBING
646
647    =============================================================================
648  */
649
650 /*
651    ===============
652    BestColor
653    ===============
654  */
655 byte BestColor( int r, int g, int b, int start, int stop ){
656         int i;
657         int dr, dg, db;
658         int bestdistortion, distortion;
659         int bestcolor;
660         byte    *pal;
661
662 //
663 // let any color go to 0 as a last resort
664 //
665         bestdistortion = 256 * 256 * 4;
666         bestcolor = 0;
667
668         pal = colormap_palette + start * 3;
669         for ( i = start ; i <= stop ; i++ )
670         {
671                 dr = r - (int)pal[0];
672                 dg = g - (int)pal[1];
673                 db = b - (int)pal[2];
674                 pal += 3;
675                 distortion = dr * dr + dg * dg + db * db;
676                 if ( distortion < bestdistortion ) {
677                         if ( !distortion ) {
678                                 return i;       // perfect match
679
680                         }
681                         bestdistortion = distortion;
682                         bestcolor = i;
683                 }
684         }
685
686         return bestcolor;
687 }
688
689
690 /*
691    ==============
692    Cmd_Colormap
693
694    $colormap filename
695
696    the brightes colormap is first in the table (FIXME: reverse this now?)
697
698    64 rows of 256 : lightmaps
699    256 rows of 256 : translucency table
700    ==============
701  */
702 void Cmd_Colormap( void ){
703         int levels, brights;
704         int l, c;
705         float frac, red, green, blue;
706         float range;
707         byte    *cropped, *lump_p;
708         char savename[1024];
709         char dest[1024];
710
711         colormap_issued = true;
712         if ( !g_release ) {
713                 memcpy( colormap_palette, lbmpalette, 768 );
714         }
715
716         if ( !ScriptTokenAvailable() ) { // just setting colormap_issued
717                 return;
718         }
719
720         GetScriptToken( false );
721         sprintf( savename, "%spics/%s.pcx", gamedir, token );
722
723         if ( g_release ) {
724                 sprintf( dest, "pics/%s.pcx", token );
725                 ReleaseFile( dest );
726                 return;
727         }
728
729         range = 2;
730         levels = 64;
731         brights = 1;    // ignore 255 (transparent)
732
733         cropped = (byte *) SafeMalloc( ( levels + 256 ) * 256, "Cmd_ColorMap" );
734         lump_p = cropped;
735
736 // shaded levels
737         for ( l = 0; l < levels; l++ )
738         {
739                 frac = range - range * (float)l / ( levels - 1 );
740                 for ( c = 0 ; c < 256 - brights ; c++ )
741                 {
742                         red = lbmpalette[c * 3];
743                         green = lbmpalette[c * 3 + 1];
744                         blue = lbmpalette[c * 3 + 2];
745
746                         red = (int)( red * frac + 0.5 );
747                         green = (int)( green * frac + 0.5 );
748                         blue = (int)( blue * frac + 0.5 );
749
750 //
751 // note: 254 instead of 255 because 255 is the transparent color, and we
752 // don't want anything remapping to that
753 // don't use color 0, because NT can't remap that (or 255)
754 //
755                         *lump_p++ = BestColor( red,green,blue, 1, 254 );
756                 }
757
758                 // fullbrights allways stay the same
759                 for ( ; c < 256 ; c++ )
760                         *lump_p++ = c;
761         }
762
763 // 66% transparancy table
764         for ( l = 0; l < 255; l++ )
765         {
766                 for ( c = 0 ; c < 255 ; c++ )
767                 {
768                         red = lbmpalette[c * 3] * 0.33 + lbmpalette[l * 3] * 0.66;
769                         green = lbmpalette[c * 3 + 1] * 0.33 + lbmpalette[l * 3 + 1] * 0.66;
770                         blue = lbmpalette[c * 3 + 2] * 0.33 + lbmpalette[l * 3 + 2] * 0.66;
771
772                         *lump_p++ = BestColor( red,green,blue, 1, 254 );
773                 }
774                 *lump_p++ = 255;
775         }
776         for ( c = 0 ; c < 256 ; c++ )
777                 *lump_p++ = 255;
778
779         // save off the new image
780         printf( "saving %s\n", savename );
781         CreatePath( savename );
782         WritePCXfile( savename, cropped, 256, levels + 256, lbmpalette );
783
784         free( cropped );
785 }
786
787 /*
788    =============================================================================
789
790    MIPTEX GRABBING
791
792    =============================================================================
793  */
794
795 byte pixdata[256];
796
797 int d_red, d_green, d_blue;
798
799 byte palmap[32][32][32];
800 qboolean palmap_built;
801
802 /*
803    =============
804    FindColor
805    =============
806  */
807 int FindColor( int r, int g, int b ){
808         int bestcolor;
809
810         if ( r > 255 ) {
811                 r = 255;
812         }
813         if ( r < 0 ) {
814                 r = 0;
815         }
816         if ( g > 255 ) {
817                 g = 255;
818         }
819         if ( g < 0 ) {
820                 g = 0;
821         }
822         if ( b > 255 ) {
823                 b = 255;
824         }
825         if ( b < 0 ) {
826                 b = 0;
827         }
828 #ifndef TABLECOLORS
829         bestcolor = BestColor( r, g, b, 0, 254 );
830 #else
831         bestcolor = palmap[r >> 3][g >> 3][b >> 3];
832 #endif
833
834         return bestcolor;
835 }
836
837
838 void BuildPalmap( void ){
839 #ifdef TABLECOLORS
840         int r, g, b;
841         int bestcolor;
842
843         if ( palmap_built ) {
844                 return;
845         }
846         palmap_built = true;
847
848         for ( r = 4 ; r < 256 ; r += 8 )
849         {
850                 for ( g = 4 ; g < 256 ; g += 8 )
851                 {
852                         for ( b = 4 ; b < 256 ; b += 8 )
853                         {
854                                 bestcolor = BestColor( r, g, b, 1, 254 );
855                                 palmap[r >> 3][g >> 3][b >> 3] = bestcolor;
856                         }
857                 }
858         }
859 #endif
860
861         if ( !colormap_issued ) {
862                 Error( "You must issue a $colormap command first" );
863         }
864
865 }
866
867 /*
868    =============
869    AveragePixels
870    =============
871  */
872 byte AveragePixels( int count ){
873         int r,g,b;
874         int i;
875         int vis;
876         int pix;
877         int bestcolor;
878         byte    *pal;
879         int fullbright;
880
881         vis = 0;
882         r = g = b = 0;
883         fullbright = 0;
884         for ( i = 0 ; i < count ; i++ )
885         {
886                 pix = pixdata[i];
887
888                 r += lbmpalette[pix * 3];
889                 g += lbmpalette[pix * 3 + 1];
890                 b += lbmpalette[pix * 3 + 2];
891                 vis++;
892         }
893
894         r /= vis;
895         g /= vis;
896         b /= vis;
897
898         // error diffusion
899         r += d_red;
900         g += d_green;
901         b += d_blue;
902
903 //
904 // find the best color
905 //
906         bestcolor = FindColor( r, g, b );
907
908         // error diffusion
909         pal = colormap_palette + bestcolor * 3;
910         d_red = r - (int)pal[0];
911         d_green = g - (int)pal[1];
912         d_blue = b - (int)pal[2];
913
914         return bestcolor;
915 }
916
917
918 typedef enum
919 {
920         pt_contents,
921         pt_flags,
922         pt_animvalue,
923         pt_altnamevalue,
924         pt_damagenamevalue,
925         pt_flagvalue,
926         pt_materialvalue,
927         pt_scale,
928         pt_mip,
929         pt_detail,
930         pt_gl,
931         pt_nomip,
932         pt_detailer,
933 } parmtype_t;
934
935 typedef struct
936 {
937         char    *name;
938         int flags;
939         parmtype_t type;
940 } mipparm_t;
941
942 mipparm_t mipparms[] =
943 {
944         // utility content attributes
945         {"pushpull",CONTENTS_PUSHPULL, pt_contents},
946         {"water",   CONTENTS_WATER, pt_contents},
947         {"slime",   CONTENTS_SLIME, pt_contents},       // mildly damaging
948         {"lava",    CONTENTS_LAVA, pt_contents},        // very damaging
949         {"window",  CONTENTS_WINDOW, pt_contents},  // solid, but doesn't eat internal textures
950         {"mist",    CONTENTS_MIST, pt_contents},    // non-solid window
951         {"origin",  CONTENTS_ORIGIN, pt_contents},  // center of rotating brushes
952         {"playerclip",  CONTENTS_PLAYERCLIP, pt_contents},
953         {"monsterclip", CONTENTS_MONSTERCLIP, pt_contents},
954
955         // utility surface attributes
956         {"hint",    SURF_HINT, pt_flags},
957         {"skip",    SURF_SKIP, pt_flags},
958         {"light",   SURF_LIGHT, pt_flagvalue},      // value is the light quantity
959
960         {"animspeed",SURF_ANIMSPEED, pt_flagvalue},     // value will hold the anim speed in fps
961
962         // texture chaining
963         {"anim",    0,          pt_animvalue},      // animname is the next animation
964         {"alt",     0,          pt_altnamevalue},   // altname is the alternate texture
965         {"damage",  0,          pt_damagenamevalue},    // damagename is the damage texture
966         {"scale",   0,          pt_scale},      // next two values are for scale
967         {"mip",     0,          pt_mip},
968         {"detail",  0,          pt_detail},
969
970         {"GL_ZERO",                 GL_ZERO,                pt_gl},
971         {"GL_ONE",                  GL_ONE,                 pt_gl},
972         {"GL_SRC_COLOR",            GL_SRC_COLOR,           pt_gl},
973         {"GL_ONE_MINUS_SRC_COLOR",  GL_ONE_MINUS_SRC_COLOR, pt_gl},
974         {"GL_DST_COLOR",            GL_DST_COLOR,           pt_gl},
975         {"GL_ONE_MINUS_DST_COLOR",  GL_ONE_MINUS_DST_COLOR, pt_gl},
976         {"GL_SRC_ALPHA",            GL_SRC_ALPHA,           pt_gl},
977         {"GL_ONE_MINUS_SRC_ALPHA",  GL_ONE_MINUS_SRC_ALPHA, pt_gl},
978         {"GL_DST_ALPHA",            GL_DST_ALPHA,           pt_gl},
979         {"GL_ONE_MINUS_DST_ALPHA",  GL_ONE_MINUS_DST_ALPHA, pt_gl},
980         {"GL_SRC_ALPHA_SATURATE",   GL_SRC_ALPHA_SATURATE,  pt_gl},
981
982         // server attributes
983         {"slick",   SURF_SLICK, pt_flags},
984
985         // drawing attributes
986         {"sky",     SURF_SKY, pt_flags},
987         {"warping", SURF_WARP, pt_flags},       // only valid with 64x64 textures
988         {"trans33", SURF_TRANS33, pt_flags},    // translucent should allso set fullbright
989         {"trans66", SURF_TRANS66, pt_flags},
990         {"flowing", SURF_FLOWING, pt_flags},    // flow direction towards angle 0
991         {"nodraw",  SURF_NODRAW, pt_flags}, // for clip textures and trigger textures
992         {"alpha",   SURF_ALPHA_TEXTURE, pt_flags},
993         {"undulate",    SURF_UNDULATE, pt_flags},       // rock surface up and down...
994         {"skyreflect",  SURF_SKYREFLECT, pt_flags},     // liquid will somewhat reflect the sky - not quite finished....
995
996         {"material", SURF_MATERIAL, pt_materialvalue},
997         {"metal",   SURF_TYPE_METAL, pt_flags},
998         {"stone",   SURF_TYPE_STONE, pt_flags},
999         {"wood",    SURF_TYPE_WOOD, pt_flags},
1000
1001         {"m_nomip", 0, pt_nomip},
1002         {"m_detail", 0, pt_detailer},
1003
1004         {NULL, 0, pt_contents}
1005 };
1006
1007 /*
1008    ==============
1009    Cmd_Mip
1010
1011    $mip filename x y width height <OPTIONS>
1012    must be multiples of sixteen
1013    SURF_WINDOW
1014    ==============
1015  */
1016
1017 void Cmd_Mip( void ){
1018         int xl,yl,xh,yh,w,h;
1019         byte            *dest, *source;
1020         int flags, value, contents;
1021         mipparm_t       *mp;
1022         char lumpname[128];
1023         char altname[128];
1024         char animname[128];
1025         char damagename[128];
1026         byte buffer[MAX_IMAGE_SIZE * MAX_IMAGE_SIZE];
1027         unsigned bufferl[MAX_IMAGE_SIZE * MAX_IMAGE_SIZE];
1028         materialtype_t  *mat;
1029         char filename[1024];
1030         unsigned        *destl, *sourcel;
1031         int linedelta, x, y;
1032         int size;
1033         miptex_t        *qtex;
1034         miptex32_t      *qtex32;
1035         float scale_x, scale_y;
1036         int mip_scale;
1037         // detail texturing
1038         char dt_name[128];
1039         float dt_scale_x, dt_scale_y;
1040         float dt_u, dt_v;
1041         float dt_alpha;
1042         int dt_src_blend_mode, dt_dst_blend_mode;
1043         int flags2;
1044
1045
1046         GetScriptToken( false );
1047         strcpy( lumpname, token );
1048
1049         GetScriptToken( false );
1050         xl = atoi( token );
1051         GetScriptToken( false );
1052         yl = atoi( token );
1053         GetScriptToken( false );
1054         w = atoi( token );
1055         GetScriptToken( false );
1056         h = atoi( token );
1057
1058         total_x += w;
1059         total_y += h;
1060         total_textures++;
1061
1062         if ( ( w & 15 ) || ( h & 15 ) ) {
1063                 Error( "line %i: miptex sizes must be multiples of 16", scriptline );
1064         }
1065
1066         flags = 0;
1067         flags2 = 0;
1068         contents = 0;
1069         value = 0;
1070         mip_scale = 0;
1071
1072         altname[0] = animname[0] = damagename[0] = 0;
1073
1074         scale_x = scale_y = 0.5;
1075
1076         // detail texturing
1077         dt_name[0] = 0;
1078         dt_scale_x = dt_scale_y = 0.0;
1079         dt_u = dt_v = 0.0;
1080         dt_alpha = 0.0;
1081         dt_src_blend_mode = dt_dst_blend_mode = 0;
1082
1083         // get optional flags and values
1084         while ( ScriptTokenAvailable() )
1085         {
1086                 GetScriptToken( false );
1087
1088                 for ( mp = mipparms ; mp->name ; mp++ )
1089                 {
1090                         if ( !strcmp( mp->name, token ) ) {
1091                                 switch ( mp->type )
1092                                 {
1093                                 case pt_animvalue:
1094                                         GetScriptToken( false );    // specify the next animation frame
1095                                         strcpy( animname, token );
1096                                         break;
1097                                 case pt_altnamevalue:
1098                                         GetScriptToken( false );    // specify the alternate texture
1099                                         strcpy( altname, token );
1100                                         break;
1101                                 case pt_damagenamevalue:
1102                                         GetScriptToken( false );    // specify the damage texture
1103                                         strcpy( damagename, token );
1104                                         break;
1105                                 case pt_flags:
1106                                         flags |= mp->flags;
1107                                         break;
1108                                 case pt_contents:
1109                                         contents |= mp->flags;
1110                                         break;
1111                                 case pt_flagvalue:
1112                                         flags |= mp->flags;
1113                                         GetScriptToken( false );    // specify the light value
1114                                         value = atoi( token );
1115                                         break;
1116                                 case pt_materialvalue:
1117                                         GetScriptToken( false );
1118                                         for ( mat = materialtypes ; mat->name ; mat++ )
1119                                         {
1120                                                 if ( !strcmp( mat->name, token ) ) {
1121                                                         // assumes SURF_MATERIAL is in top 8 bits
1122                                                         flags = ( flags & 0x0FFFFFF ) | ( mat->value << 24 );
1123                                                         break;
1124                                                 }
1125                                         }
1126                                         break;
1127                                 case pt_scale:
1128                                         GetScriptToken( false );    // specify the x scale
1129                                         scale_x = atof( token );
1130                                         GetScriptToken( false );    // specify the y scale
1131                                         scale_y = atof( token );
1132                                         break;
1133
1134                                 case pt_mip:
1135                                         mip_scale = 1;
1136                                         break;
1137
1138                                 case pt_detailer:
1139                                         flags2 |= MIP32_DETAILER_FLAG2;
1140                                         break;
1141
1142                                 case pt_nomip:
1143                                         flags2 |= MIP32_NOMIP_FLAG2;
1144                                         break;
1145
1146                                 case pt_detail:
1147                                         GetScriptToken( false );
1148                                         strcpy( dt_name, token );
1149                                         GetScriptToken( false );
1150                                         dt_scale_x = atof( token );
1151                                         GetScriptToken( false );
1152                                         dt_scale_y = atof( token );
1153                                         GetScriptToken( false );
1154                                         dt_u = atof( token );
1155                                         GetScriptToken( false );
1156                                         dt_v = atof( token );
1157                                         GetScriptToken( false );
1158                                         dt_alpha = atof( token );
1159                                         GetScriptToken( false );
1160                                         for ( mp = mipparms ; mp->name ; mp++ )
1161                                         {
1162                                                 if ( !strcmp( mp->name, token ) ) {
1163                                                         if ( mp->type == pt_gl ) {
1164                                                                 dt_src_blend_mode = mp->flags;
1165                                                                 break;
1166                                                         }
1167                                                 }
1168                                         }
1169                                         if ( !mp->name ) {
1170                                                 Error( "line %i: invalid gl blend mode %s", scriptline, token );
1171                                         }
1172                                         GetScriptToken( false );
1173                                         for ( mp = mipparms ; mp->name ; mp++ )
1174                                         {
1175                                                 if ( !strcmp( mp->name, token ) ) {
1176                                                         if ( mp->type == pt_gl ) {
1177                                                                 dt_dst_blend_mode = mp->flags;
1178                                                                 break;
1179                                                         }
1180                                                 }
1181                                         }
1182                                         if ( !mp->name ) {
1183                                                 Error( "line %i: invalid gl blend mode %s", scriptline, token );
1184                                         }
1185                                         break;
1186                                 }
1187                                 break;
1188                         }
1189                 }
1190                 if ( !mp->name ) {
1191                         Error( "line %i: unknown parm %s", scriptline, token );
1192                 }
1193         }
1194
1195         if ( g_release ) {
1196                 return; // textures are only released by $maps
1197
1198         }
1199         xh = xl + w;
1200         yh = yl + h;
1201         if ( xh * yh > MAX_IMAGE_SIZE * MAX_IMAGE_SIZE ) {
1202                 Error( "line %i image %s: image is too big!", scriptline, lumpname );
1203         }
1204
1205         if ( TrueColorImage ) {
1206                 if ( xl >= longimagewidth || xh > longimagewidth ||
1207                          yl >= longimageheight || yh > longimageheight ) {
1208                         Error( "line %i image %s: bad clip dimmensions (%d,%d) (%d,%d) > image (%d,%d)", scriptline, lumpname, xl,yl,w,h,longimagewidth,longimageheight );
1209                 }
1210
1211                 sourcel = longimage + ( yl * longimagewidth ) + xl;
1212                 destl = bufferl;
1213                 linedelta = ( longimagewidth - w );
1214
1215                 for ( y = yl ; y < yh ; y++ )
1216                 {
1217                         for ( x = xl ; x < xh ; x++ )
1218                         {
1219                                 *destl++ = *sourcel++;  // RGBA
1220                         }
1221                         sourcel += linedelta;
1222                 }
1223
1224                 qtex32 = CreateMip32( bufferl, w, h, &size, true );
1225
1226                 qtex32->flags |= LittleLong( flags );
1227                 qtex32->flags2 |= LittleLong( flags2 );
1228                 qtex32->contents = LittleLong( contents );
1229                 qtex32->value = LittleLong( value );
1230                 qtex32->scale_x = scale_x;
1231                 qtex32->scale_y = scale_y;
1232                 qtex32->mip_scale = mip_scale;
1233                 sprintf( qtex32->name, "%s/%s", mip_prefix, lumpname );
1234                 if ( animname[0] ) {
1235                         sprintf( qtex32->animname, "%s/%s", mip_prefix, animname );
1236                 }
1237                 if ( altname[0] ) {
1238                         sprintf( qtex32->altname, "%s/%s", mip_prefix, altname );
1239                 }
1240                 if ( damagename[0] ) {
1241                         sprintf( qtex32->damagename, "%s/%s", mip_prefix, damagename );
1242                 }
1243                 if ( dt_name[0] & ( ( flags2 & MIP32_DETAILER_FLAG2 ) == 0 ) ) {
1244                         sprintf( qtex32->dt_name, "%s/%s", mip_prefix, dt_name );
1245                         qtex32->dt_scale_x = dt_scale_x;
1246                         qtex32->dt_scale_y = dt_scale_y;
1247                         qtex32->dt_u = dt_u;
1248                         qtex32->dt_v = dt_v;
1249                         qtex32->dt_alpha = dt_alpha;
1250                         qtex32->dt_src_blend_mode = dt_src_blend_mode;
1251                         qtex32->dt_dst_blend_mode = dt_dst_blend_mode;
1252                 }
1253
1254                 //
1255                 // write it out
1256                 //
1257                 sprintf( filename, "%stextures/%s/%s.m32", g_outputDir, mip_prefix, lumpname );
1258                 if ( qtex32->flags & ( SURF_ALPHA_TEXTURE ) ) {
1259                         printf( "writing %s with ALPHA\n", filename );
1260                 }
1261                 else{
1262                         printf( "writing %s\n", filename );
1263                 }
1264                 SaveFile( filename, (byte *)qtex32, size );
1265
1266                 free( qtex32 );
1267         }
1268         else
1269         {
1270                 if ( xl >= byteimagewidth || xh > byteimagewidth ||
1271                          yl >= byteimageheight || yh > byteimageheight ) {
1272                         Error( "line %i: bad clip dimmensions (%d,%d) (%d,%d) > image (%d,%d)", scriptline, xl,yl,w,h,byteimagewidth,byteimageheight );
1273                 }
1274
1275                 source = byteimage + yl * byteimagewidth + xl;
1276                 dest = buffer;
1277                 linedelta = byteimagewidth - w;
1278
1279                 for ( y = yl ; y < yh ; y++ )
1280                 {
1281                         for ( x = xl ; x < xh ; x++ )
1282                         {
1283                                 *dest++ = *source++;
1284                         }
1285                         source += linedelta;
1286                 }
1287
1288                 qtex = CreateMip( buffer, w, h, lbmpalette, &size, true );
1289
1290                 qtex->flags = LittleLong( flags );
1291                 qtex->contents = LittleLong( contents );
1292                 qtex->value = LittleLong( value );
1293                 sprintf( qtex->name, "%s/%s", mip_prefix, lumpname );
1294                 if ( animname[0] ) {
1295                         sprintf( qtex->animname, "%s/%s", mip_prefix, animname );
1296                 }
1297
1298                 //
1299                 // write it out
1300                 //
1301                 sprintf( filename, "%stextures/%s/%s.m8", g_outputDir, mip_prefix, lumpname );
1302                 printf( "writing %s\n", filename );
1303                 SaveFile( filename, (byte *)qtex, size );
1304
1305                 free( qtex );
1306         }
1307 }
1308
1309 /*
1310    ===============
1311    Cmd_Mippal
1312    ===============
1313  */
1314 void Cmd_Mippal( void ){
1315         colormap_issued = true;
1316         if ( g_release ) {
1317                 return;
1318         }
1319
1320         memcpy( colormap_palette, lbmpalette, 768 );
1321
1322         BuildPalmap();
1323 }
1324
1325
1326 /*
1327    ===============
1328    Cmd_Mipdir
1329    ===============
1330  */
1331 void Cmd_Mipdir( void ){
1332         char filename[1024];
1333
1334         GetScriptToken( false );
1335         strcpy( mip_prefix, token );
1336         // create the directory if needed
1337         sprintf( filename, "%stextures", g_outputDir );
1338         Q_mkdir( filename );
1339         sprintf( filename, "%stextures/%s", g_outputDir, mip_prefix );
1340         Q_mkdir( filename );
1341 }
1342
1343
1344 /*
1345    =============================================================================
1346
1347    ENVIRONMENT MAP GRABBING
1348
1349    Creates six pcx files from tga files without any palette edge seams
1350    also copies the tga files for GL rendering.
1351    =============================================================================
1352  */
1353
1354 // 3dstudio environment map suffixes
1355 char    *suf[6] = {"rt", "ft", "lf", "bk", "up", "dn"};
1356
1357 /*
1358    =================
1359    Cmd_Environment
1360    =================
1361  */
1362 void Cmd_Environment( void ){
1363         char name[1024];
1364         int i, x, y;
1365         byte image[256 * 256];
1366         byte    *tga;
1367
1368         GetScriptToken( false );
1369
1370         if ( g_release ) {
1371                 for ( i = 0 ; i < 6 ; i++ )
1372                 {
1373                         sprintf( name, "env/%s%s.pcx", token, suf[i] );
1374                         ReleaseFile( name );
1375                         sprintf( name, "env/%s%s.tga", token, suf[i] );
1376                         ReleaseFile( name );
1377                 }
1378                 return;
1379         }
1380         // get the palette
1381         BuildPalmap();
1382
1383         sprintf( name, "%senv/", gamedir );
1384         CreatePath( name );
1385
1386         // convert the images
1387         for ( i = 0 ; i < 6 ; i++ )
1388         {
1389                 sprintf( name, "%senv/%s%s.tga", gamedir, token, suf[i] );
1390                 printf( "loading %s...\n", name );
1391                 LoadTGA( name, &tga, NULL, NULL );
1392
1393                 for ( y = 0 ; y < 256 ; y++ )
1394                 {
1395                         for ( x = 0 ; x < 256 ; x++ )
1396                         {
1397                                 image[y * 256 + x] = FindColor( tga[( y * 256 + x ) * 4 + 0],tga[( y * 256 + x ) * 4 + 1],tga[( y * 256 + x ) * 4 + 2] );
1398                         }
1399                 }
1400                 free( tga );
1401                 sprintf( name, "%senv/%s%s.pcx", gamedir, token, suf[i] );
1402                 if ( FileTime( name ) != -1 ) {
1403                         printf( "%s already exists, not overwriting.\n", name );
1404                 }
1405                 else{
1406                         WritePCXfile( name, image, 256, 256, colormap_palette );
1407                 }
1408         }
1409 }