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