1 /*
2 ===========================================================================
3 Copyright (C) 1997-2006 Id Software, Inc.
4 
5 This file is part of Quake 2 Tools source code.
6 
7 Quake 2 Tools source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11 
12 Quake 2 Tools source code is distributed in the hope that it will be
13 useful, 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 Quake 2 Tools source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 ===========================================================================
21 */
22 
23 #include "qdata.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 */
RemapZero(byte * pixels,byte * palette,int width,int height)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 */
Cmd_Grab(void)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 */
Cmd_Raw(void)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 */
BestColor(int r,int g,int b,int start,int stop)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 */
Cmd_Colormap(void)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 */
FindColor(int r,int g,int b)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 
BuildPalmap(void)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 */
AveragePixels(int count)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 */
Cmd_Mip(void)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 */
Cmd_Mippal(void)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 */
Cmd_Mipdir(void)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 */
Cmd_Environment(void)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 
764