]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake2/qdata/images.c
tools: reduce diff noise
[xonotic/netradiant.git] / tools / quake2 / qdata / 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 #include "inout.h"
24
25 char mip_prefix[1024];              // directory to dump the textures in
26
27 qboolean colormap_issued;
28 byte colormap_palette[768];
29
30 /*
31    ==============
32    RemapZero
33
34    Replaces all 0 bytes in an image with the closest palette entry.
35    This is because NT won't let us change index 0, so any palette
36    animation leaves those pixels untouched.
37    ==============
38  */
39 void RemapZero( byte *pixels, byte *palette, int width, int height ){
40         int i, c;
41         int alt_zero;
42         int value, best;
43
44         alt_zero = 0;
45         best = 9999999;
46         for ( i = 1 ; i < 255 ; i++ )
47         {
48                 value = palette[i * 3 + 0] + palette[i * 3 + 1] + palette[i * 3 + 2];
49                 if ( value < best ) {
50                         best = value;
51                         alt_zero = i;
52                 }
53         }
54
55         c = width * height;
56         for ( i = 0 ; i < c ; i++ )
57                 if ( pixels[i] == 0 ) {
58                         pixels[i] = alt_zero;
59                 }
60 }
61
62 /*
63    ==============
64    Cmd_Grab
65
66    $grab filename x y width height
67    ==============
68  */
69 void Cmd_Grab( void ){
70         int xl,yl,w,h,y;
71         byte            *cropped;
72         char savename[1024];
73         char dest[1024];
74
75         GetToken( false );
76
77         if ( token[0] == '/' || token[0] == '\\' ) {
78                 sprintf( savename, "%s%s.pcx", gamedir, token + 1 );
79         }
80         else{
81                 sprintf( savename, "%spics/%s.pcx", gamedir, token );
82         }
83
84         if ( g_release ) {
85                 if ( token[0] == '/' || token[0] == '\\' ) {
86                         sprintf( dest, "%s.pcx", token + 1 );
87                 }
88                 else{
89                         sprintf( dest, "pics/%s.pcx", token );
90                 }
91
92                 ReleaseFile( dest );
93                 return;
94         }
95
96         GetToken( false );
97         xl = atoi( token );
98         GetToken( false );
99         yl = atoi( token );
100         GetToken( false );
101         w = atoi( token );
102         GetToken( false );
103         h = atoi( token );
104
105         if ( xl < 0 || yl < 0 || w < 0 || h < 0 || xl + w > byteimagewidth || yl + h > byteimageheight ) {
106                 Error( "GrabPic: Bad size: %i, %i, %i, %i",xl,yl,w,h );
107         }
108
109         // crop it to the proper size
110         cropped = malloc( w * h );
111         for ( y = 0 ; y < h ; y++ )
112         {
113                 memcpy( cropped + y * w, byteimage + ( y + yl ) * byteimagewidth + xl, w );
114         }
115
116         // save off the new image
117         printf( "saving %s\n", savename );
118         CreatePath( savename );
119         WritePCXfile( savename, cropped, w, h, lbmpalette );
120
121         free( cropped );
122 }
123
124 /*
125    ==============
126    Cmd_Raw
127
128    $grab filename x y width height
129    ==============
130  */
131 void Cmd_Raw( void ){
132         int xl,yl,w,h,y;
133         byte            *cropped;
134         char savename[1024];
135         char dest[1024];
136
137         GetToken( false );
138
139         sprintf( savename, "%s%s.lmp", gamedir, token );
140
141         if ( g_release ) {
142                 sprintf( dest, "%s.lmp", token );
143                 ReleaseFile( dest );
144                 return;
145         }
146
147         GetToken( false );
148         xl = atoi( token );
149         GetToken( false );
150         yl = atoi( token );
151         GetToken( false );
152         w = atoi( token );
153         GetToken( false );
154         h = atoi( token );
155
156         if ( xl < 0 || yl < 0 || w < 0 || h < 0 || xl + w > byteimagewidth || yl + h > byteimageheight ) {
157                 Error( "GrabPic: Bad size: %i, %i, %i, %i",xl,yl,w,h );
158         }
159
160         // crop it to the proper size
161         cropped = malloc( w * h );
162         for ( y = 0 ; y < h ; y++ )
163         {
164                 memcpy( cropped + y * w, byteimage + ( y + yl ) * byteimagewidth + xl, w );
165         }
166
167         // save off the new image
168         printf( "saving %s\n", savename );
169         CreatePath( savename );
170
171         SaveFile( savename, cropped, w * h );
172
173         free( cropped );
174 }
175
176 /*
177    =============================================================================
178
179    COLORMAP GRABBING
180
181    =============================================================================
182  */
183
184 /*
185    ===============
186    BestColor
187    ===============
188  */
189 byte BestColor( int r, int g, int b, int start, int stop ){
190         int i;
191         int dr, dg, db;
192         int bestdistortion, distortion;
193         int bestcolor;
194         byte    *pal;
195
196 //
197 // let any color go to 0 as a last resort
198 //
199         bestdistortion = 256 * 256 * 4;
200         bestcolor = 0;
201
202         pal = colormap_palette + start * 3;
203         for ( i = start ; i <= stop ; i++ )
204         {
205                 dr = r - (int)pal[0];
206                 dg = g - (int)pal[1];
207                 db = b - (int)pal[2];
208                 pal += 3;
209                 distortion = dr * dr + dg * dg + db * db;
210                 if ( distortion < bestdistortion ) {
211                         if ( !distortion ) {
212                                 return i;       // perfect match
213
214                         }
215                         bestdistortion = distortion;
216                         bestcolor = i;
217                 }
218         }
219
220         return bestcolor;
221 }
222
223
224 /*
225    ==============
226    Cmd_Colormap
227
228    $colormap filename
229
230    the brightes colormap is first in the table (FIXME: reverse this now?)
231
232    64 rows of 256 : lightmaps
233    256 rows of 256 : translucency table
234    ==============
235  */
236 void Cmd_Colormap( void ){
237         int levels, brights;
238         int l, c;
239         float frac, red, green, blue;
240         float range;
241         byte    *cropped, *lump_p;
242         char savename[1024];
243         char dest[1024];
244
245         colormap_issued = true;
246         if ( !g_release ) {
247                 memcpy( colormap_palette, lbmpalette, 768 );
248         }
249
250         if ( !TokenAvailable() ) { // just setting colormap_issued
251                 return;
252         }
253
254         GetToken( false );
255         sprintf( savename, "%spics/%s.pcx", gamedir, token );
256
257         if ( g_release ) {
258                 sprintf( dest, "pics/%s.pcx", token );
259                 ReleaseFile( dest );
260                 return;
261         }
262
263         range = 2;
264         levels = 64;
265         brights = 1;    // ignore 255 (transparent)
266
267         cropped = malloc( ( levels + 256 ) * 256 );
268         lump_p = cropped;
269
270 // shaded levels
271         for ( l = 0; l < levels; l++ )
272         {
273                 frac = range - range * (float)l / ( levels - 1 );
274                 for ( c = 0 ; c < 256 - brights ; c++ )
275                 {
276                         red = lbmpalette[c * 3];
277                         green = lbmpalette[c * 3 + 1];
278                         blue = lbmpalette[c * 3 + 2];
279
280                         red = (int)( red * frac + 0.5 );
281                         green = (int)( green * frac + 0.5 );
282                         blue = (int)( blue * frac + 0.5 );
283
284 //
285 // note: 254 instead of 255 because 255 is the transparent color, and we
286 // don't want anything remapping to that
287 // don't use color 0, because NT can't remap that (or 255)
288 //
289                         *lump_p++ = BestColor( red,green,blue, 1, 254 );
290                 }
291
292                 // fullbrights allways stay the same
293                 for ( ; c < 256 ; c++ )
294                         *lump_p++ = c;
295         }
296
297 // 66% transparancy table
298         for ( l = 0; l < 255; l++ )
299         {
300                 for ( c = 0 ; c < 255 ; c++ )
301                 {
302                         red = lbmpalette[c * 3] * 0.33 + lbmpalette[l * 3] * 0.66;
303                         green = lbmpalette[c * 3 + 1] * 0.33 + lbmpalette[l * 3 + 1] * 0.66;
304                         blue = lbmpalette[c * 3 + 2] * 0.33 + lbmpalette[l * 3 + 2] * 0.66;
305
306                         *lump_p++ = BestColor( red,green,blue, 1, 254 );
307                 }
308                 *lump_p++ = 255;
309         }
310         for ( c = 0 ; c < 256 ; c++ )
311                 *lump_p++ = 255;
312
313         // save off the new image
314         printf( "saving %s\n", savename );
315         CreatePath( savename );
316         WritePCXfile( savename, cropped, 256, levels + 256, lbmpalette );
317
318         free( cropped );
319 }
320
321 /*
322    =============================================================================
323
324    MIPTEX GRABBING
325
326    =============================================================================
327  */
328
329 byte pixdata[256];
330
331 int d_red, d_green, d_blue;
332
333 byte palmap[32][32][32];
334 qboolean palmap_built;
335
336 /*
337    =============
338    FindColor
339    =============
340  */
341 int FindColor( int r, int g, int b ){
342         int bestcolor;
343
344         if ( r > 255 ) {
345                 r = 255;
346         }
347         if ( r < 0 ) {
348                 r = 0;
349         }
350         if ( g > 255 ) {
351                 g = 255;
352         }
353         if ( g < 0 ) {
354                 g = 0;
355         }
356         if ( b > 255 ) {
357                 b = 255;
358         }
359         if ( b < 0 ) {
360                 b = 0;
361         }
362 #ifndef TABLECOLORS
363         bestcolor = BestColor( r, g, b, 0, 254 );
364 #else
365         bestcolor = palmap[r >> 3][g >> 3][b >> 3];
366 #endif
367
368         return bestcolor;
369 }
370
371
372 void BuildPalmap( void ){
373 #ifdef TABLECOLORS
374         int r, g, b;
375         int bestcolor;
376
377         if ( palmap_built ) {
378                 return;
379         }
380         palmap_built = true;
381
382         for ( r = 4 ; r < 256 ; r += 8 )
383         {
384                 for ( g = 4 ; g < 256 ; g += 8 )
385                 {
386                         for ( b = 4 ; b < 256 ; b += 8 )
387                         {
388                                 bestcolor = BestColor( r, g, b, 1, 254 );
389                                 palmap[r >> 3][g >> 3][b >> 3] = bestcolor;
390                         }
391                 }
392         }
393 #endif
394
395         if ( !colormap_issued ) {
396                 Error( "You must issue a $colormap command first" );
397         }
398
399 }
400
401 /*
402    =============
403    AveragePixels
404    =============
405  */
406 byte AveragePixels( int count ){
407         int r,g,b;
408         int i;
409         int vis;
410         int pix;
411         int bestcolor;
412         byte    *pal;
413         int fullbright;
414
415         vis = 0;
416         r = g = b = 0;
417         fullbright = 0;
418         for ( i = 0 ; i < count ; i++ )
419         {
420                 pix = pixdata[i];
421
422                 r += lbmpalette[pix * 3];
423                 g += lbmpalette[pix * 3 + 1];
424                 b += lbmpalette[pix * 3 + 2];
425                 vis++;
426         }
427
428         r /= vis;
429         g /= vis;
430         b /= vis;
431
432         // error diffusion
433         r += d_red;
434         g += d_green;
435         b += d_blue;
436
437 //
438 // find the best color
439 //
440         bestcolor = FindColor( r, g, b );
441
442         // error diffusion
443         pal = colormap_palette + bestcolor * 3;
444         d_red = r - (int)pal[0];
445         d_green = g - (int)pal[1];
446         d_blue = b - (int)pal[2];
447
448         return bestcolor;
449 }
450
451
452 typedef enum
453 {
454         pt_contents,
455         pt_flags,
456         pt_animvalue,
457         pt_flagvalue
458 } parmtype_t;
459
460 typedef struct
461 {
462         char    *name;
463         int flags;
464         parmtype_t type;
465 } mipparm_t;
466
467 mipparm_t mipparms[] =
468 {
469         // utility content attributes
470         {"water",   CONTENTS_WATER, pt_contents},
471         {"slime",   CONTENTS_SLIME, pt_contents},       // mildly damaging
472         {"lava",    CONTENTS_LAVA, pt_contents},        // very damaging
473         {"window",  CONTENTS_WINDOW, pt_contents},  // solid, but doesn't eat internal textures
474         {"mist",    CONTENTS_MIST, pt_contents},    // non-solid window
475         {"origin",  CONTENTS_ORIGIN, pt_contents},  // center of rotating brushes
476         {"playerclip",  CONTENTS_PLAYERCLIP, pt_contents},
477         {"monsterclip", CONTENTS_MONSTERCLIP, pt_contents},
478
479         // utility surface attributes
480         {"hint",    SURF_HINT, pt_flags},
481         {"skip",    SURF_SKIP, pt_flags},
482         {"light",   SURF_LIGHT, pt_flagvalue},      // value is the light quantity
483
484         // texture chaining
485         {"anim",    0,          pt_animvalue},      // value is the next animation
486
487         // server attributes
488         {"slick",   SURF_SLICK, pt_flags},
489
490         // drawing attributes
491         {"sky",     SURF_SKY, pt_flags},
492         {"warping", SURF_WARP, pt_flags},       // only valid with 64x64 textures
493         {"trans33", SURF_TRANS33, pt_flags},    // translucent should allso set fullbright
494         {"trans66", SURF_TRANS66, pt_flags},
495         {"flowing", SURF_FLOWING, pt_flags},    // flow direction towards angle 0
496         {"nodraw",  SURF_NODRAW, pt_flags}, // for clip textures and trigger textures
497
498         {NULL, 0, pt_contents}
499 };
500
501
502
503 /*
504    ==============
505    Cmd_Mip
506
507    $mip filename x y width height <OPTIONS>
508    must be multiples of sixteen
509    SURF_WINDOW
510    ==============
511  */
512 void Cmd_Mip( void ){
513         int x,y,xl,yl,xh,yh,w,h;
514         byte            *screen_p, *source;
515         int linedelta;
516         miptex_t        *qtex;
517         int miplevel, mipstep;
518         int xx, yy, pix;
519         int count;
520         int flags, value, contents;
521         mipparm_t       *mp;
522         char lumpname[64];
523         byte            *lump_p;
524         char filename[1024];
525         char animname[64];
526
527         GetToken( false );
528         strcpy( lumpname, token );
529
530         GetToken( false );
531         xl = atoi( token );
532         GetToken( false );
533         yl = atoi( token );
534         GetToken( false );
535         w = atoi( token );
536         GetToken( false );
537         h = atoi( token );
538
539         if ( ( w & 15 ) || ( h & 15 ) ) {
540                 Error( "line %i: miptex sizes must be multiples of 16", scriptline );
541         }
542
543         flags = 0;
544         contents = 0;
545         value = 0;
546
547         animname[0] = 0;
548
549         // get optional flags and values
550         while ( TokenAvailable() )
551         {
552                 GetToken( false );
553
554                 for ( mp = mipparms ; mp->name ; mp++ )
555                 {
556                         if ( !strcmp( mp->name, token ) ) {
557                                 switch ( mp->type )
558                                 {
559                                 case pt_animvalue:
560                                         GetToken( false );   // specify the next animation frame
561                                         strcpy( animname, token );
562                                         break;
563                                 case pt_flags:
564                                         flags |= mp->flags;
565                                         break;
566                                 case pt_contents:
567                                         contents |= mp->flags;
568                                         break;
569                                 case pt_flagvalue:
570                                         flags |= mp->flags;
571                                         GetToken( false );   // specify the light value
572                                         value = atoi( token );
573                                         break;
574                                 }
575                                 break;
576                         }
577                 }
578                 if ( !mp->name ) {
579                         Error( "line %i: unknown parm %s", scriptline, token );
580                 }
581         }
582
583         sprintf( filename, "%stextures/%s/%s.wal", gamedir, mip_prefix, lumpname );
584         if ( g_release ) {
585                 return; // textures are only released by $maps
586
587         }
588         xh = xl + w;
589         yh = yl + h;
590
591         qtex = malloc( sizeof( miptex_t ) + w * h * 2 );
592         memset( qtex, 0, sizeof( miptex_t ) );
593
594         qtex->width = LittleLong( w );
595         qtex->height = LittleLong( h );
596         qtex->flags = LittleLong( flags );
597         qtex->contents = LittleLong( contents );
598         qtex->value = LittleLong( value );
599         sprintf( qtex->name, "%s/%s", mip_prefix, lumpname );
600         if ( animname[0] ) {
601                 sprintf( qtex->animname, "%s/%s", mip_prefix, animname );
602         }
603
604         lump_p = (byte *)( &qtex->value + 1 );
605
606         screen_p = byteimage + yl * byteimagewidth + xl;
607         linedelta = byteimagewidth - w;
608
609         source = lump_p;
610         qtex->offsets[0] = LittleLong( lump_p - (byte *)qtex );
611
612         for ( y = yl ; y < yh ; y++ )
613         {
614                 for ( x = xl ; x < xh ; x++ )
615                 {
616                         pix = *screen_p++;
617                         if ( pix == 255 ) {
618                                 pix = 1;        // should never happen
619                         }
620                         *lump_p++ = pix;
621                 }
622                 screen_p += linedelta;
623         }
624
625 //
626 // subsample for greater mip levels
627 //
628         d_red = d_green = d_blue = 0;   // no distortion yet
629
630         for ( miplevel = 1 ; miplevel < 4 ; miplevel++ )
631         {
632                 qtex->offsets[miplevel] = LittleLong( lump_p - (byte *)qtex );
633
634                 mipstep = 1 << miplevel;
635                 for ( y = 0 ; y < h ; y += mipstep )
636                 {
637
638                         for ( x = 0 ; x < w ; x += mipstep )
639                         {
640                                 count = 0;
641                                 for ( yy = 0 ; yy < mipstep ; yy++ )
642                                         for ( xx = 0 ; xx < mipstep ; xx++ )
643                                         {
644                                                 pixdata[count] = source[ ( y + yy ) * w + x + xx ];
645                                                 count++;
646                                         }
647                                 *lump_p++ = AveragePixels( count );
648                         }
649                 }
650         }
651
652 //
653 // dword align the size
654 //
655         while ( (int)lump_p & 3 )
656                 *lump_p++ = 0;
657
658 //
659 // write it out
660 //
661         printf( "writing %s\n", filename );
662         SaveFile( filename, (byte *)qtex, lump_p - (byte *)qtex );
663
664         free( qtex );
665 }
666
667 /*
668    ===============
669    Cmd_Mippal
670    ===============
671  */
672 void Cmd_Mippal( void ){
673         colormap_issued = true;
674         if ( g_release ) {
675                 return;
676         }
677
678         memcpy( colormap_palette, lbmpalette, 768 );
679
680         BuildPalmap();
681 }
682
683
684 /*
685    ===============
686    Cmd_Mipdir
687    ===============
688  */
689 void Cmd_Mipdir( void ){
690         char filename[1024];
691
692         GetToken( false );
693         strcpy( mip_prefix, token );
694         // create the directory if needed
695         sprintf( filename, "%stextures", gamedir, mip_prefix );
696         Q_mkdir( filename );
697         sprintf( filename, "%stextures/%s", gamedir, mip_prefix );
698         Q_mkdir( filename );
699 }
700
701
702 /*
703    =============================================================================
704
705    ENVIRONMENT MAP GRABBING
706
707    Creates six pcx files from tga files without any palette edge seams
708    also copies the tga files for GL rendering.
709    =============================================================================
710  */
711
712 // 3dstudio environment map suffixes
713 char    *suf[6] = {"rt", "ft", "lf", "bk", "up", "dn"};
714
715 /*
716    =================
717    Cmd_Environment
718    =================
719  */
720 void Cmd_Environment( void ){
721         char name[1024];
722         int i, x, y;
723         byte image[256 * 256];
724         byte    *tga;
725
726         GetToken( false );
727
728         if ( g_release ) {
729                 for ( i = 0 ; i < 6 ; i++ )
730                 {
731                         sprintf( name, "env/%s%s.pcx", token, suf[i] );
732                         ReleaseFile( name );
733                         sprintf( name, "env/%s%s.tga", token, suf[i] );
734                         ReleaseFile( name );
735                 }
736                 return;
737         }
738         // get the palette
739         BuildPalmap();
740
741         sprintf( name, "%senv/", gamedir );
742         CreatePath( name );
743
744         // convert the images
745         for ( i = 0 ; i < 6 ; i++ )
746         {
747                 sprintf( name, "%senv/%s%s.tga", gamedir, token, suf[i] );
748                 printf( "loading %s...\n", name );
749                 LoadTGA( name, &tga, NULL, NULL );
750
751                 for ( y = 0 ; y < 256 ; y++ )
752                 {
753                         for ( x = 0 ; x < 256 ; x++ )
754                         {
755                                 image[y * 256 + x] = FindColor( tga[( y * 256 + x ) * 4 + 0],tga[( y * 256 + x ) * 4 + 1],tga[( y * 256 + x ) * 4 + 2] );
756                         }
757                 }
758                 free( tga );
759                 sprintf( name, "%senv/%s%s.pcx", gamedir, token, suf[i] );
760                 if ( FileTime( name ) != -1 ) {
761                         printf( "%s already exists, not overwriting.\n", name );
762                 }
763                 else{
764                         WritePCXfile( name, image, 256, 256, colormap_palette );
765                 }
766         }
767 }