]> icculus.org git repositories - divverent/netradiant.git/blob - tools/quake3/q3map2/main.c
PROPERLY ignore sky brushes for minimap
[divverent/netradiant.git] / tools / quake3 / q3map2 / main.c
1 /* -------------------------------------------------------------------------------;
2
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
5
6 This file is part of GtkRadiant.
7
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21
22 -------------------------------------------------------------------------------
23
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
26
27 ------------------------------------------------------------------------------- */
28
29
30
31 /* marker */
32 #define MAIN_C
33
34
35
36 /* dependencies */
37 #include "q3map2.h"
38
39
40
41 /*
42 Random()
43 returns a pseudorandom number between 0 and 1
44 */
45
46 vec_t Random( void )
47 {
48         return (vec_t) rand() / RAND_MAX;
49 }
50
51
52
53 /*
54 ExitQ3Map()
55 cleanup routine
56 */
57
58 static void ExitQ3Map( void )
59 {
60         BSPFilesCleanup();
61         if( mapDrawSurfs != NULL )
62                 free( mapDrawSurfs );
63 }
64
65
66
67 /* minimap stuff */
68
69 typedef struct minimap_s
70 {
71         bspModel_t *model;
72         int width;
73         int height;
74         int samples;
75         float *sample_offsets;
76         float sharpen_boxmult;
77         float sharpen_centermult;
78         float boost, brightness, contrast;
79         float *data1f;
80         float *sharpendata1f;
81         vec3_t mins, size;
82 }
83 minimap_t;
84 static minimap_t minimap;
85
86 qboolean BrushIntersectionWithLine(bspBrush_t *brush, vec3_t start, vec3_t dir, float *t_in, float *t_out)
87 {
88         int i;
89         qboolean in = qfalse, out = qfalse;
90         bspBrushSide_t *sides = &bspBrushSides[brush->firstSide];
91
92         for(i = 0; i < brush->numSides; ++i)
93         {
94                 bspPlane_t *p = &bspPlanes[sides[i].planeNum];
95                 float sn = DotProduct(start, p->normal);
96                 float dn = DotProduct(dir, p->normal);
97                 if(dn == 0)
98                 {
99                         if(sn > p->dist)
100                                 return qfalse; // outside!
101                 }
102                 else
103                 {
104                         float t = (p->dist - sn) / dn;
105                         if(dn < 0)
106                         {
107                                 if(!in || t > *t_in)
108                                 {
109                                         *t_in = t;
110                                         in = qtrue;
111                                         // as t_in can only increase, and t_out can only decrease, early out
112                                         if(out && *t_in >= *t_out)
113                                                 return qfalse;
114                                 }
115                         }
116                         else
117                         {
118                                 if(!out || t < *t_out)
119                                 {
120                                         *t_out = t;
121                                         out = qtrue;
122                                         // as t_in can only increase, and t_out can only decrease, early out
123                                         if(in && *t_in >= *t_out)
124                                                 return qfalse;
125                                 }
126                         }
127                 }
128         }
129         return in && out;
130 }
131
132 static float MiniMapSample(float x, float y)
133 {
134         vec3_t org, dir;
135         int i, bi;
136         float t0, t1;
137         float samp;
138         bspBrush_t *b;
139         bspBrushSide_t *s;
140         int cnt;
141
142         org[0] = x;
143         org[1] = y;
144         org[2] = 0;
145         dir[0] = 0;
146         dir[1] = 0;
147         dir[2] = 1;
148
149         cnt = 0;
150         samp = 0;
151         for(i = 0; i < minimap.model->numBSPBrushes; ++i)
152         {
153                 bi = minimap.model->firstBSPBrush + i;
154                 if(opaqueBrushes[bi >> 3] & (1 << (bi & 7)))
155                 {
156                         b = &bspBrushes[bi];
157
158                         // sort out mins/maxs of the brush
159                         s = &bspBrushSides[b->firstSide];
160                         if(x < -bspPlanes[s[0].planeNum].dist)
161                                 continue;
162                         if(x > +bspPlanes[s[1].planeNum].dist)
163                                 continue;
164                         if(y < -bspPlanes[s[2].planeNum].dist)
165                                 continue;
166                         if(y > +bspPlanes[s[3].planeNum].dist)
167                                 continue;
168
169                         if(BrushIntersectionWithLine(b, org, dir, &t0, &t1))
170                         {
171                                 samp += t1 - t0;
172                                 ++cnt;
173                         }
174                 }
175         }
176
177         return samp;
178 }
179
180 void RandomVector2f(float v[2])
181 {
182         do
183         {
184                 v[0] = 2 * Random() - 1;
185                 v[1] = 2 * Random() - 1;
186         }
187         while(v[0] * v[0] + v[1] * v[1] > 1);
188 }
189
190 static void MiniMapRandomlySupersampled(int y)
191 {
192         int x, i;
193         float *p = &minimap.data1f[y * minimap.width];
194         float ymin = minimap.mins[1] + minimap.size[1] * (y / (float) minimap.height);
195         float dx   =                   minimap.size[0]      / (float) minimap.width;
196         float dy   =                   minimap.size[1]      / (float) minimap.height;
197         float uv[2];
198         float thisval;
199
200         for(x = 0; x < minimap.width; ++x)
201         {
202                 float xmin = minimap.mins[0] + minimap.size[0] * (x / (float) minimap.width);
203                 float val = 0;
204
205                 for(i = 0; i < minimap.samples; ++i)
206                 {
207                         RandomVector2f(uv);
208                         thisval = MiniMapSample(
209                                 xmin + (uv[0] + 0.5) * dx, /* exaggerated random pattern for better results */
210                                 ymin + (uv[1] + 0.5) * dy  /* exaggerated random pattern for better results */
211                         );
212                         val += thisval;
213                 }
214                 val /= minimap.samples * minimap.size[2];
215                 *p++ = val;
216         }
217 }
218
219 static void MiniMapSupersampled(int y)
220 {
221         int x, i;
222         float *p = &minimap.data1f[y * minimap.width];
223         float ymin = minimap.mins[1] + minimap.size[1] * (y / (float) minimap.height);
224         float dx   =                   minimap.size[0]      / (float) minimap.width;
225         float dy   =                   minimap.size[1]      / (float) minimap.height;
226
227         for(x = 0; x < minimap.width; ++x)
228         {
229                 float xmin = minimap.mins[0] + minimap.size[0] * (x / (float) minimap.width);
230                 float val = 0;
231
232                 for(i = 0; i < minimap.samples; ++i)
233                 {
234                         float thisval = MiniMapSample(
235                                 xmin + minimap.sample_offsets[2*i+0] * dx,
236                                 ymin + minimap.sample_offsets[2*i+1] * dy
237                         );
238                         val += thisval;
239                 }
240                 val /= minimap.samples * minimap.size[2];
241                 *p++ = val;
242         }
243 }
244
245 static void MiniMapNoSupersampling(int y)
246 {
247         int x;
248         float *p = &minimap.data1f[y * minimap.width];
249         float ymin = minimap.mins[1] + minimap.size[1] * ((y + 0.5) / (float) minimap.height);
250
251         for(x = 0; x < minimap.width; ++x)
252         {
253                 float xmin = minimap.mins[0] + minimap.size[0] * ((x + 0.5) / (float) minimap.width);
254                 *p++ = MiniMapSample(xmin, ymin) / minimap.size[2];
255         }
256 }
257
258 static void MiniMapSharpen(int y)
259 {
260         int x;
261         qboolean up = (y > 0);
262         qboolean down = (y < minimap.height - 1);
263         float *p = &minimap.data1f[y * minimap.width];
264         float *q = &minimap.sharpendata1f[y * minimap.width];
265
266         for(x = 0; x < minimap.width; ++x)
267         {
268                 qboolean left = (x > 0);
269                 qboolean right = (x < minimap.width - 1);
270                 float val = p[0] * minimap.sharpen_centermult;
271
272                 if(left && up)
273                         val += p[-1 -minimap.width] * minimap.sharpen_boxmult;
274                 if(left && down)
275                         val += p[-1 +minimap.width] * minimap.sharpen_boxmult;
276                 if(right && up)
277                         val += p[+1 -minimap.width] * minimap.sharpen_boxmult;
278                 if(right && down)
279                         val += p[+1 +minimap.width] * minimap.sharpen_boxmult;
280                         
281                 if(left)
282                         val += p[-1] * minimap.sharpen_boxmult;
283                 if(right)
284                         val += p[+1] * minimap.sharpen_boxmult;
285                 if(up)
286                         val += p[-minimap.width] * minimap.sharpen_boxmult;
287                 if(down)
288                         val += p[+minimap.width] * minimap.sharpen_boxmult;
289
290                 ++p;
291                 *q++ = val;
292         }
293 }
294
295 static void MiniMapContrastBoost(int y)
296 {
297         int x;
298         float *q = &minimap.data1f[y * minimap.width];
299         for(x = 0; x < minimap.width; ++x)
300         {
301                 *q = *q * minimap.boost / ((minimap.boost - 1) * *q + 1);
302                 ++q;
303         }
304 }
305
306 static void MiniMapBrightnessContrast(int y)
307 {
308         int x;
309         float *q = &minimap.data1f[y * minimap.width];
310         for(x = 0; x < minimap.width; ++x)
311         {
312                 *q = *q * minimap.contrast + minimap.brightness;
313                 ++q;
314         }
315 }
316
317 void MiniMapMakeMinsMaxs(vec3_t mins_in, vec3_t maxs_in, float border, qboolean keepaspect)
318 {
319         vec3_t mins, maxs, extend;
320         VectorCopy(mins_in, mins);
321         VectorCopy(maxs_in, maxs);
322
323         // line compatible to nexuiz mapinfo
324         Sys_Printf("size %f %f %f %f %f %f\n", mins[0], mins[1], mins[2], maxs[0], maxs[1], maxs[2]);
325
326         if(keepaspect)
327         {
328                 VectorSubtract(maxs, mins, extend);
329                 if(extend[1] > extend[0])
330                 {
331                         mins[0] -= (extend[1] - extend[0]) * 0.5;
332                         maxs[0] += (extend[1] - extend[0]) * 0.5;
333                 }
334                 else
335                 {
336                         mins[1] -= (extend[0] - extend[1]) * 0.5;
337                         maxs[1] += (extend[0] - extend[1]) * 0.5;
338                 }
339         }
340
341         /* border: amount of black area around the image */
342         /* input: border, 1-2*border, border but we need border/(1-2*border) */
343
344         VectorSubtract(maxs, mins, extend);
345         VectorScale(extend, border / (1 - 2 * border), extend);
346
347         VectorSubtract(mins, extend, mins);
348         VectorAdd(maxs, extend, maxs);
349
350         VectorCopy(mins, minimap.mins);
351         VectorSubtract(maxs, mins, minimap.size);
352
353         // line compatible to nexuiz mapinfo
354         Sys_Printf("size_texcoords %f %f %f %f %f %f\n", mins[0], mins[1], mins[2], maxs[0], maxs[1], maxs[2]);
355 }
356
357 /*
358 MiniMapSetupBrushes()
359 determines solid non-sky brushes in the world
360 */
361
362 void MiniMapSetupBrushes( void )
363 {
364         SetupBrushesFlags(C_SOLID | C_SKY, C_SOLID);
365 }
366
367 qboolean MiniMapEvaluateSampleOffsets(int *bestj, int *bestk, float *bestval)
368 {
369         float val, dx, dy;
370         int j, k;
371
372         *bestj = *bestk = -1;
373         *bestval = 3; /* max possible val is 2 */
374
375         for(j = 0; j < minimap.samples; ++j)
376                 for(k = j + 1; k < minimap.samples; ++k)
377                 {
378                         dx = minimap.sample_offsets[2*j+0] - minimap.sample_offsets[2*k+0];
379                         dy = minimap.sample_offsets[2*j+1] - minimap.sample_offsets[2*k+1];
380                         if(dx > +0.5) dx -= 1;
381                         if(dx < -0.5) dx += 1;
382                         if(dy > +0.5) dy -= 1;
383                         if(dy < -0.5) dy += 1;
384                         val = dx * dx + dy * dy;
385                         if(val < *bestval)
386                         {
387                                 *bestj = j;
388                                 *bestk = k;
389                                 *bestval = val;
390                         }
391                 }
392         
393         return *bestval < 3;
394 }
395
396 void MiniMapMakeSampleOffsets()
397 {
398         int i, j, k, jj, kk;
399         float val, valj, valk, sx, sy, rx, ry;
400
401         Sys_Printf( "Generating good sample offsets (this may take a while)...\n" );
402
403         /* start with entirely random samples */
404         for(i = 0; i < minimap.samples; ++i)
405         {
406                 minimap.sample_offsets[2*i+0] = Random();
407                 minimap.sample_offsets[2*i+1] = Random();
408         }
409
410         for(i = 0; i < 1000; ++i)
411         {
412                 if(MiniMapEvaluateSampleOffsets(&j, &k, &val))
413                 {
414                         sx = minimap.sample_offsets[2*j+0];
415                         sy = minimap.sample_offsets[2*j+1];
416                         minimap.sample_offsets[2*j+0] = rx = Random();
417                         minimap.sample_offsets[2*j+1] = ry = Random();
418                         if(!MiniMapEvaluateSampleOffsets(&jj, &kk, &valj))
419                                 valj = -1;
420                         minimap.sample_offsets[2*j+0] = sx;
421                         minimap.sample_offsets[2*j+1] = sy;
422
423                         sx = minimap.sample_offsets[2*k+0];
424                         sy = minimap.sample_offsets[2*k+1];
425                         minimap.sample_offsets[2*k+0] = rx;
426                         minimap.sample_offsets[2*k+1] = ry;
427                         if(!MiniMapEvaluateSampleOffsets(&jj, &kk, &valk))
428                                 valk = -1;
429                         minimap.sample_offsets[2*k+0] = sx;
430                         minimap.sample_offsets[2*k+1] = sy;
431
432                         if(valj > valk)
433                         {
434                                 if(valj > val)
435                                 {
436                                         /* valj is the greatest */
437                                         minimap.sample_offsets[2*j+0] = rx;
438                                         minimap.sample_offsets[2*j+1] = ry;
439                                         i = -1;
440                                 }
441                                 else
442                                 {
443                                         /* valj is the greater and it is useless - forget it */
444                                 }
445                         }
446                         else
447                         {
448                                 if(valk > val)
449                                 {
450                                         /* valk is the greatest */
451                                         minimap.sample_offsets[2*k+0] = rx;
452                                         minimap.sample_offsets[2*k+1] = ry;
453                                         i = -1;
454                                 }
455                                 else
456                                 {
457                                         /* valk is the greater and it is useless - forget it */
458                                 }
459                         }
460                 }
461                 else
462                         break;
463         }
464 }
465
466 void MergeRelativePath(char *out, const char *absolute, const char *relative)
467 {
468         const char *endpos = absolute + strlen(absolute);
469         while(endpos != absolute && (endpos[-1] == '/' || endpos[-1] == '\\'))
470                 --endpos;
471         while(relative[0] == '.' && relative[1] == '.' && (relative[2] == '/' || relative[2] == '\\'))
472         {
473                 relative += 3;
474                 while(endpos != absolute)
475                 {
476                         --endpos;
477                         if(*endpos == '/' || *endpos == '\\')
478                                 break;
479                 }
480                 while(endpos != absolute && (endpos[-1] == '/' || endpos[-1] == '\\'))
481                         --endpos;
482         }
483         memcpy(out, absolute, endpos - absolute);
484         out[endpos - absolute] = '/';
485         strcpy(out + (endpos - absolute + 1), relative);
486 }
487
488 int MiniMapBSPMain( int argc, char **argv )
489 {
490         char minimapFilename[1024];
491         char basename[1024];
492         char path[1024];
493         char relativeMinimapFilename[1024];
494         qboolean autolevel;
495         float minimapSharpen;
496         float border;
497         byte *data4b, *p;
498         float *q;
499         int x, y;
500         int i;
501         miniMapMode_t mode;
502         vec3_t mins, maxs;
503         qboolean keepaspect;
504
505         /* arg checking */
506         if( argc < 2 )
507         {
508                 Sys_Printf( "Usage: q3map [-v] -minimap [-size n] [-sharpen f] [-samples n | -random n] [-o filename.tga] [-minmax Xmin Ymin Zmin Xmax Ymax Zmax] <mapname>\n" );
509                 return 0;
510         }
511
512         /* load the BSP first */
513         strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
514         StripExtension( source );
515         DefaultExtension( source, ".bsp" );
516         Sys_Printf( "Loading %s\n", source );
517         BeginMapShaderFile( source );
518         LoadShaderInfo();
519         LoadBSPFile( source );
520
521         minimap.model = &bspModels[0];
522         VectorCopy(minimap.model->mins, mins);
523         VectorCopy(minimap.model->maxs, maxs);
524
525         *minimapFilename = 0;
526         minimapSharpen = game->miniMapSharpen;
527         minimap.width = minimap.height = game->miniMapSize;
528         border = game->miniMapBorder;
529         keepaspect = game->miniMapKeepAspect;
530         mode = game->miniMapMode;
531
532         autolevel = qfalse;
533         minimap.samples = 1;
534         minimap.sample_offsets = NULL;
535         minimap.boost = 1.0;
536         minimap.brightness = 0.0;
537         minimap.contrast = 1.0;
538
539         /* process arguments */
540         for( i = 1; i < (argc - 1); i++ )
541         {
542                 if( !strcmp( argv[ i ],  "-size" ) )
543                 {
544                         minimap.width = minimap.height = atoi(argv[i + 1]);
545                         i++;
546                         Sys_Printf( "Image size set to %i\n", minimap.width );
547                 }
548                 else if( !strcmp( argv[ i ],  "-sharpen" ) )
549                 {
550                         minimapSharpen = atof(argv[i + 1]);
551                         i++;
552                         Sys_Printf( "Sharpening coefficient set to %f\n", minimapSharpen );
553                 }
554                 else if( !strcmp( argv[ i ],  "-samples" ) )
555                 {
556                         minimap.samples = atoi(argv[i + 1]);
557                         i++;
558                         Sys_Printf( "Samples set to %i\n", minimap.samples );
559                         if(minimap.sample_offsets)
560                                 free(minimap.sample_offsets);
561                         minimap.sample_offsets = malloc(2 * sizeof(*minimap.sample_offsets) * minimap.samples);
562                         MiniMapMakeSampleOffsets();
563                 }
564                 else if( !strcmp( argv[ i ],  "-random" ) )
565                 {
566                         minimap.samples = atoi(argv[i + 1]);
567                         i++;
568                         Sys_Printf( "Random samples set to %i\n", minimap.samples );
569                         if(minimap.sample_offsets)
570                                 free(minimap.sample_offsets);
571                         minimap.sample_offsets = NULL;
572                 }
573                 else if( !strcmp( argv[ i ],  "-border" ) )
574                 {
575                         border = atof(argv[i + 1]);
576                         i++;
577                         Sys_Printf( "Border set to %f\n", border );
578                 }
579                 else if( !strcmp( argv[ i ],  "-keepaspect" ) )
580                 {
581                         keepaspect = qtrue;
582                         Sys_Printf( "Keeping aspect ratio by letterboxing\n", border );
583                 }
584                 else if( !strcmp( argv[ i ],  "-nokeepaspect" ) )
585                 {
586                         keepaspect = qfalse;
587                         Sys_Printf( "Not keeping aspect ratio\n", border );
588                 }
589                 else if( !strcmp( argv[ i ],  "-o" ) )
590                 {
591                         strcpy(minimapFilename, argv[i + 1]);
592                         i++;
593                         Sys_Printf( "Output file name set to %s\n", minimapFilename );
594                 }
595                 else if( !strcmp( argv[ i ],  "-minmax" ) && i < (argc - 7) )
596                 {
597                         mins[0] = atof(argv[i + 1]);
598                         mins[1] = atof(argv[i + 2]);
599                         mins[2] = atof(argv[i + 3]);
600                         maxs[0] = atof(argv[i + 4]);
601                         maxs[1] = atof(argv[i + 5]);
602                         maxs[2] = atof(argv[i + 6]);
603                         i += 6;
604                         Sys_Printf( "Map mins/maxs overridden\n" );
605                 }
606                 else if( !strcmp( argv[ i ],  "-gray" ) )
607                 {
608                         mode = MINIMAP_MODE_GRAY;
609                         Sys_Printf( "Writing as white-on-black image\n" );
610                 }
611                 else if( !strcmp( argv[ i ],  "-black" ) )
612                 {
613                         mode = MINIMAP_MODE_BLACK;
614                         Sys_Printf( "Writing as black alpha image\n" );
615                 }
616                 else if( !strcmp( argv[ i ],  "-white" ) )
617                 {
618                         mode = MINIMAP_MODE_WHITE;
619                         Sys_Printf( "Writing as white alpha image\n" );
620                 }
621                 else if( !strcmp( argv[ i ],  "-boost" ) && i < (argc - 2) )
622                 {
623                         minimap.boost = atof(argv[i + 1]);
624                         i++;
625                         Sys_Printf( "Contrast boost set to %f\n", minimap.boost );
626                 }
627                 else if( !strcmp( argv[ i ],  "-brightness" ) && i < (argc - 2) )
628                 {
629                         minimap.brightness = atof(argv[i + 1]);
630                         i++;
631                         Sys_Printf( "Brightness set to %f\n", minimap.brightness );
632                 }
633                 else if( !strcmp( argv[ i ],  "-contrast" ) && i < (argc - 2) )
634                 {
635                         minimap.contrast = atof(argv[i + 1]);
636                         i++;
637                         Sys_Printf( "Contrast set to %f\n", minimap.contrast );
638                 }
639                 else if( !strcmp( argv[ i ],  "-autolevel" ) )
640                 {
641                         autolevel = qtrue;
642                         Sys_Printf( "Auto level enabled\n", border );
643                 }
644                 else if( !strcmp( argv[ i ],  "-noautolevel" ) )
645                 {
646                         autolevel = qfalse;
647                         Sys_Printf( "Auto level disabled\n", border );
648                 }
649         }
650
651         MiniMapMakeMinsMaxs(mins, maxs, border, keepaspect);
652
653         if(!*minimapFilename)
654         {
655                 ExtractFileBase(source, basename);
656                 ExtractFilePath(source, path);
657                 sprintf(relativeMinimapFilename, game->miniMapNameFormat, basename);
658                 MergeRelativePath(minimapFilename, path, relativeMinimapFilename);
659                 Sys_Printf("Output file name automatically set to %s\n", minimapFilename);
660         }
661         ExtractFilePath(minimapFilename, path);
662         Q_mkdir(path);
663
664         if(minimapSharpen >= 0)
665         {
666                 minimap.sharpen_centermult = 8 * minimapSharpen + 1;
667                 minimap.sharpen_boxmult    =    -minimapSharpen;
668         }
669
670         minimap.data1f = safe_malloc(minimap.width * minimap.height * sizeof(*minimap.data1f));
671         data4b = safe_malloc(minimap.width * minimap.height * 4);
672         if(minimapSharpen >= 0)
673                 minimap.sharpendata1f = safe_malloc(minimap.width * minimap.height * sizeof(*minimap.data1f));
674
675         MiniMapSetupBrushes();
676
677         if(minimap.samples <= 1)
678         {
679                 Sys_Printf( "\n--- MiniMapNoSupersampling (%d) ---\n", minimap.height );
680                 RunThreadsOnIndividual(minimap.height, qtrue, MiniMapNoSupersampling);
681         }
682         else
683         {
684                 if(minimap.sample_offsets)
685                 {
686                         Sys_Printf( "\n--- MiniMapSupersampled (%d) ---\n", minimap.height );
687                         RunThreadsOnIndividual(minimap.height, qtrue, MiniMapSupersampled);
688                 }
689                 else
690                 {
691                         Sys_Printf( "\n--- MiniMapRandomlySupersampled (%d) ---\n", minimap.height );
692                         RunThreadsOnIndividual(minimap.height, qtrue, MiniMapRandomlySupersampled);
693                 }
694         }
695
696         if(minimap.boost != 1.0)
697         {
698                 Sys_Printf( "\n--- MiniMapContrastBoost (%d) ---\n", minimap.height );
699                 RunThreadsOnIndividual(minimap.height, qtrue, MiniMapContrastBoost);
700         }
701
702         if(autolevel)
703         {
704                 Sys_Printf( "\n--- MiniMapAutoLevel (%d) ---\n", minimap.height );
705                 float mi = 1, ma = 0;
706                 float s, o;
707
708                 // TODO threads!
709                 q = minimap.data1f;
710                 for(y = 0; y < minimap.height; ++y)
711                         for(x = 0; x < minimap.width; ++x)
712                         {
713                                 float v = *q++;
714                                 if(v < mi)
715                                         mi = v;
716                                 if(v > ma)
717                                         ma = v;
718                         }
719                 s = 1 / (ma - mi);
720                 o = mi / (ma - mi);
721
722                 // equations:
723                 //   brightness + contrast * v
724                 // after autolevel:
725                 //   brightness + contrast * (v * s - o)
726                 // =
727                 //   (brightness - contrast * o) + (contrast * s) * v
728                 minimap.brightness = minimap.brightness - minimap.contrast * o;
729                 minimap.contrast *= s;
730
731                 Sys_Printf( "Auto level: Brightness changed to %f\n", minimap.brightness );
732                 Sys_Printf( "Auto level: Contrast changed to %f\n", minimap.contrast );
733         }
734
735         if(minimap.brightness != 0 || minimap.contrast != 1)
736         {
737                 Sys_Printf( "\n--- MiniMapBrightnessContrast (%d) ---\n", minimap.height );
738                 RunThreadsOnIndividual(minimap.height, qtrue, MiniMapBrightnessContrast);
739         }
740
741         if(minimap.sharpendata1f)
742         {
743                 Sys_Printf( "\n--- MiniMapSharpen (%d) ---\n", minimap.height );
744                 RunThreadsOnIndividual(minimap.height, qtrue, MiniMapSharpen);
745                 q = minimap.sharpendata1f;
746         }
747         else
748         {
749                 q = minimap.data1f;
750         }
751
752         Sys_Printf( "\nConverting...");
753
754         switch(mode)
755         {
756                 case MINIMAP_MODE_GRAY:
757                         p = data4b;
758                         for(y = 0; y < minimap.height; ++y)
759                                 for(x = 0; x < minimap.width; ++x)
760                                 {
761                                         byte b;
762                                         float v = *q++;
763                                         if(v < 0) v = 0;
764                                         if(v > 255.0/256.0) v = 255.0/256.0;
765                                         b = v * 256;
766                                         *p++ = b;
767                                 }
768                         Sys_Printf( " writing to %s...", minimapFilename );
769                         WriteTGAGray(minimapFilename, data4b, minimap.width, minimap.height);
770                         break;
771                 case MINIMAP_MODE_BLACK:
772                         p = data4b;
773                         for(y = 0; y < minimap.height; ++y)
774                                 for(x = 0; x < minimap.width; ++x)
775                                 {
776                                         byte b;
777                                         float v = *q++;
778                                         if(v < 0) v = 0;
779                                         if(v > 255.0/256.0) v = 255.0/256.0;
780                                         b = v * 256;
781                                         *p++ = 0;
782                                         *p++ = 0;
783                                         *p++ = 0;
784                                         *p++ = b;
785                                 }
786                         Sys_Printf( " writing to %s...", minimapFilename );
787                         WriteTGA(minimapFilename, data4b, minimap.width, minimap.height);
788                         break;
789                 case MINIMAP_MODE_WHITE:
790                         p = data4b;
791                         for(y = 0; y < minimap.height; ++y)
792                                 for(x = 0; x < minimap.width; ++x)
793                                 {
794                                         byte b;
795                                         float v = *q++;
796                                         if(v < 0) v = 0;
797                                         if(v > 255.0/256.0) v = 255.0/256.0;
798                                         b = v * 256;
799                                         *p++ = 255;
800                                         *p++ = 255;
801                                         *p++ = 255;
802                                         *p++ = b;
803                                 }
804                         Sys_Printf( " writing to %s...", minimapFilename );
805                         WriteTGA(minimapFilename, data4b, minimap.width, minimap.height);
806                         break;
807         }
808
809         Sys_Printf( " done.\n" );
810
811         /* return to sender */
812         return 0;
813 }
814
815
816
817
818
819 /*
820 MD4BlockChecksum()
821 calculates an md4 checksum for a block of data
822 */
823
824 static int MD4BlockChecksum( void *buffer, int length )
825 {
826         return Com_BlockChecksum(buffer, length);
827 }
828
829 /*
830 FixAAS()
831 resets an aas checksum to match the given BSP
832 */
833
834 int FixAAS( int argc, char **argv )
835 {
836         int                     length, checksum;
837         void            *buffer;
838         FILE            *file;
839         char            aas[ 1024 ], **ext;
840         char            *exts[] =
841                                 {
842                                         ".aas",
843                                         "_b0.aas",
844                                         "_b1.aas",
845                                         NULL
846                                 };
847         
848         
849         /* arg checking */
850         if( argc < 2 )
851         {
852                 Sys_Printf( "Usage: q3map -fixaas [-v] <mapname>\n" );
853                 return 0;
854         }
855         
856         /* do some path mangling */
857         strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
858         StripExtension( source );
859         DefaultExtension( source, ".bsp" );
860         
861         /* note it */
862         Sys_Printf( "--- FixAAS ---\n" );
863         
864         /* load the bsp */
865         Sys_Printf( "Loading %s\n", source );
866         length = LoadFile( source, &buffer );
867         
868         /* create bsp checksum */
869         Sys_Printf( "Creating checksum...\n" );
870         checksum = LittleLong( MD4BlockChecksum( buffer, length ) );
871         
872         /* write checksum to aas */
873         ext = exts;
874         while( *ext )
875         {
876                 /* mangle name */
877                 strcpy( aas, source );
878                 StripExtension( aas );
879                 strcat( aas, *ext );
880                 Sys_Printf( "Trying %s\n", aas );
881                 ext++;
882                 
883                 /* fix it */
884                 file = fopen( aas, "r+b" );
885                 if( !file )
886                         continue;
887                 if( fwrite( &checksum, 4, 1, file ) != 1 )
888                         Error( "Error writing checksum to %s", aas );
889                 fclose( file );
890         }
891         
892         /* return to sender */
893         return 0;
894 }
895
896
897
898 /*
899 AnalyzeBSP() - ydnar
900 analyzes a Quake engine BSP file
901 */
902
903 typedef struct abspHeader_s
904 {
905         char                    ident[ 4 ];
906         int                             version;
907         
908         bspLump_t               lumps[ 1 ];     /* unknown size */
909 }
910 abspHeader_t;
911
912 typedef struct abspLumpTest_s
913 {
914         int                             radix, minCount;
915         char                    *name;
916 }
917 abspLumpTest_t;
918
919 int AnalyzeBSP( int argc, char **argv )
920 {
921         abspHeader_t                    *header;
922         int                                             size, i, version, offset, length, lumpInt, count;
923         char                                    ident[ 5 ];
924         void                                    *lump;
925         float                                   lumpFloat;
926         char                                    lumpString[ 1024 ], source[ 1024 ];
927         qboolean                                lumpSwap = qfalse;
928         abspLumpTest_t                  *lumpTest;
929         static abspLumpTest_t   lumpTests[] =
930                                                         {
931                                                                 { sizeof( bspPlane_t ),                 6,              "IBSP LUMP_PLANES" },
932                                                                 { sizeof( bspBrush_t ),                 1,              "IBSP LUMP_BRUSHES" },
933                                                                 { 8,                                                    6,              "IBSP LUMP_BRUSHSIDES" },
934                                                                 { sizeof( bspBrushSide_t ),             6,              "RBSP LUMP_BRUSHSIDES" },
935                                                                 { sizeof( bspModel_t ),                 1,              "IBSP LUMP_MODELS" },
936                                                                 { sizeof( bspNode_t ),                  2,              "IBSP LUMP_NODES" },
937                                                                 { sizeof( bspLeaf_t ),                  1,              "IBSP LUMP_LEAFS" },
938                                                                 { 104,                                                  3,              "IBSP LUMP_DRAWSURFS" },
939                                                                 { 44,                                                   3,              "IBSP LUMP_DRAWVERTS" },
940                                                                 { 4,                                                    6,              "IBSP LUMP_DRAWINDEXES" },
941                                                                 { 128 * 128 * 3,                                1,              "IBSP LUMP_LIGHTMAPS" },
942                                                                 { 256 * 256 * 3,                                1,              "IBSP LUMP_LIGHTMAPS (256 x 256)" },
943                                                                 { 512 * 512 * 3,                                1,              "IBSP LUMP_LIGHTMAPS (512 x 512)" },
944                                                                 { 0, 0, NULL }
945                                                         };
946         
947         
948         /* arg checking */
949         if( argc < 1 )
950         {
951                 Sys_Printf( "Usage: q3map -analyze [-lumpswap] [-v] <mapname>\n" );
952                 return 0;
953         }
954         
955         /* process arguments */
956         for( i = 1; i < (argc - 1); i++ )
957         {
958                 /* -format map|ase|... */
959                 if( !strcmp( argv[ i ],  "-lumpswap" ) )
960                 {
961                         Sys_Printf( "Swapped lump structs enabled\n" );
962                         lumpSwap = qtrue;
963                 }
964         }
965         
966         /* clean up map name */
967         strcpy( source, ExpandArg( argv[ i ] ) );
968         Sys_Printf( "Loading %s\n", source );
969         
970         /* load the file */
971         size = LoadFile( source, (void**) &header );
972         if( size == 0 || header == NULL )
973         {
974                 Sys_Printf( "Unable to load %s.\n", source );
975                 return -1;
976         }
977         
978         /* analyze ident/version */
979         memcpy( ident, header->ident, 4 );
980         ident[ 4 ] = '\0';
981         version = LittleLong( header->version );
982         
983         Sys_Printf( "Identity:      %s\n", ident );
984         Sys_Printf( "Version:       %d\n", version );
985         Sys_Printf( "---------------------------------------\n" );
986         
987         /* analyze each lump */
988         for( i = 0; i < 100; i++ )
989         {
990                 /* call of duty swapped lump pairs */
991                 if( lumpSwap )
992                 {
993                         offset = LittleLong( header->lumps[ i ].length );
994                         length = LittleLong( header->lumps[ i ].offset );
995                 }
996                 
997                 /* standard lump pairs */
998                 else
999                 {
1000                         offset = LittleLong( header->lumps[ i ].offset );
1001                         length = LittleLong( header->lumps[ i ].length );
1002                 }
1003                 
1004                 /* extract data */
1005                 lump = (byte*) header + offset;
1006                 lumpInt = LittleLong( (int) *((int*) lump) );
1007                 lumpFloat = LittleFloat( (float) *((float*) lump) );
1008                 memcpy( lumpString, (char*) lump, ((size_t)length < sizeof(lumpString) ? (size_t)length : sizeof(lumpString)-1) );
1009                 lumpString[ sizeof(lumpString)-1 ] = '\0';
1010                 
1011                 /* print basic lump info */
1012                 Sys_Printf( "Lump:          %d\n", i );
1013                 Sys_Printf( "Offset:        %d bytes\n", offset );
1014                 Sys_Printf( "Length:        %d bytes\n", length );
1015                 
1016                 /* only operate on valid lumps */
1017                 if( length > 0 )
1018                 {
1019                         /* print data in 4 formats */
1020                         Sys_Printf( "As hex:        %08X\n", lumpInt );
1021                         Sys_Printf( "As int:        %d\n", lumpInt );
1022                         Sys_Printf( "As float:      %f\n", lumpFloat );
1023                         Sys_Printf( "As string:     %s\n", lumpString );
1024                         
1025                         /* guess lump type */
1026                         if( lumpString[ 0 ] == '{' && lumpString[ 2 ] == '"' )
1027                                 Sys_Printf( "Type guess:    IBSP LUMP_ENTITIES\n" );
1028                         else if( strstr( lumpString, "textures/" ) )
1029                                 Sys_Printf( "Type guess:    IBSP LUMP_SHADERS\n" );
1030                         else
1031                         {
1032                                 /* guess based on size/count */
1033                                 for( lumpTest = lumpTests; lumpTest->radix > 0; lumpTest++ )
1034                                 {
1035                                         if( (length % lumpTest->radix) != 0 )
1036                                                 continue;
1037                                         count = length / lumpTest->radix;
1038                                         if( count < lumpTest->minCount )
1039                                                 continue;
1040                                         Sys_Printf( "Type guess:    %s (%d x %d)\n", lumpTest->name, count, lumpTest->radix );
1041                                 }
1042                         }
1043                 }
1044                 
1045                 Sys_Printf( "---------------------------------------\n" );
1046                 
1047                 /* end of file */
1048                 if( offset + length >= size )
1049                         break;
1050         }
1051         
1052         /* last stats */
1053         Sys_Printf( "Lump count:    %d\n", i + 1 );
1054         Sys_Printf( "File size:     %d bytes\n", size );
1055         
1056         /* return to caller */
1057         return 0;
1058 }
1059
1060
1061
1062 /*
1063 BSPInfo()
1064 emits statistics about the bsp file
1065 */
1066
1067 int BSPInfo( int count, char **fileNames )
1068 {
1069         int                     i;
1070         char            source[ 1024 ], ext[ 64 ];
1071         int                     size;
1072         FILE            *f;
1073         
1074         
1075         /* dummy check */
1076         if( count < 1 )
1077         {
1078                 Sys_Printf( "No files to dump info for.\n");
1079                 return -1;
1080         }
1081         
1082         /* enable info mode */
1083         infoMode = qtrue;
1084         
1085         /* walk file list */
1086         for( i = 0; i < count; i++ )
1087         {
1088                 Sys_Printf( "---------------------------------\n" );
1089                 
1090                 /* mangle filename and get size */
1091                 strcpy( source, fileNames[ i ] );
1092                 ExtractFileExtension( source, ext );
1093                 if( !Q_stricmp( ext, "map" ) )
1094                         StripExtension( source );
1095                 DefaultExtension( source, ".bsp" );
1096                 f = fopen( source, "rb" );
1097                 if( f )
1098                 {
1099                         size = Q_filelength (f);
1100                         fclose( f );
1101                 }
1102                 else
1103                         size = 0;
1104                 
1105                 /* load the bsp file and print lump sizes */
1106                 Sys_Printf( "%s\n", source );
1107                 LoadBSPFile( source );          
1108                 PrintBSPFileSizes();
1109                 
1110                 /* print sizes */
1111                 Sys_Printf( "\n" );
1112                 Sys_Printf( "          total         %9d\n", size );
1113                 Sys_Printf( "                        %9d KB\n", size / 1024 );
1114                 Sys_Printf( "                        %9d MB\n", size / (1024 * 1024) );
1115                 
1116                 Sys_Printf( "---------------------------------\n" );
1117         }
1118         
1119         /* return count */
1120         return i;
1121 }
1122
1123
1124 static void ExtrapolateTexcoords(const float *axyz, const float *ast, const float *bxyz, const float *bst, const float *cxyz, const float *cst, const float *axyz_new, float *ast_out, const float *bxyz_new, float *bst_out, const float *cxyz_new, float *cst_out)
1125 {
1126         vec4_t scoeffs, tcoeffs;
1127         float md;
1128         m4x4_t solvematrix;
1129
1130         vec3_t norm;
1131         vec3_t dab, dac;
1132         VectorSubtract(bxyz, axyz, dab);
1133         VectorSubtract(cxyz, axyz, dac);
1134         CrossProduct(dab, dac, norm);
1135         
1136         // assume:
1137         //   s = f(x, y, z)
1138         //   s(v + norm) = s(v) when n ortho xyz
1139         
1140         // s(v) = DotProduct(v, scoeffs) + scoeffs[3]
1141
1142         // solve:
1143         //   scoeffs * (axyz, 1) == ast[0]
1144         //   scoeffs * (bxyz, 1) == bst[0]
1145         //   scoeffs * (cxyz, 1) == cst[0]
1146         //   scoeffs * (norm, 0) == 0
1147         // scoeffs * [axyz, 1 | bxyz, 1 | cxyz, 1 | norm, 0] = [ast[0], bst[0], cst[0], 0]
1148         solvematrix[0] = axyz[0];
1149         solvematrix[4] = axyz[1];
1150         solvematrix[8] = axyz[2];
1151         solvematrix[12] = 1;
1152         solvematrix[1] = bxyz[0];
1153         solvematrix[5] = bxyz[1];
1154         solvematrix[9] = bxyz[2];
1155         solvematrix[13] = 1;
1156         solvematrix[2] = cxyz[0];
1157         solvematrix[6] = cxyz[1];
1158         solvematrix[10] = cxyz[2];
1159         solvematrix[14] = 1;
1160         solvematrix[3] = norm[0];
1161         solvematrix[7] = norm[1];
1162         solvematrix[11] = norm[2];
1163         solvematrix[15] = 0;
1164
1165         md = m4_det(solvematrix);
1166         if(md*md < 1e-10)
1167         {
1168                 Sys_Printf("Cannot invert some matrix, some texcoords aren't extrapolated!");
1169                 return;
1170         }
1171
1172         m4x4_invert(solvematrix);
1173
1174         scoeffs[0] = ast[0];
1175         scoeffs[1] = bst[0];
1176         scoeffs[2] = cst[0];
1177         scoeffs[3] = 0;
1178         m4x4_transform_vec4(solvematrix, scoeffs);
1179         tcoeffs[0] = ast[1];
1180         tcoeffs[1] = bst[1];
1181         tcoeffs[2] = cst[1];
1182         tcoeffs[3] = 0;
1183         m4x4_transform_vec4(solvematrix, tcoeffs);
1184
1185         ast_out[0] = scoeffs[0] * axyz_new[0] + scoeffs[1] * axyz_new[1] + scoeffs[2] * axyz_new[2] + scoeffs[3];
1186         ast_out[1] = tcoeffs[0] * axyz_new[0] + tcoeffs[1] * axyz_new[1] + tcoeffs[2] * axyz_new[2] + tcoeffs[3];
1187         bst_out[0] = scoeffs[0] * bxyz_new[0] + scoeffs[1] * bxyz_new[1] + scoeffs[2] * bxyz_new[2] + scoeffs[3];
1188         bst_out[1] = tcoeffs[0] * bxyz_new[0] + tcoeffs[1] * bxyz_new[1] + tcoeffs[2] * bxyz_new[2] + tcoeffs[3];
1189         cst_out[0] = scoeffs[0] * cxyz_new[0] + scoeffs[1] * cxyz_new[1] + scoeffs[2] * cxyz_new[2] + scoeffs[3];
1190         cst_out[1] = tcoeffs[0] * cxyz_new[0] + tcoeffs[1] * cxyz_new[1] + tcoeffs[2] * cxyz_new[2] + tcoeffs[3];
1191 }
1192
1193 /*
1194 ScaleBSPMain()
1195 amaze and confuse your enemies with wierd scaled maps!
1196 */
1197
1198 int ScaleBSPMain( int argc, char **argv )
1199 {
1200         int                     i, j;
1201         float           f, a;
1202         vec3_t scale;
1203         vec3_t          vec;
1204         char            str[ 1024 ];
1205         int uniform, axis;
1206         qboolean texscale;
1207         float *old_xyzst = NULL;
1208         float spawn_ref = 0;
1209         
1210         
1211         /* arg checking */
1212         if( argc < 3 )
1213         {
1214                 Sys_Printf( "Usage: q3map [-v] -scale [-tex] [-spawn_ref <value>] <value> <mapname>\n" );
1215                 return 0;
1216         }
1217         
1218         texscale = qfalse;
1219         for(i = 1; i < argc-2; ++i)
1220         {
1221                 if(!strcmp(argv[i], "-tex"))
1222                 {
1223                         texscale = qtrue;
1224                 }
1225                 else if(!strcmp(argv[i], "-spawn_ref"))
1226                 {
1227                         spawn_ref = atof(argv[i+1]);
1228                         ++i;
1229                 }
1230                 else
1231                         break;
1232         }
1233         
1234         /* get scale */
1235         // if(argc-2 >= i) // always true
1236                 scale[2] = scale[1] = scale[0] = atof( argv[ argc - 2 ] );
1237         if(argc-3 >= i)
1238                 scale[1] = scale[0] = atof( argv[ argc - 3 ] );
1239         if(argc-4 >= i)
1240                 scale[0] = atof( argv[ argc - 4 ] );
1241
1242         uniform = ((scale[0] == scale[1]) && (scale[1] == scale[2]));
1243
1244         if( scale[0] == 0.0f || scale[1] == 0.0f || scale[2] == 0.0f )
1245         {
1246                 Sys_Printf( "Usage: q3map [-v] -scale [-tex] [-spawn_ref <value>] <value> <mapname>\n" );
1247                 Sys_Printf( "Non-zero scale value required.\n" );
1248                 return 0;
1249         }
1250         
1251         /* do some path mangling */
1252         strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
1253         StripExtension( source );
1254         DefaultExtension( source, ".bsp" );
1255         
1256         /* load the bsp */
1257         Sys_Printf( "Loading %s\n", source );
1258         LoadBSPFile( source );
1259         ParseEntities();
1260         
1261         /* note it */
1262         Sys_Printf( "--- ScaleBSP ---\n" );
1263         Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );
1264         
1265         /* scale entity keys */
1266         for( i = 0; i < numBSPEntities && i < numEntities; i++ )
1267         {
1268                 /* scale origin */
1269                 GetVectorForKey( &entities[ i ], "origin", vec );
1270                 if( (vec[ 0 ] || vec[ 1 ] || vec[ 2 ]) )
1271                 {
1272                         if(!strncmp(ValueForKey(&entities[i], "classname"), "info_player_", 12))
1273                                 vec[2] += spawn_ref;
1274                         vec[0] *= scale[0];
1275                         vec[1] *= scale[1];
1276                         vec[2] *= scale[2];
1277                         if(!strncmp(ValueForKey(&entities[i], "classname"), "info_player_", 12))
1278                                 vec[2] -= spawn_ref;
1279                         sprintf( str, "%f %f %f", vec[ 0 ], vec[ 1 ], vec[ 2 ] );
1280                         SetKeyValue( &entities[ i ], "origin", str );
1281                 }
1282
1283                 a = FloatForKey( &entities[ i ], "angle" );
1284                 if(a == -1 || a == -2) // z scale
1285                         axis = 2;
1286                 else if(fabs(sin(DEG2RAD(a))) < 0.707)
1287                         axis = 0;
1288                 else
1289                         axis = 1;
1290                 
1291                 /* scale door lip */
1292                 f = FloatForKey( &entities[ i ], "lip" );
1293                 if( f )
1294                 {
1295                         f *= scale[axis];
1296                         sprintf( str, "%f", f );
1297                         SetKeyValue( &entities[ i ], "lip", str );
1298                 }
1299                 
1300                 /* scale plat height */
1301                 f = FloatForKey( &entities[ i ], "height" );
1302                 if( f )
1303                 {
1304                         f *= scale[2];
1305                         sprintf( str, "%f", f );
1306                         SetKeyValue( &entities[ i ], "height", str );
1307                 }
1308
1309                 // TODO maybe allow a definition file for entities to specify which values are scaled how?
1310         }
1311         
1312         /* scale models */
1313         for( i = 0; i < numBSPModels; i++ )
1314         {
1315                 bspModels[ i ].mins[0] *= scale[0];
1316                 bspModels[ i ].mins[1] *= scale[1];
1317                 bspModels[ i ].mins[2] *= scale[2];
1318                 bspModels[ i ].maxs[0] *= scale[0];
1319                 bspModels[ i ].maxs[1] *= scale[1];
1320                 bspModels[ i ].maxs[2] *= scale[2];
1321         }
1322         
1323         /* scale nodes */
1324         for( i = 0; i < numBSPNodes; i++ )
1325         {
1326                 bspNodes[ i ].mins[0] *= scale[0];
1327                 bspNodes[ i ].mins[1] *= scale[1];
1328                 bspNodes[ i ].mins[2] *= scale[2];
1329                 bspNodes[ i ].maxs[0] *= scale[0];
1330                 bspNodes[ i ].maxs[1] *= scale[1];
1331                 bspNodes[ i ].maxs[2] *= scale[2];
1332         }
1333         
1334         /* scale leafs */
1335         for( i = 0; i < numBSPLeafs; i++ )
1336         {
1337                 bspLeafs[ i ].mins[0] *= scale[0];
1338                 bspLeafs[ i ].mins[1] *= scale[1];
1339                 bspLeafs[ i ].mins[2] *= scale[2];
1340                 bspLeafs[ i ].maxs[0] *= scale[0];
1341                 bspLeafs[ i ].maxs[1] *= scale[1];
1342                 bspLeafs[ i ].maxs[2] *= scale[2];
1343         }
1344         
1345         if(texscale)
1346         {
1347                 Sys_Printf("Using texture unlocking (and probably breaking texture alignment a lot)\n");
1348                 old_xyzst = safe_malloc(sizeof(*old_xyzst) * numBSPDrawVerts * 5);
1349                 for(i = 0; i < numBSPDrawVerts; i++)
1350                 {
1351                         old_xyzst[5*i+0] = bspDrawVerts[i].xyz[0];
1352                         old_xyzst[5*i+1] = bspDrawVerts[i].xyz[1];
1353                         old_xyzst[5*i+2] = bspDrawVerts[i].xyz[2];
1354                         old_xyzst[5*i+3] = bspDrawVerts[i].st[0];
1355                         old_xyzst[5*i+4] = bspDrawVerts[i].st[1];
1356                 }
1357         }
1358
1359         /* scale drawverts */
1360         for( i = 0; i < numBSPDrawVerts; i++ )
1361         {
1362                 bspDrawVerts[i].xyz[0] *= scale[0];
1363                 bspDrawVerts[i].xyz[1] *= scale[1];
1364                 bspDrawVerts[i].xyz[2] *= scale[2];
1365                 bspDrawVerts[i].normal[0] /= scale[0];
1366                 bspDrawVerts[i].normal[1] /= scale[1];
1367                 bspDrawVerts[i].normal[2] /= scale[2];
1368                 VectorNormalize(bspDrawVerts[i].normal, bspDrawVerts[i].normal);
1369         }
1370
1371         if(texscale)
1372         {
1373                 for(i = 0; i < numBSPDrawSurfaces; i++)
1374                 {
1375                         switch(bspDrawSurfaces[i].surfaceType)
1376                         {
1377                                 case SURFACE_FACE:
1378                                 case SURFACE_META:
1379                                         if(bspDrawSurfaces[i].numIndexes % 3)
1380                                                 Error("Not a triangulation!");
1381                                         for(j = bspDrawSurfaces[i].firstIndex; j < bspDrawSurfaces[i].firstIndex + bspDrawSurfaces[i].numIndexes; j += 3)
1382                                         {
1383                                                 int ia = bspDrawIndexes[j] + bspDrawSurfaces[i].firstVert, ib = bspDrawIndexes[j+1] + bspDrawSurfaces[i].firstVert, ic = bspDrawIndexes[j+2] + bspDrawSurfaces[i].firstVert;
1384                                                 bspDrawVert_t *a = &bspDrawVerts[ia], *b = &bspDrawVerts[ib], *c = &bspDrawVerts[ic];
1385                                                 float *oa = &old_xyzst[ia*5], *ob = &old_xyzst[ib*5], *oc = &old_xyzst[ic*5];
1386                                                 // extrapolate:
1387                                                 //   a->xyz -> oa
1388                                                 //   b->xyz -> ob
1389                                                 //   c->xyz -> oc
1390                                                 ExtrapolateTexcoords(
1391                                                         &oa[0], &oa[3],
1392                                                         &ob[0], &ob[3],
1393                                                         &oc[0], &oc[3],
1394                                                         a->xyz, a->st,
1395                                                         b->xyz, b->st,
1396                                                         c->xyz, c->st);
1397                                         }
1398                                         break;
1399                         }
1400                 }
1401         }
1402         
1403         /* scale planes */
1404         if(uniform)
1405         {
1406                 for( i = 0; i < numBSPPlanes; i++ )
1407                 {
1408                         bspPlanes[ i ].dist *= scale[0];
1409                 }
1410         }
1411         else
1412         {
1413                 for( i = 0; i < numBSPPlanes; i++ )
1414                 {
1415                         bspPlanes[ i ].normal[0] /= scale[0];
1416                         bspPlanes[ i ].normal[1] /= scale[1];
1417                         bspPlanes[ i ].normal[2] /= scale[2];
1418                         f = 1/VectorLength(bspPlanes[i].normal);
1419                         VectorScale(bspPlanes[i].normal, f, bspPlanes[i].normal);
1420                         bspPlanes[ i ].dist *= f;
1421                 }
1422         }
1423         
1424         /* scale gridsize */
1425         GetVectorForKey( &entities[ 0 ], "gridsize", vec );
1426         if( (vec[ 0 ] + vec[ 1 ] + vec[ 2 ]) == 0.0f )
1427                 VectorCopy( gridSize, vec );
1428         vec[0] *= scale[0];
1429         vec[1] *= scale[1];
1430         vec[2] *= scale[2];
1431         sprintf( str, "%f %f %f", vec[ 0 ], vec[ 1 ], vec[ 2 ] );
1432         SetKeyValue( &entities[ 0 ], "gridsize", str );
1433
1434         /* inject command line parameters */
1435         InjectCommandLine(argv, 0, argc - 1);
1436         
1437         /* write the bsp */
1438         UnparseEntities();
1439         StripExtension( source );
1440         DefaultExtension( source, "_s.bsp" );
1441         Sys_Printf( "Writing %s\n", source );
1442         WriteBSPFile( source );
1443         
1444         /* return to sender */
1445         return 0;
1446 }
1447
1448
1449 /*
1450 PseudoCompileBSP()
1451 a stripped down ProcessModels
1452 */
1453 void PseudoCompileBSP(qboolean need_tree)
1454 {
1455         int models;
1456         char modelValue[10];
1457         entity_t *entity;
1458         face_t *faces;
1459         tree_t *tree;
1460         node_t *node;
1461         brush_t *brush;
1462         side_t *side;
1463         int i;
1464
1465         SetDrawSurfacesBuffer();
1466         mapDrawSurfs = safe_malloc( sizeof( mapDrawSurface_t ) * MAX_MAP_DRAW_SURFS );
1467         memset( mapDrawSurfs, 0, sizeof( mapDrawSurface_t ) * MAX_MAP_DRAW_SURFS );
1468         numMapDrawSurfs = 0;
1469
1470         BeginBSPFile();
1471         models = 1;
1472         for( mapEntityNum = 0; mapEntityNum < numEntities; mapEntityNum++ )
1473         {
1474                 /* get entity */
1475                 entity = &entities[ mapEntityNum ];
1476                 if( entity->brushes == NULL && entity->patches == NULL )
1477                         continue;
1478
1479                 if(mapEntityNum != 0)
1480                 {
1481                         sprintf( modelValue, "*%d", models++);
1482                         SetKeyValue(entity, "model", modelValue);
1483                 }
1484                 
1485                 /* process the model */
1486                 Sys_FPrintf( SYS_VRB, "############### model %i ###############\n", numBSPModels );
1487                 BeginModel();
1488
1489                 entity->firstDrawSurf = numMapDrawSurfs;
1490
1491                 ClearMetaTriangles();
1492                 PatchMapDrawSurfs(entity);
1493
1494                 if(mapEntityNum == 0 && need_tree)
1495                 {
1496                         faces = MakeStructuralBSPFaceList(entities[0].brushes);
1497                         tree = FaceBSP(faces);
1498                         node = tree->headnode;
1499                 }
1500                 else
1501                 {
1502                         node = AllocNode();
1503                         node->planenum = PLANENUM_LEAF;
1504                         tree = AllocTree();
1505                         tree->headnode = node;
1506                 }
1507
1508                 /* a minimized ClipSidesIntoTree */
1509                 for( brush = entity->brushes; brush; brush = brush->next )
1510                 {
1511                         /* walk the brush sides */
1512                         for( i = 0; i < brush->numsides; i++ )
1513                         {
1514                                 /* get side */
1515                                 side = &brush->sides[ i ];
1516                                 if( side->winding == NULL )
1517                                         continue;
1518                                 /* shader? */
1519                                 if( side->shaderInfo == NULL )
1520                                         continue;
1521                                 /* save this winding as a visible surface */
1522                                 DrawSurfaceForSide(entity, brush, side, side->winding);
1523                         }
1524                 }
1525
1526                 if(meta)
1527                 {
1528                         ClassifyEntitySurfaces(entity);
1529                         MakeEntityDecals(entity);
1530                         MakeEntityMetaTriangles(entity);
1531                         SmoothMetaTriangles();
1532                         MergeMetaTriangles();
1533                 }
1534                 FilterDrawsurfsIntoTree(entity, tree);
1535
1536                 FilterStructuralBrushesIntoTree(entity, tree);
1537                 FilterDetailBrushesIntoTree(entity, tree);
1538
1539                 EmitBrushes(entity->brushes, &entity->firstBrush, &entity->numBrushes );
1540                 EndModel(entity, node);
1541         }
1542         EndBSPFile(qfalse);
1543 }
1544
1545 /*
1546 ConvertBSPMain()
1547 main argument processing function for bsp conversion
1548 */
1549
1550 int ConvertBSPMain( int argc, char **argv )
1551 {
1552         int             i;
1553         int             (*convertFunc)( char * );
1554         game_t  *convertGame;
1555         char            ext[1024];
1556         qboolean        map_allowed, force_bsp, force_map;
1557         
1558         
1559         /* set default */
1560         convertFunc = ConvertBSPToASE;
1561         convertGame = NULL;
1562         map_allowed = qfalse;
1563         force_bsp = qfalse;
1564         force_map = qfalse;
1565         
1566         /* arg checking */
1567         if( argc < 1 )
1568         {
1569                 Sys_Printf( "Usage: q3map -convert -format <ase|obj|map_bp|map> [-shadesasbitmap|-lightmapsastexcoord|-deluxemapsastexcoord] [-readbsp|-readmap [-meta|-patchmeta]] <mapname>\n" );
1570                 return 0;
1571         }
1572         
1573         /* process arguments */
1574         for( i = 1; i < (argc - 1); i++ )
1575         {
1576                 /* -format map|ase|... */
1577                 if( !strcmp( argv[ i ],  "-format" ) )
1578                 {
1579                         i++;
1580                         if( !Q_stricmp( argv[ i ], "ase" ) )
1581                         {
1582                                 convertFunc = ConvertBSPToASE;
1583                                 map_allowed = qfalse;
1584                         }
1585                         else if( !Q_stricmp( argv[ i ], "obj" ) )
1586                         {
1587                                 convertFunc = ConvertBSPToOBJ;
1588                                 map_allowed = qfalse;
1589                         }
1590                         else if( !Q_stricmp( argv[ i ], "map_bp" ) )
1591                         {
1592                                 convertFunc = ConvertBSPToMap_BP;
1593                                 map_allowed = qtrue;
1594                         }
1595                         else if( !Q_stricmp( argv[ i ], "map" ) )
1596                         {
1597                                 convertFunc = ConvertBSPToMap;
1598                                 map_allowed = qtrue;
1599                         }
1600                         else
1601                         {
1602                                 convertGame = GetGame( argv[ i ] );
1603                                 map_allowed = qfalse;
1604                                 if( convertGame == NULL )
1605                                         Sys_Printf( "Unknown conversion format \"%s\". Defaulting to ASE.\n", argv[ i ] );
1606                         }
1607                 }
1608                 else if( !strcmp( argv[ i ],  "-ne" ) )
1609                 {
1610                         normalEpsilon = atof( argv[ i + 1 ] );
1611                         i++;
1612                         Sys_Printf( "Normal epsilon set to %f\n", normalEpsilon );
1613                 }
1614                 else if( !strcmp( argv[ i ],  "-de" ) )
1615                 {
1616                         distanceEpsilon = atof( argv[ i + 1 ] );
1617                         i++;
1618                         Sys_Printf( "Distance epsilon set to %f\n", distanceEpsilon );
1619                 }
1620                 else if( !strcmp( argv[ i ],  "-shadersasbitmap" ) )
1621                         shadersAsBitmap = qtrue;
1622                 else if( !strcmp( argv[ i ],  "-lightmapsastexcoord" ) )
1623                         lightmapsAsTexcoord = qtrue;
1624                 else if( !strcmp( argv[ i ],  "-deluxemapsastexcoord" ) )
1625                 {
1626                         lightmapsAsTexcoord = qtrue;
1627                         deluxemap = qtrue;
1628                 }
1629                 else if( !strcmp( argv[ i ],  "-readbsp" ) )
1630                         force_bsp = qtrue;
1631                 else if( !strcmp( argv[ i ],  "-readmap" ) )
1632                         force_map = qtrue;
1633                 else if( !strcmp( argv[ i ],  "-meta" ) )
1634                         meta = qtrue;
1635                 else if( !strcmp( argv[ i ],  "-patchmeta" ) )
1636                 {
1637                         meta = qtrue;
1638                         patchMeta = qtrue;
1639                 }
1640         }
1641
1642         LoadShaderInfo();
1643         
1644         /* clean up map name */
1645         strcpy(source, ExpandArg(argv[i]));
1646         ExtractFileExtension(source, ext);
1647
1648         if(!map_allowed && !force_map)
1649                 force_bsp = qtrue;
1650
1651         if(force_map || (!force_bsp && !Q_stricmp(ext, "map") && map_allowed))
1652         {
1653                 if(!map_allowed)
1654                         Sys_Printf("WARNING: the requested conversion should not be done from .map files. Compile a .bsp first.\n");
1655                 StripExtension(source);
1656                 DefaultExtension(source, ".map");
1657                 Sys_Printf("Loading %s\n", source);
1658                 LoadMapFile(source, qfalse, convertGame == NULL);
1659                 PseudoCompileBSP(convertGame != NULL);
1660         }
1661         else
1662         {
1663                 StripExtension(source);
1664                 DefaultExtension(source, ".bsp");
1665                 Sys_Printf("Loading %s\n", source);
1666                 LoadBSPFile(source);
1667                 ParseEntities();
1668         }
1669         
1670         /* bsp format convert? */
1671         if( convertGame != NULL )
1672         {
1673                 /* set global game */
1674                 game = convertGame;
1675                 
1676                 /* write bsp */
1677                 StripExtension( source );
1678                 DefaultExtension( source, "_c.bsp" );
1679                 Sys_Printf( "Writing %s\n", source );
1680                 WriteBSPFile( source );
1681                 
1682                 /* return to sender */
1683                 return 0;
1684         }
1685         
1686         /* normal convert */
1687         return convertFunc( source );
1688 }
1689
1690
1691
1692 /*
1693 main()
1694 q3map mojo...
1695 */
1696
1697 int main( int argc, char **argv )
1698 {
1699         int             i, r;
1700         double  start, end;
1701         
1702         
1703         /* we want consistent 'randomness' */
1704         srand( 0 );
1705         
1706         /* start timer */
1707         start = I_FloatTime();
1708
1709         /* this was changed to emit version number over the network */
1710         printf( Q3MAP_VERSION "\n" );
1711         
1712         /* set exit call */
1713         atexit( ExitQ3Map );
1714
1715         /* read general options first */
1716         for( i = 1; i < argc; i++ )
1717         {
1718                 /* -connect */
1719                 if( !strcmp( argv[ i ], "-connect" ) )
1720                 {
1721                         argv[ i ] = NULL;
1722                         i++;
1723                         Broadcast_Setup( argv[ i ] );
1724                         argv[ i ] = NULL;
1725                 }
1726                 
1727                 /* verbose */
1728                 else if( !strcmp( argv[ i ], "-v" ) )
1729                 {
1730                         if(!verbose)
1731                         {
1732                                 verbose = qtrue;
1733                                 argv[ i ] = NULL;
1734                         }
1735                 }
1736                 
1737                 /* force */
1738                 else if( !strcmp( argv[ i ], "-force" ) )
1739                 {
1740                         force = qtrue;
1741                         argv[ i ] = NULL;
1742                 }
1743                 
1744                 /* patch subdivisions */
1745                 else if( !strcmp( argv[ i ], "-subdivisions" ) )
1746                 {
1747                         argv[ i ] = NULL;
1748                         i++;
1749                         patchSubdivisions = atoi( argv[ i ] );
1750                         argv[ i ] = NULL;
1751                         if( patchSubdivisions <= 0 )
1752                                 patchSubdivisions = 1;
1753                 }
1754                 
1755                 /* threads */
1756                 else if( !strcmp( argv[ i ], "-threads" ) )
1757                 {
1758                         argv[ i ] = NULL;
1759                         i++;
1760                         numthreads = atoi( argv[ i ] );
1761                         argv[ i ] = NULL;
1762                 }
1763         }
1764
1765         /* init model library */
1766         PicoInit();
1767         PicoSetMallocFunc( safe_malloc );
1768         PicoSetFreeFunc( free );
1769         PicoSetPrintFunc( PicoPrintFunc );
1770         PicoSetLoadFileFunc( PicoLoadFileFunc );
1771         PicoSetFreeFileFunc( free );
1772         
1773         /* set number of threads */
1774         ThreadSetDefault();
1775         
1776         /* generate sinusoid jitter table */
1777         for( i = 0; i < MAX_JITTERS; i++ )
1778         {
1779                 jitters[ i ] = sin( i * 139.54152147 );
1780                 //%     Sys_Printf( "Jitter %4d: %f\n", i, jitters[ i ] );
1781         }
1782         
1783         /* we print out two versions, q3map's main version (since it evolves a bit out of GtkRadiant)
1784            and we put the GtkRadiant version to make it easy to track with what version of Radiant it was built with */
1785         
1786         Sys_Printf( "Q3Map         - v1.0r (c) 1999 Id Software Inc.\n" );
1787         Sys_Printf( "Q3Map (ydnar) - v" Q3MAP_VERSION "\n" );
1788         Sys_Printf( "NetRadiant    - v" RADIANT_VERSION " " __DATE__ " " __TIME__ "\n" );
1789         Sys_Printf( "%s\n", Q3MAP_MOTD );
1790         
1791         /* ydnar: new path initialization */
1792         InitPaths( &argc, argv );
1793
1794         /* set game options */
1795         if (!patchSubdivisions)
1796                 patchSubdivisions = game->patchSubdivisions;
1797         
1798         /* check if we have enough options left to attempt something */
1799         if( argc < 2 )
1800                 Error( "Usage: %s [general options] [options] mapfile", argv[ 0 ] );
1801         
1802         /* fixaas */
1803         if( !strcmp( argv[ 1 ], "-fixaas" ) )
1804                 r = FixAAS( argc - 1, argv + 1 );
1805         
1806         /* analyze */
1807         else if( !strcmp( argv[ 1 ], "-analyze" ) )
1808                 r = AnalyzeBSP( argc - 1, argv + 1 );
1809         
1810         /* info */
1811         else if( !strcmp( argv[ 1 ], "-info" ) )
1812                 r = BSPInfo( argc - 2, argv + 2 );
1813         
1814         /* vis */
1815         else if( !strcmp( argv[ 1 ], "-vis" ) )
1816                 r = VisMain( argc - 1, argv + 1 );
1817         
1818         /* light */
1819         else if( !strcmp( argv[ 1 ], "-light" ) )
1820                 r = LightMain( argc - 1, argv + 1 );
1821         
1822         /* vlight */
1823         else if( !strcmp( argv[ 1 ], "-vlight" ) )
1824         {
1825                 Sys_Printf( "WARNING: VLight is no longer supported, defaulting to -light -fast instead\n\n" );
1826                 argv[ 1 ] = "-fast";    /* eek a hack */
1827                 r = LightMain( argc, argv );
1828         }
1829         
1830         /* ydnar: lightmap export */
1831         else if( !strcmp( argv[ 1 ], "-export" ) )
1832                 r = ExportLightmapsMain( argc - 1, argv + 1 );
1833         
1834         /* ydnar: lightmap import */
1835         else if( !strcmp( argv[ 1 ], "-import" ) )
1836                 r = ImportLightmapsMain( argc - 1, argv + 1 );
1837         
1838         /* ydnar: bsp scaling */
1839         else if( !strcmp( argv[ 1 ], "-scale" ) )
1840                 r = ScaleBSPMain( argc - 1, argv + 1 );
1841         
1842         /* ydnar: bsp conversion */
1843         else if( !strcmp( argv[ 1 ], "-convert" ) )
1844                 r = ConvertBSPMain( argc - 1, argv + 1 );
1845         
1846         /* div0: minimap */
1847         else if( !strcmp( argv[ 1 ], "-minimap" ) )
1848                 r = MiniMapBSPMain(argc - 1, argv + 1);
1849
1850         /* ydnar: otherwise create a bsp */
1851         else
1852                 r = BSPMain( argc, argv );
1853         
1854         /* emit time */
1855         end = I_FloatTime();
1856         Sys_Printf( "%9.0f seconds elapsed\n", end - start );
1857         
1858         /* shut down connection */
1859         Broadcast_Shutdown();
1860         
1861         /* return any error code */
1862         return r;
1863 }