-autolevel 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         int                             i, b, compileFlags;
365         bspBrush_t              *brush;
366         bspShader_t             *shader;
367         shaderInfo_t    *si;
368         
369         
370         /* note it */
371         Sys_FPrintf( SYS_VRB, "--- MiniMapSetupBrushes ---\n" );
372         
373         /* allocate */
374         if( opaqueBrushes == NULL )
375                 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
376         
377         /* clear */
378         memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
379         numOpaqueBrushes = 0;
380         
381         /* walk the list of worldspawn brushes */
382         for( i = 0; i < minimap.model->numBSPBrushes; i++ )
383         {
384                 /* get brush */
385                 b = minimap.model->firstBSPBrush + i;
386                 brush = &bspBrushes[ b ];
387                 
388 #if 0
389                 /* check all sides */
390                 compileFlags = 0;
391                 for( j = 0; j < brush->numSides; j++ )
392                 {
393                         /* do bsp shader calculations */
394                         side = &bspBrushSides[ brush->firstSide + j ];
395                         shader = &bspShaders[ side->shaderNum ];
396                         
397                         /* get shader info */
398                         si = ShaderInfoForShader( shader->shader );
399                         if( si == NULL )
400                                 continue;
401                         
402                         /* or together compile flags */
403                         compileFlags |= si->compileFlags;
404                 }
405 #else
406                 shader = &bspShaders[ brush->shaderNum ];
407                 si = ShaderInfoForShader( shader->shader );
408                 if( si == NULL )
409                         compileFlags = 0;
410                 else
411                         compileFlags = si->compileFlags;
412 #endif
413                 
414                 /* determine if this brush is solid */
415                 if( (compileFlags & (C_SOLID | C_SKY)) == C_SOLID )
416                 {
417                         opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));
418                         numOpaqueBrushes++;
419                         maxOpaqueBrush = i;
420                 }
421         }
422         
423         /* emit some statistics */
424         Sys_FPrintf( SYS_VRB, "%9d solid brushes\n", numOpaqueBrushes );
425 }
426
427 qboolean MiniMapEvaluateSampleOffsets(int *bestj, int *bestk, float *bestval)
428 {
429         float val, dx, dy;
430         int j, k;
431
432         *bestj = *bestk = -1;
433         *bestval = 3; /* max possible val is 2 */
434
435         for(j = 0; j < minimap.samples; ++j)
436                 for(k = j + 1; k < minimap.samples; ++k)
437                 {
438                         dx = minimap.sample_offsets[2*j+0] - minimap.sample_offsets[2*k+0];
439                         dy = minimap.sample_offsets[2*j+1] - minimap.sample_offsets[2*k+1];
440                         if(dx > +0.5) dx -= 1;
441                         if(dx < -0.5) dx += 1;
442                         if(dy > +0.5) dy -= 1;
443                         if(dy < -0.5) dy += 1;
444                         val = dx * dx + dy * dy;
445                         if(val < *bestval)
446                         {
447                                 *bestj = j;
448                                 *bestk = k;
449                                 *bestval = val;
450                         }
451                 }
452         
453         return *bestval < 3;
454 }
455
456 void MiniMapMakeSampleOffsets()
457 {
458         int i, j, k, jj, kk;
459         float val, valj, valk, sx, sy, rx, ry;
460
461         Sys_Printf( "Generating good sample offsets (this may take a while)...\n" );
462
463         /* start with entirely random samples */
464         for(i = 0; i < minimap.samples; ++i)
465         {
466                 minimap.sample_offsets[2*i+0] = Random();
467                 minimap.sample_offsets[2*i+1] = Random();
468         }
469
470         for(i = 0; i < 1000; ++i)
471         {
472                 if(MiniMapEvaluateSampleOffsets(&j, &k, &val))
473                 {
474                         sx = minimap.sample_offsets[2*j+0];
475                         sy = minimap.sample_offsets[2*j+1];
476                         minimap.sample_offsets[2*j+0] = rx = Random();
477                         minimap.sample_offsets[2*j+1] = ry = Random();
478                         if(!MiniMapEvaluateSampleOffsets(&jj, &kk, &valj))
479                                 valj = -1;
480                         minimap.sample_offsets[2*j+0] = sx;
481                         minimap.sample_offsets[2*j+1] = sy;
482
483                         sx = minimap.sample_offsets[2*k+0];
484                         sy = minimap.sample_offsets[2*k+1];
485                         minimap.sample_offsets[2*k+0] = rx;
486                         minimap.sample_offsets[2*k+1] = ry;
487                         if(!MiniMapEvaluateSampleOffsets(&jj, &kk, &valk))
488                                 valk = -1;
489                         minimap.sample_offsets[2*k+0] = sx;
490                         minimap.sample_offsets[2*k+1] = sy;
491
492                         if(valj > valk)
493                         {
494                                 if(valj > val)
495                                 {
496                                         /* valj is the greatest */
497                                         minimap.sample_offsets[2*j+0] = rx;
498                                         minimap.sample_offsets[2*j+1] = ry;
499                                         i = -1;
500                                 }
501                                 else
502                                 {
503                                         /* valj is the greater and it is useless - forget it */
504                                 }
505                         }
506                         else
507                         {
508                                 if(valk > val)
509                                 {
510                                         /* valk is the greatest */
511                                         minimap.sample_offsets[2*k+0] = rx;
512                                         minimap.sample_offsets[2*k+1] = ry;
513                                         i = -1;
514                                 }
515                                 else
516                                 {
517                                         /* valk is the greater and it is useless - forget it */
518                                 }
519                         }
520                 }
521                 else
522                         break;
523         }
524 }
525
526 void MergeRelativePath(char *out, const char *absolute, const char *relative)
527 {
528         const char *endpos = absolute + strlen(absolute);
529         while(endpos != absolute && (endpos[-1] == '/' || endpos[-1] == '\\'))
530                 --endpos;
531         while(relative[0] == '.' && relative[1] == '.' && (relative[2] == '/' || relative[2] == '\\'))
532         {
533                 relative += 3;
534                 while(endpos != absolute)
535                 {
536                         --endpos;
537                         if(*endpos == '/' || *endpos == '\\')
538                                 break;
539                 }
540                 while(endpos != absolute && (endpos[-1] == '/' || endpos[-1] == '\\'))
541                         --endpos;
542         }
543         memcpy(out, absolute, endpos - absolute);
544         out[endpos - absolute] = '/';
545         strcpy(out + (endpos - absolute + 1), relative);
546 }
547
548 int MiniMapBSPMain( int argc, char **argv )
549 {
550         char minimapFilename[1024];
551         char basename[1024];
552         char path[1024];
553         char relativeMinimapFilename[1024];
554         qboolean autolevel;
555         float minimapSharpen;
556         float border;
557         byte *data4b, *p;
558         float *q;
559         int x, y;
560         int i;
561         miniMapMode_t mode;
562         vec3_t mins, maxs;
563         qboolean keepaspect;
564
565         /* arg checking */
566         if( argc < 2 )
567         {
568                 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" );
569                 return 0;
570         }
571
572         /* load the BSP first */
573         strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
574         StripExtension( source );
575         DefaultExtension( source, ".bsp" );
576         Sys_Printf( "Loading %s\n", source );
577         BeginMapShaderFile( source );
578         LoadShaderInfo();
579         LoadBSPFile( source );
580
581         minimap.model = &bspModels[0];
582         VectorCopy(minimap.model->mins, mins);
583         VectorCopy(minimap.model->maxs, maxs);
584
585         *minimapFilename = 0;
586         minimapSharpen = game->miniMapSharpen;
587         minimap.width = minimap.height = game->miniMapSize;
588         border = game->miniMapBorder;
589         keepaspect = game->miniMapKeepAspect;
590         mode = game->miniMapMode;
591
592         autolevel = qfalse;
593         minimap.samples = 1;
594         minimap.sample_offsets = NULL;
595         minimap.boost = 1.0;
596         minimap.brightness = 0.0;
597         minimap.contrast = 1.0;
598
599         /* process arguments */
600         for( i = 1; i < (argc - 1); i++ )
601         {
602                 if( !strcmp( argv[ i ],  "-size" ) )
603                 {
604                         minimap.width = minimap.height = atoi(argv[i + 1]);
605                         i++;
606                         Sys_Printf( "Image size set to %i\n", minimap.width );
607                 }
608                 else if( !strcmp( argv[ i ],  "-sharpen" ) )
609                 {
610                         minimapSharpen = atof(argv[i + 1]);
611                         i++;
612                         Sys_Printf( "Sharpening coefficient set to %f\n", minimapSharpen );
613                 }
614                 else if( !strcmp( argv[ i ],  "-samples" ) )
615                 {
616                         minimap.samples = atoi(argv[i + 1]);
617                         i++;
618                         Sys_Printf( "Samples set to %i\n", minimap.samples );
619                         if(minimap.sample_offsets)
620                                 free(minimap.sample_offsets);
621                         minimap.sample_offsets = malloc(2 * sizeof(*minimap.sample_offsets) * minimap.samples);
622                         MiniMapMakeSampleOffsets();
623                 }
624                 else if( !strcmp( argv[ i ],  "-random" ) )
625                 {
626                         minimap.samples = atoi(argv[i + 1]);
627                         i++;
628                         Sys_Printf( "Random samples set to %i\n", minimap.samples );
629                         if(minimap.sample_offsets)
630                                 free(minimap.sample_offsets);
631                         minimap.sample_offsets = NULL;
632                 }
633                 else if( !strcmp( argv[ i ],  "-border" ) )
634                 {
635                         border = atof(argv[i + 1]);
636                         i++;
637                         Sys_Printf( "Border set to %f\n", border );
638                 }
639                 else if( !strcmp( argv[ i ],  "-keepaspect" ) )
640                 {
641                         keepaspect = qtrue;
642                         Sys_Printf( "Keeping aspect ratio by letterboxing\n", border );
643                 }
644                 else if( !strcmp( argv[ i ],  "-nokeepaspect" ) )
645                 {
646                         keepaspect = qfalse;
647                         Sys_Printf( "Not keeping aspect ratio\n", border );
648                 }
649                 else if( !strcmp( argv[ i ],  "-o" ) )
650                 {
651                         strcpy(minimapFilename, argv[i + 1]);
652                         i++;
653                         Sys_Printf( "Output file name set to %s\n", minimapFilename );
654                 }
655                 else if( !strcmp( argv[ i ],  "-minmax" ) && i < (argc - 7) )
656                 {
657                         mins[0] = atof(argv[i + 1]);
658                         mins[1] = atof(argv[i + 2]);
659                         mins[2] = atof(argv[i + 3]);
660                         maxs[0] = atof(argv[i + 4]);
661                         maxs[1] = atof(argv[i + 5]);
662                         maxs[2] = atof(argv[i + 6]);
663                         i += 6;
664                         Sys_Printf( "Map mins/maxs overridden\n" );
665                 }
666                 else if( !strcmp( argv[ i ],  "-gray" ) )
667                 {
668                         mode = MINIMAP_MODE_GRAY;
669                         Sys_Printf( "Writing as white-on-black image\n" );
670                 }
671                 else if( !strcmp( argv[ i ],  "-black" ) )
672                 {
673                         mode = MINIMAP_MODE_BLACK;
674                         Sys_Printf( "Writing as black alpha image\n" );
675                 }
676                 else if( !strcmp( argv[ i ],  "-white" ) )
677                 {
678                         mode = MINIMAP_MODE_WHITE;
679                         Sys_Printf( "Writing as white alpha image\n" );
680                 }
681                 else if( !strcmp( argv[ i ],  "-boost" ) && i < (argc - 2) )
682                 {
683                         minimap.boost = atof(argv[i + 1]);
684                         i++;
685                         Sys_Printf( "Contrast boost set to %f\n", minimap.boost );
686                 }
687                 else if( !strcmp( argv[ i ],  "-brightness" ) && i < (argc - 2) )
688                 {
689                         minimap.brightness = atof(argv[i + 1]);
690                         i++;
691                         Sys_Printf( "Brightness set to %f\n", minimap.brightness );
692                 }
693                 else if( !strcmp( argv[ i ],  "-contrast" ) && i < (argc - 2) )
694                 {
695                         minimap.contrast = atof(argv[i + 1]);
696                         i++;
697                         Sys_Printf( "Contrast set to %f\n", minimap.contrast );
698                 }
699                 else if( !strcmp( argv[ i ],  "-autolevel" ) )
700                 {
701                         autolevel = qtrue;
702                         Sys_Printf( "Auto level enabled\n", border );
703                 }
704                 else if( !strcmp( argv[ i ],  "-noautolevel" ) )
705                 {
706                         autolevel = qfalse;
707                         Sys_Printf( "Auto level disabled\n", border );
708                 }
709         }
710
711         MiniMapMakeMinsMaxs(mins, maxs, border, keepaspect);
712
713         if(!*minimapFilename)
714         {
715                 ExtractFileBase(source, basename);
716                 ExtractFilePath(source, path);
717                 sprintf(relativeMinimapFilename, game->miniMapNameFormat, basename);
718                 MergeRelativePath(minimapFilename, path, relativeMinimapFilename);
719                 Sys_Printf("Output file name automatically set to %s\n", minimapFilename);
720         }
721         ExtractFilePath(minimapFilename, path);
722         Q_mkdir(path);
723
724         if(minimapSharpen >= 0)
725         {
726                 minimap.sharpen_centermult = 8 * minimapSharpen + 1;
727                 minimap.sharpen_boxmult    =    -minimapSharpen;
728         }
729
730         minimap.data1f = safe_malloc(minimap.width * minimap.height * sizeof(*minimap.data1f));
731         data4b = safe_malloc(minimap.width * minimap.height * 4);
732         if(minimapSharpen >= 0)
733                 minimap.sharpendata1f = safe_malloc(minimap.width * minimap.height * sizeof(*minimap.data1f));
734
735         MiniMapSetupBrushes();
736
737         if(minimap.samples <= 1)
738         {
739                 Sys_Printf( "\n--- MiniMapNoSupersampling (%d) ---\n", minimap.height );
740                 RunThreadsOnIndividual(minimap.height, qtrue, MiniMapNoSupersampling);
741         }
742         else
743         {
744                 if(minimap.sample_offsets)
745                 {
746                         Sys_Printf( "\n--- MiniMapSupersampled (%d) ---\n", minimap.height );
747                         RunThreadsOnIndividual(minimap.height, qtrue, MiniMapSupersampled);
748                 }
749                 else
750                 {
751                         Sys_Printf( "\n--- MiniMapRandomlySupersampled (%d) ---\n", minimap.height );
752                         RunThreadsOnIndividual(minimap.height, qtrue, MiniMapRandomlySupersampled);
753                 }
754         }
755
756         if(minimap.boost != 1.0)
757         {
758                 Sys_Printf( "\n--- MiniMapContrastBoost (%d) ---\n", minimap.height );
759                 RunThreadsOnIndividual(minimap.height, qtrue, MiniMapContrastBoost);
760         }
761
762         if(autolevel)
763         {
764                 Sys_Printf( "\n--- MiniMapAutoLevel (%d) ---\n", minimap.height );
765                 float mi = 1, ma = 0;
766                 float s, o;
767
768                 // TODO threads!
769                 for(y = 0; y < minimap.height; ++y)
770                         for(x = 0; x < minimap.width; ++x)
771                         {
772                                 float v = *q++;
773                                 if(v < mi)
774                                         mi = v;
775                                 if(v > ma)
776                                         ma = v;
777                         }
778                 s = 1 / (ma - mi);
779                 o = mi / (ma - mi);
780
781                 // equations:
782                 //   brightness + contrast * v
783                 // after autolevel:
784                 //   brightness + contrast * (v * s - o)
785                 // =
786                 //   (brightness - contrast * o) + (contrast * s) * v
787                 minimap.brightness = minimap.brightness - minimap.contrast * o;
788                 minimap.contrast *= s;
789
790                 Sys_Printf( "Auto level: Brightness changed to %f\n", minimap.boost );
791                 Sys_Printf( "Auto level: Contrast changed to %f\n", minimap.contrast );
792         }
793
794         if(minimap.brightness != 0 || minimap.contrast != 1)
795         {
796                 Sys_Printf( "\n--- MiniMapBrightnessContrast (%d) ---\n", minimap.height );
797                 RunThreadsOnIndividual(minimap.height, qtrue, MiniMapBrightnessContrast);
798         }
799
800         if(minimap.sharpendata1f)
801         {
802                 Sys_Printf( "\n--- MiniMapSharpen (%d) ---\n", minimap.height );
803                 RunThreadsOnIndividual(minimap.height, qtrue, MiniMapSharpen);
804                 q = minimap.sharpendata1f;
805         }
806         else
807         {
808                 q = minimap.data1f;
809         }
810
811         Sys_Printf( "\nConverting...");
812
813         switch(mode)
814         {
815                 case MINIMAP_MODE_GRAY:
816                         p = data4b;
817                         for(y = 0; y < minimap.height; ++y)
818                                 for(x = 0; x < minimap.width; ++x)
819                                 {
820                                         byte b;
821                                         float v = *q++;
822                                         if(v < 0) v = 0;
823                                         if(v > 255.0/256.0) v = 255.0/256.0;
824                                         b = v * 256;
825                                         *p++ = b;
826                                 }
827                         Sys_Printf( " writing to %s...", minimapFilename );
828                         WriteTGAGray(minimapFilename, data4b, minimap.width, minimap.height);
829                         break;
830                 case MINIMAP_MODE_BLACK:
831                         p = data4b;
832                         for(y = 0; y < minimap.height; ++y)
833                                 for(x = 0; x < minimap.width; ++x)
834                                 {
835                                         byte b;
836                                         float v = *q++;
837                                         if(v < 0) v = 0;
838                                         if(v > 255.0/256.0) v = 255.0/256.0;
839                                         b = v * 256;
840                                         *p++ = 0;
841                                         *p++ = 0;
842                                         *p++ = 0;
843                                         *p++ = b;
844                                 }
845                         Sys_Printf( " writing to %s...", minimapFilename );
846                         WriteTGA(minimapFilename, data4b, minimap.width, minimap.height);
847                         break;
848                 case MINIMAP_MODE_WHITE:
849                         p = data4b;
850                         for(y = 0; y < minimap.height; ++y)
851                                 for(x = 0; x < minimap.width; ++x)
852                                 {
853                                         byte b;
854                                         float v = *q++;
855                                         if(v < 0) v = 0;
856                                         if(v > 255.0/256.0) v = 255.0/256.0;
857                                         b = v * 256;
858                                         *p++ = 255;
859                                         *p++ = 255;
860                                         *p++ = 255;
861                                         *p++ = b;
862                                 }
863                         Sys_Printf( " writing to %s...", minimapFilename );
864                         WriteTGA(minimapFilename, data4b, minimap.width, minimap.height);
865                         break;
866         }
867
868         Sys_Printf( " done.\n" );
869
870         /* return to sender */
871         return 0;
872 }
873
874
875
876
877
878 /*
879 MD4BlockChecksum()
880 calculates an md4 checksum for a block of data
881 */
882
883 static int MD4BlockChecksum( void *buffer, int length )
884 {
885         return Com_BlockChecksum(buffer, length);
886 }
887
888 /*
889 FixAAS()
890 resets an aas checksum to match the given BSP
891 */
892
893 int FixAAS( int argc, char **argv )
894 {
895         int                     length, checksum;
896         void            *buffer;
897         FILE            *file;
898         char            aas[ 1024 ], **ext;
899         char            *exts[] =
900                                 {
901                                         ".aas",
902                                         "_b0.aas",
903                                         "_b1.aas",
904                                         NULL
905                                 };
906         
907         
908         /* arg checking */
909         if( argc < 2 )
910         {
911                 Sys_Printf( "Usage: q3map -fixaas [-v] <mapname>\n" );
912                 return 0;
913         }
914         
915         /* do some path mangling */
916         strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
917         StripExtension( source );
918         DefaultExtension( source, ".bsp" );
919         
920         /* note it */
921         Sys_Printf( "--- FixAAS ---\n" );
922         
923         /* load the bsp */
924         Sys_Printf( "Loading %s\n", source );
925         length = LoadFile( source, &buffer );
926         
927         /* create bsp checksum */
928         Sys_Printf( "Creating checksum...\n" );
929         checksum = LittleLong( MD4BlockChecksum( buffer, length ) );
930         
931         /* write checksum to aas */
932         ext = exts;
933         while( *ext )
934         {
935                 /* mangle name */
936                 strcpy( aas, source );
937                 StripExtension( aas );
938                 strcat( aas, *ext );
939                 Sys_Printf( "Trying %s\n", aas );
940                 ext++;
941                 
942                 /* fix it */
943                 file = fopen( aas, "r+b" );
944                 if( !file )
945                         continue;
946                 if( fwrite( &checksum, 4, 1, file ) != 1 )
947                         Error( "Error writing checksum to %s", aas );
948                 fclose( file );
949         }
950         
951         /* return to sender */
952         return 0;
953 }
954
955
956
957 /*
958 AnalyzeBSP() - ydnar
959 analyzes a Quake engine BSP file
960 */
961
962 typedef struct abspHeader_s
963 {
964         char                    ident[ 4 ];
965         int                             version;
966         
967         bspLump_t               lumps[ 1 ];     /* unknown size */
968 }
969 abspHeader_t;
970
971 typedef struct abspLumpTest_s
972 {
973         int                             radix, minCount;
974         char                    *name;
975 }
976 abspLumpTest_t;
977
978 int AnalyzeBSP( int argc, char **argv )
979 {
980         abspHeader_t                    *header;
981         int                                             size, i, version, offset, length, lumpInt, count;
982         char                                    ident[ 5 ];
983         void                                    *lump;
984         float                                   lumpFloat;
985         char                                    lumpString[ 1024 ], source[ 1024 ];
986         qboolean                                lumpSwap = qfalse;
987         abspLumpTest_t                  *lumpTest;
988         static abspLumpTest_t   lumpTests[] =
989                                                         {
990                                                                 { sizeof( bspPlane_t ),                 6,              "IBSP LUMP_PLANES" },
991                                                                 { sizeof( bspBrush_t ),                 1,              "IBSP LUMP_BRUSHES" },
992                                                                 { 8,                                                    6,              "IBSP LUMP_BRUSHSIDES" },
993                                                                 { sizeof( bspBrushSide_t ),             6,              "RBSP LUMP_BRUSHSIDES" },
994                                                                 { sizeof( bspModel_t ),                 1,              "IBSP LUMP_MODELS" },
995                                                                 { sizeof( bspNode_t ),                  2,              "IBSP LUMP_NODES" },
996                                                                 { sizeof( bspLeaf_t ),                  1,              "IBSP LUMP_LEAFS" },
997                                                                 { 104,                                                  3,              "IBSP LUMP_DRAWSURFS" },
998                                                                 { 44,                                                   3,              "IBSP LUMP_DRAWVERTS" },
999                                                                 { 4,                                                    6,              "IBSP LUMP_DRAWINDEXES" },
1000                                                                 { 128 * 128 * 3,                                1,              "IBSP LUMP_LIGHTMAPS" },
1001                                                                 { 256 * 256 * 3,                                1,              "IBSP LUMP_LIGHTMAPS (256 x 256)" },
1002                                                                 { 512 * 512 * 3,                                1,              "IBSP LUMP_LIGHTMAPS (512 x 512)" },
1003                                                                 { 0, 0, NULL }
1004                                                         };
1005         
1006         
1007         /* arg checking */
1008         if( argc < 1 )
1009         {
1010                 Sys_Printf( "Usage: q3map -analyze [-lumpswap] [-v] <mapname>\n" );
1011                 return 0;
1012         }
1013         
1014         /* process arguments */
1015         for( i = 1; i < (argc - 1); i++ )
1016         {
1017                 /* -format map|ase|... */
1018                 if( !strcmp( argv[ i ],  "-lumpswap" ) )
1019                 {
1020                         Sys_Printf( "Swapped lump structs enabled\n" );
1021                         lumpSwap = qtrue;
1022                 }
1023         }
1024         
1025         /* clean up map name */
1026         strcpy( source, ExpandArg( argv[ i ] ) );
1027         Sys_Printf( "Loading %s\n", source );
1028         
1029         /* load the file */
1030         size = LoadFile( source, (void**) &header );
1031         if( size == 0 || header == NULL )
1032         {
1033                 Sys_Printf( "Unable to load %s.\n", source );
1034                 return -1;
1035         }
1036         
1037         /* analyze ident/version */
1038         memcpy( ident, header->ident, 4 );
1039         ident[ 4 ] = '\0';
1040         version = LittleLong( header->version );
1041         
1042         Sys_Printf( "Identity:      %s\n", ident );
1043         Sys_Printf( "Version:       %d\n", version );
1044         Sys_Printf( "---------------------------------------\n" );
1045         
1046         /* analyze each lump */
1047         for( i = 0; i < 100; i++ )
1048         {
1049                 /* call of duty swapped lump pairs */
1050                 if( lumpSwap )
1051                 {
1052                         offset = LittleLong( header->lumps[ i ].length );
1053                         length = LittleLong( header->lumps[ i ].offset );
1054                 }
1055                 
1056                 /* standard lump pairs */
1057                 else
1058                 {
1059                         offset = LittleLong( header->lumps[ i ].offset );
1060                         length = LittleLong( header->lumps[ i ].length );
1061                 }
1062                 
1063                 /* extract data */
1064                 lump = (byte*) header + offset;
1065                 lumpInt = LittleLong( (int) *((int*) lump) );
1066                 lumpFloat = LittleFloat( (float) *((float*) lump) );
1067                 memcpy( lumpString, (char*) lump, ((size_t)length < sizeof(lumpString) ? (size_t)length : sizeof(lumpString)-1) );
1068                 lumpString[ sizeof(lumpString)-1 ] = '\0';
1069                 
1070                 /* print basic lump info */
1071                 Sys_Printf( "Lump:          %d\n", i );
1072                 Sys_Printf( "Offset:        %d bytes\n", offset );
1073                 Sys_Printf( "Length:        %d bytes\n", length );
1074                 
1075                 /* only operate on valid lumps */
1076                 if( length > 0 )
1077                 {
1078                         /* print data in 4 formats */
1079                         Sys_Printf( "As hex:        %08X\n", lumpInt );
1080                         Sys_Printf( "As int:        %d\n", lumpInt );
1081                         Sys_Printf( "As float:      %f\n", lumpFloat );
1082                         Sys_Printf( "As string:     %s\n", lumpString );
1083                         
1084                         /* guess lump type */
1085                         if( lumpString[ 0 ] == '{' && lumpString[ 2 ] == '"' )
1086                                 Sys_Printf( "Type guess:    IBSP LUMP_ENTITIES\n" );
1087                         else if( strstr( lumpString, "textures/" ) )
1088                                 Sys_Printf( "Type guess:    IBSP LUMP_SHADERS\n" );
1089                         else
1090                         {
1091                                 /* guess based on size/count */
1092                                 for( lumpTest = lumpTests; lumpTest->radix > 0; lumpTest++ )
1093                                 {
1094                                         if( (length % lumpTest->radix) != 0 )
1095                                                 continue;
1096                                         count = length / lumpTest->radix;
1097                                         if( count < lumpTest->minCount )
1098                                                 continue;
1099                                         Sys_Printf( "Type guess:    %s (%d x %d)\n", lumpTest->name, count, lumpTest->radix );
1100                                 }
1101                         }
1102                 }
1103                 
1104                 Sys_Printf( "---------------------------------------\n" );
1105                 
1106                 /* end of file */
1107                 if( offset + length >= size )
1108                         break;
1109         }
1110         
1111         /* last stats */
1112         Sys_Printf( "Lump count:    %d\n", i + 1 );
1113         Sys_Printf( "File size:     %d bytes\n", size );
1114         
1115         /* return to caller */
1116         return 0;
1117 }
1118
1119
1120
1121 /*
1122 BSPInfo()
1123 emits statistics about the bsp file
1124 */
1125
1126 int BSPInfo( int count, char **fileNames )
1127 {
1128         int                     i;
1129         char            source[ 1024 ], ext[ 64 ];
1130         int                     size;
1131         FILE            *f;
1132         
1133         
1134         /* dummy check */
1135         if( count < 1 )
1136         {
1137                 Sys_Printf( "No files to dump info for.\n");
1138                 return -1;
1139         }
1140         
1141         /* enable info mode */
1142         infoMode = qtrue;
1143         
1144         /* walk file list */
1145         for( i = 0; i < count; i++ )
1146         {
1147                 Sys_Printf( "---------------------------------\n" );
1148                 
1149                 /* mangle filename and get size */
1150                 strcpy( source, fileNames[ i ] );
1151                 ExtractFileExtension( source, ext );
1152                 if( !Q_stricmp( ext, "map" ) )
1153                         StripExtension( source );
1154                 DefaultExtension( source, ".bsp" );
1155                 f = fopen( source, "rb" );
1156                 if( f )
1157                 {
1158                         size = Q_filelength (f);
1159                         fclose( f );
1160                 }
1161                 else
1162                         size = 0;
1163                 
1164                 /* load the bsp file and print lump sizes */
1165                 Sys_Printf( "%s\n", source );
1166                 LoadBSPFile( source );          
1167                 PrintBSPFileSizes();
1168                 
1169                 /* print sizes */
1170                 Sys_Printf( "\n" );
1171                 Sys_Printf( "          total         %9d\n", size );
1172                 Sys_Printf( "                        %9d KB\n", size / 1024 );
1173                 Sys_Printf( "                        %9d MB\n", size / (1024 * 1024) );
1174                 
1175                 Sys_Printf( "---------------------------------\n" );
1176         }
1177         
1178         /* return count */
1179         return i;
1180 }
1181
1182
1183 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)
1184 {
1185         vec4_t scoeffs, tcoeffs;
1186         float md;
1187         m4x4_t solvematrix;
1188
1189         vec3_t norm;
1190         vec3_t dab, dac;
1191         VectorSubtract(bxyz, axyz, dab);
1192         VectorSubtract(cxyz, axyz, dac);
1193         CrossProduct(dab, dac, norm);
1194         
1195         // assume:
1196         //   s = f(x, y, z)
1197         //   s(v + norm) = s(v) when n ortho xyz
1198         
1199         // s(v) = DotProduct(v, scoeffs) + scoeffs[3]
1200
1201         // solve:
1202         //   scoeffs * (axyz, 1) == ast[0]
1203         //   scoeffs * (bxyz, 1) == bst[0]
1204         //   scoeffs * (cxyz, 1) == cst[0]
1205         //   scoeffs * (norm, 0) == 0
1206         // scoeffs * [axyz, 1 | bxyz, 1 | cxyz, 1 | norm, 0] = [ast[0], bst[0], cst[0], 0]
1207         solvematrix[0] = axyz[0];
1208         solvematrix[4] = axyz[1];
1209         solvematrix[8] = axyz[2];
1210         solvematrix[12] = 1;
1211         solvematrix[1] = bxyz[0];
1212         solvematrix[5] = bxyz[1];
1213         solvematrix[9] = bxyz[2];
1214         solvematrix[13] = 1;
1215         solvematrix[2] = cxyz[0];
1216         solvematrix[6] = cxyz[1];
1217         solvematrix[10] = cxyz[2];
1218         solvematrix[14] = 1;
1219         solvematrix[3] = norm[0];
1220         solvematrix[7] = norm[1];
1221         solvematrix[11] = norm[2];
1222         solvematrix[15] = 0;
1223
1224         md = m4_det(solvematrix);
1225         if(md*md < 1e-10)
1226         {
1227                 Sys_Printf("Cannot invert some matrix, some texcoords aren't extrapolated!");
1228                 return;
1229         }
1230
1231         m4x4_invert(solvematrix);
1232
1233         scoeffs[0] = ast[0];
1234         scoeffs[1] = bst[0];
1235         scoeffs[2] = cst[0];
1236         scoeffs[3] = 0;
1237         m4x4_transform_vec4(solvematrix, scoeffs);
1238         tcoeffs[0] = ast[1];
1239         tcoeffs[1] = bst[1];
1240         tcoeffs[2] = cst[1];
1241         tcoeffs[3] = 0;
1242         m4x4_transform_vec4(solvematrix, tcoeffs);
1243
1244         ast_out[0] = scoeffs[0] * axyz_new[0] + scoeffs[1] * axyz_new[1] + scoeffs[2] * axyz_new[2] + scoeffs[3];
1245         ast_out[1] = tcoeffs[0] * axyz_new[0] + tcoeffs[1] * axyz_new[1] + tcoeffs[2] * axyz_new[2] + tcoeffs[3];
1246         bst_out[0] = scoeffs[0] * bxyz_new[0] + scoeffs[1] * bxyz_new[1] + scoeffs[2] * bxyz_new[2] + scoeffs[3];
1247         bst_out[1] = tcoeffs[0] * bxyz_new[0] + tcoeffs[1] * bxyz_new[1] + tcoeffs[2] * bxyz_new[2] + tcoeffs[3];
1248         cst_out[0] = scoeffs[0] * cxyz_new[0] + scoeffs[1] * cxyz_new[1] + scoeffs[2] * cxyz_new[2] + scoeffs[3];
1249         cst_out[1] = tcoeffs[0] * cxyz_new[0] + tcoeffs[1] * cxyz_new[1] + tcoeffs[2] * cxyz_new[2] + tcoeffs[3];
1250 }
1251
1252 /*
1253 ScaleBSPMain()
1254 amaze and confuse your enemies with wierd scaled maps!
1255 */
1256
1257 int ScaleBSPMain( int argc, char **argv )
1258 {
1259         int                     i, j;
1260         float           f, a;
1261         vec3_t scale;
1262         vec3_t          vec;
1263         char            str[ 1024 ];
1264         int uniform, axis;
1265         qboolean texscale;
1266         float *old_xyzst = NULL;
1267         float spawn_ref = 0;
1268         
1269         
1270         /* arg checking */
1271         if( argc < 3 )
1272         {
1273                 Sys_Printf( "Usage: q3map [-v] -scale [-tex] [-spawn_ref <value>] <value> <mapname>\n" );
1274                 return 0;
1275         }
1276         
1277         texscale = qfalse;
1278         for(i = 1; i < argc-2; ++i)
1279         {
1280                 if(!strcmp(argv[i], "-tex"))
1281                 {
1282                         texscale = qtrue;
1283                 }
1284                 else if(!strcmp(argv[i], "-spawn_ref"))
1285                 {
1286                         spawn_ref = atof(argv[i+1]);
1287                         ++i;
1288                 }
1289                 else
1290                         break;
1291         }
1292         
1293         /* get scale */
1294         // if(argc-2 >= i) // always true
1295                 scale[2] = scale[1] = scale[0] = atof( argv[ argc - 2 ] );
1296         if(argc-3 >= i)
1297                 scale[1] = scale[0] = atof( argv[ argc - 3 ] );
1298         if(argc-4 >= i)
1299                 scale[0] = atof( argv[ argc - 4 ] );
1300
1301         uniform = ((scale[0] == scale[1]) && (scale[1] == scale[2]));
1302
1303         if( scale[0] == 0.0f || scale[1] == 0.0f || scale[2] == 0.0f )
1304         {
1305                 Sys_Printf( "Usage: q3map [-v] -scale [-tex] [-spawn_ref <value>] <value> <mapname>\n" );
1306                 Sys_Printf( "Non-zero scale value required.\n" );
1307                 return 0;
1308         }
1309         
1310         /* do some path mangling */
1311         strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
1312         StripExtension( source );
1313         DefaultExtension( source, ".bsp" );
1314         
1315         /* load the bsp */
1316         Sys_Printf( "Loading %s\n", source );
1317         LoadBSPFile( source );
1318         ParseEntities();
1319         
1320         /* note it */
1321         Sys_Printf( "--- ScaleBSP ---\n" );
1322         Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );
1323         
1324         /* scale entity keys */
1325         for( i = 0; i < numBSPEntities && i < numEntities; i++ )
1326         {
1327                 /* scale origin */
1328                 GetVectorForKey( &entities[ i ], "origin", vec );
1329                 if( (vec[ 0 ] || vec[ 1 ] || vec[ 2 ]) )
1330                 {
1331                         if(!strncmp(ValueForKey(&entities[i], "classname"), "info_player_", 12))
1332                                 vec[2] += spawn_ref;
1333                         vec[0] *= scale[0];
1334                         vec[1] *= scale[1];
1335                         vec[2] *= scale[2];
1336                         if(!strncmp(ValueForKey(&entities[i], "classname"), "info_player_", 12))
1337                                 vec[2] -= spawn_ref;
1338                         sprintf( str, "%f %f %f", vec[ 0 ], vec[ 1 ], vec[ 2 ] );
1339                         SetKeyValue( &entities[ i ], "origin", str );
1340                 }
1341
1342                 a = FloatForKey( &entities[ i ], "angle" );
1343                 if(a == -1 || a == -2) // z scale
1344                         axis = 2;
1345                 else if(fabs(sin(DEG2RAD(a))) < 0.707)
1346                         axis = 0;
1347                 else
1348                         axis = 1;
1349                 
1350                 /* scale door lip */
1351                 f = FloatForKey( &entities[ i ], "lip" );
1352                 if( f )
1353                 {
1354                         f *= scale[axis];
1355                         sprintf( str, "%f", f );
1356                         SetKeyValue( &entities[ i ], "lip", str );
1357                 }
1358                 
1359                 /* scale plat height */
1360                 f = FloatForKey( &entities[ i ], "height" );
1361                 if( f )
1362                 {
1363                         f *= scale[2];
1364                         sprintf( str, "%f", f );
1365                         SetKeyValue( &entities[ i ], "height", str );
1366                 }
1367
1368                 // TODO maybe allow a definition file for entities to specify which values are scaled how?
1369         }
1370         
1371         /* scale models */
1372         for( i = 0; i < numBSPModels; i++ )
1373         {
1374                 bspModels[ i ].mins[0] *= scale[0];
1375                 bspModels[ i ].mins[1] *= scale[1];
1376                 bspModels[ i ].mins[2] *= scale[2];
1377                 bspModels[ i ].maxs[0] *= scale[0];
1378                 bspModels[ i ].maxs[1] *= scale[1];
1379                 bspModels[ i ].maxs[2] *= scale[2];
1380         }
1381         
1382         /* scale nodes */
1383         for( i = 0; i < numBSPNodes; i++ )
1384         {
1385                 bspNodes[ i ].mins[0] *= scale[0];
1386                 bspNodes[ i ].mins[1] *= scale[1];
1387                 bspNodes[ i ].mins[2] *= scale[2];
1388                 bspNodes[ i ].maxs[0] *= scale[0];
1389                 bspNodes[ i ].maxs[1] *= scale[1];
1390                 bspNodes[ i ].maxs[2] *= scale[2];
1391         }
1392         
1393         /* scale leafs */
1394         for( i = 0; i < numBSPLeafs; i++ )
1395         {
1396                 bspLeafs[ i ].mins[0] *= scale[0];
1397                 bspLeafs[ i ].mins[1] *= scale[1];
1398                 bspLeafs[ i ].mins[2] *= scale[2];
1399                 bspLeafs[ i ].maxs[0] *= scale[0];
1400                 bspLeafs[ i ].maxs[1] *= scale[1];
1401                 bspLeafs[ i ].maxs[2] *= scale[2];
1402         }
1403         
1404         if(texscale)
1405         {
1406                 Sys_Printf("Using texture unlocking (and probably breaking texture alignment a lot)\n");
1407                 old_xyzst = safe_malloc(sizeof(*old_xyzst) * numBSPDrawVerts * 5);
1408                 for(i = 0; i < numBSPDrawVerts; i++)
1409                 {
1410                         old_xyzst[5*i+0] = bspDrawVerts[i].xyz[0];
1411                         old_xyzst[5*i+1] = bspDrawVerts[i].xyz[1];
1412                         old_xyzst[5*i+2] = bspDrawVerts[i].xyz[2];
1413                         old_xyzst[5*i+3] = bspDrawVerts[i].st[0];
1414                         old_xyzst[5*i+4] = bspDrawVerts[i].st[1];
1415                 }
1416         }
1417
1418         /* scale drawverts */
1419         for( i = 0; i < numBSPDrawVerts; i++ )
1420         {
1421                 bspDrawVerts[i].xyz[0] *= scale[0];
1422                 bspDrawVerts[i].xyz[1] *= scale[1];
1423                 bspDrawVerts[i].xyz[2] *= scale[2];
1424                 bspDrawVerts[i].normal[0] /= scale[0];
1425                 bspDrawVerts[i].normal[1] /= scale[1];
1426                 bspDrawVerts[i].normal[2] /= scale[2];
1427                 VectorNormalize(bspDrawVerts[i].normal, bspDrawVerts[i].normal);
1428         }
1429
1430         if(texscale)
1431         {
1432                 for(i = 0; i < numBSPDrawSurfaces; i++)
1433                 {
1434                         switch(bspDrawSurfaces[i].surfaceType)
1435                         {
1436                                 case SURFACE_FACE:
1437                                 case SURFACE_META:
1438                                         if(bspDrawSurfaces[i].numIndexes % 3)
1439                                                 Error("Not a triangulation!");
1440                                         for(j = bspDrawSurfaces[i].firstIndex; j < bspDrawSurfaces[i].firstIndex + bspDrawSurfaces[i].numIndexes; j += 3)
1441                                         {
1442                                                 int ia = bspDrawIndexes[j] + bspDrawSurfaces[i].firstVert, ib = bspDrawIndexes[j+1] + bspDrawSurfaces[i].firstVert, ic = bspDrawIndexes[j+2] + bspDrawSurfaces[i].firstVert;
1443                                                 bspDrawVert_t *a = &bspDrawVerts[ia], *b = &bspDrawVerts[ib], *c = &bspDrawVerts[ic];
1444                                                 float *oa = &old_xyzst[ia*5], *ob = &old_xyzst[ib*5], *oc = &old_xyzst[ic*5];
1445                                                 // extrapolate:
1446                                                 //   a->xyz -> oa
1447                                                 //   b->xyz -> ob
1448                                                 //   c->xyz -> oc
1449                                                 ExtrapolateTexcoords(
1450                                                         &oa[0], &oa[3],
1451                                                         &ob[0], &ob[3],
1452                                                         &oc[0], &oc[3],
1453                                                         a->xyz, a->st,
1454                                                         b->xyz, b->st,
1455                                                         c->xyz, c->st);
1456                                         }
1457                                         break;
1458                         }
1459                 }
1460         }
1461         
1462         /* scale planes */
1463         if(uniform)
1464         {
1465                 for( i = 0; i < numBSPPlanes; i++ )
1466                 {
1467                         bspPlanes[ i ].dist *= scale[0];
1468                 }
1469         }
1470         else
1471         {
1472                 for( i = 0; i < numBSPPlanes; i++ )
1473                 {
1474                         bspPlanes[ i ].normal[0] /= scale[0];
1475                         bspPlanes[ i ].normal[1] /= scale[1];
1476                         bspPlanes[ i ].normal[2] /= scale[2];
1477                         f = 1/VectorLength(bspPlanes[i].normal);
1478                         VectorScale(bspPlanes[i].normal, f, bspPlanes[i].normal);
1479                         bspPlanes[ i ].dist *= f;
1480                 }
1481         }
1482         
1483         /* scale gridsize */
1484         GetVectorForKey( &entities[ 0 ], "gridsize", vec );
1485         if( (vec[ 0 ] + vec[ 1 ] + vec[ 2 ]) == 0.0f )
1486                 VectorCopy( gridSize, vec );
1487         vec[0] *= scale[0];
1488         vec[1] *= scale[1];
1489         vec[2] *= scale[2];
1490         sprintf( str, "%f %f %f", vec[ 0 ], vec[ 1 ], vec[ 2 ] );
1491         SetKeyValue( &entities[ 0 ], "gridsize", str );
1492
1493         /* inject command line parameters */
1494         InjectCommandLine(argv, 0, argc - 1);
1495         
1496         /* write the bsp */
1497         UnparseEntities();
1498         StripExtension( source );
1499         DefaultExtension( source, "_s.bsp" );
1500         Sys_Printf( "Writing %s\n", source );
1501         WriteBSPFile( source );
1502         
1503         /* return to sender */
1504         return 0;
1505 }
1506
1507
1508 /*
1509 PseudoCompileBSP()
1510 a stripped down ProcessModels
1511 */
1512 void PseudoCompileBSP(qboolean need_tree)
1513 {
1514         int models;
1515         char modelValue[10];
1516         entity_t *entity;
1517         face_t *faces;
1518         tree_t *tree;
1519         node_t *node;
1520         brush_t *brush;
1521         side_t *side;
1522         int i;
1523
1524         SetDrawSurfacesBuffer();
1525         mapDrawSurfs = safe_malloc( sizeof( mapDrawSurface_t ) * MAX_MAP_DRAW_SURFS );
1526         memset( mapDrawSurfs, 0, sizeof( mapDrawSurface_t ) * MAX_MAP_DRAW_SURFS );
1527         numMapDrawSurfs = 0;
1528
1529         BeginBSPFile();
1530         models = 1;
1531         for( mapEntityNum = 0; mapEntityNum < numEntities; mapEntityNum++ )
1532         {
1533                 /* get entity */
1534                 entity = &entities[ mapEntityNum ];
1535                 if( entity->brushes == NULL && entity->patches == NULL )
1536                         continue;
1537
1538                 if(mapEntityNum != 0)
1539                 {
1540                         sprintf( modelValue, "*%d", models++);
1541                         SetKeyValue(entity, "model", modelValue);
1542                 }
1543                 
1544                 /* process the model */
1545                 Sys_FPrintf( SYS_VRB, "############### model %i ###############\n", numBSPModels );
1546                 BeginModel();
1547
1548                 entity->firstDrawSurf = numMapDrawSurfs;
1549
1550                 ClearMetaTriangles();
1551                 PatchMapDrawSurfs(entity);
1552
1553                 if(mapEntityNum == 0 && need_tree)
1554                 {
1555                         faces = MakeStructuralBSPFaceList(entities[0].brushes);
1556                         tree = FaceBSP(faces);
1557                         node = tree->headnode;
1558                 }
1559                 else
1560                 {
1561                         node = AllocNode();
1562                         node->planenum = PLANENUM_LEAF;
1563                         tree = AllocTree();
1564                         tree->headnode = node;
1565                 }
1566
1567                 /* a minimized ClipSidesIntoTree */
1568                 for( brush = entity->brushes; brush; brush = brush->next )
1569                 {
1570                         /* walk the brush sides */
1571                         for( i = 0; i < brush->numsides; i++ )
1572                         {
1573                                 /* get side */
1574                                 side = &brush->sides[ i ];
1575                                 if( side->winding == NULL )
1576                                         continue;
1577                                 /* shader? */
1578                                 if( side->shaderInfo == NULL )
1579                                         continue;
1580                                 /* save this winding as a visible surface */
1581                                 DrawSurfaceForSide(entity, brush, side, side->winding);
1582                         }
1583                 }
1584
1585                 if(meta)
1586                 {
1587                         ClassifyEntitySurfaces(entity);
1588                         MakeEntityDecals(entity);
1589                         MakeEntityMetaTriangles(entity);
1590                         SmoothMetaTriangles();
1591                         MergeMetaTriangles();
1592                 }
1593                 FilterDrawsurfsIntoTree(entity, tree);
1594
1595                 FilterStructuralBrushesIntoTree(entity, tree);
1596                 FilterDetailBrushesIntoTree(entity, tree);
1597
1598                 EmitBrushes(entity->brushes, &entity->firstBrush, &entity->numBrushes );
1599                 EndModel(entity, node);
1600         }
1601         EndBSPFile(qfalse);
1602 }
1603
1604 /*
1605 ConvertBSPMain()
1606 main argument processing function for bsp conversion
1607 */
1608
1609 int ConvertBSPMain( int argc, char **argv )
1610 {
1611         int             i;
1612         int             (*convertFunc)( char * );
1613         game_t  *convertGame;
1614         char            ext[1024];
1615         qboolean        map_allowed, force_bsp, force_map;
1616         
1617         
1618         /* set default */
1619         convertFunc = ConvertBSPToASE;
1620         convertGame = NULL;
1621         map_allowed = qfalse;
1622         force_bsp = qfalse;
1623         force_map = qfalse;
1624         
1625         /* arg checking */
1626         if( argc < 1 )
1627         {
1628                 Sys_Printf( "Usage: q3map -convert -format <ase|obj|map_bp|map> [-shadesasbitmap|-lightmapsastexcoord|-deluxemapsastexcoord] [-readbsp|-readmap [-meta|-patchmeta]] <mapname>\n" );
1629                 return 0;
1630         }
1631         
1632         /* process arguments */
1633         for( i = 1; i < (argc - 1); i++ )
1634         {
1635                 /* -format map|ase|... */
1636                 if( !strcmp( argv[ i ],  "-format" ) )
1637                 {
1638                         i++;
1639                         if( !Q_stricmp( argv[ i ], "ase" ) )
1640                         {
1641                                 convertFunc = ConvertBSPToASE;
1642                                 map_allowed = qfalse;
1643                         }
1644                         else if( !Q_stricmp( argv[ i ], "obj" ) )
1645                         {
1646                                 convertFunc = ConvertBSPToOBJ;
1647                                 map_allowed = qfalse;
1648                         }
1649                         else if( !Q_stricmp( argv[ i ], "map_bp" ) )
1650                         {
1651                                 convertFunc = ConvertBSPToMap_BP;
1652                                 map_allowed = qtrue;
1653                         }
1654                         else if( !Q_stricmp( argv[ i ], "map" ) )
1655                         {
1656                                 convertFunc = ConvertBSPToMap;
1657                                 map_allowed = qtrue;
1658                         }
1659                         else
1660                         {
1661                                 convertGame = GetGame( argv[ i ] );
1662                                 map_allowed = qfalse;
1663                                 if( convertGame == NULL )
1664                                         Sys_Printf( "Unknown conversion format \"%s\". Defaulting to ASE.\n", argv[ i ] );
1665                         }
1666                 }
1667                 else if( !strcmp( argv[ i ],  "-ne" ) )
1668                 {
1669                         normalEpsilon = atof( argv[ i + 1 ] );
1670                         i++;
1671                         Sys_Printf( "Normal epsilon set to %f\n", normalEpsilon );
1672                 }
1673                 else if( !strcmp( argv[ i ],  "-de" ) )
1674                 {
1675                         distanceEpsilon = atof( argv[ i + 1 ] );
1676                         i++;
1677                         Sys_Printf( "Distance epsilon set to %f\n", distanceEpsilon );
1678                 }
1679                 else if( !strcmp( argv[ i ],  "-shadersasbitmap" ) )
1680                         shadersAsBitmap = qtrue;
1681                 else if( !strcmp( argv[ i ],  "-lightmapsastexcoord" ) )
1682                         lightmapsAsTexcoord = qtrue;
1683                 else if( !strcmp( argv[ i ],  "-deluxemapsastexcoord" ) )
1684                 {
1685                         lightmapsAsTexcoord = qtrue;
1686                         deluxemap = qtrue;
1687                 }
1688                 else if( !strcmp( argv[ i ],  "-readbsp" ) )
1689                         force_bsp = qtrue;
1690                 else if( !strcmp( argv[ i ],  "-readmap" ) )
1691                         force_map = qtrue;
1692                 else if( !strcmp( argv[ i ],  "-meta" ) )
1693                         meta = qtrue;
1694                 else if( !strcmp( argv[ i ],  "-patchmeta" ) )
1695                 {
1696                         meta = qtrue;
1697                         patchMeta = qtrue;
1698                 }
1699         }
1700
1701         LoadShaderInfo();
1702         
1703         /* clean up map name */
1704         strcpy(source, ExpandArg(argv[i]));
1705         ExtractFileExtension(source, ext);
1706
1707         if(!map_allowed && !force_map)
1708                 force_bsp = qtrue;
1709
1710         if(force_map || (!force_bsp && !Q_stricmp(ext, "map") && map_allowed))
1711         {
1712                 if(!map_allowed)
1713                         Sys_Printf("WARNING: the requested conversion should not be done from .map files. Compile a .bsp first.\n");
1714                 StripExtension(source);
1715                 DefaultExtension(source, ".map");
1716                 Sys_Printf("Loading %s\n", source);
1717                 LoadMapFile(source, qfalse, convertGame == NULL);
1718                 PseudoCompileBSP(convertGame != NULL);
1719         }
1720         else
1721         {
1722                 StripExtension(source);
1723                 DefaultExtension(source, ".bsp");
1724                 Sys_Printf("Loading %s\n", source);
1725                 LoadBSPFile(source);
1726                 ParseEntities();
1727         }
1728         
1729         /* bsp format convert? */
1730         if( convertGame != NULL )
1731         {
1732                 /* set global game */
1733                 game = convertGame;
1734                 
1735                 /* write bsp */
1736                 StripExtension( source );
1737                 DefaultExtension( source, "_c.bsp" );
1738                 Sys_Printf( "Writing %s\n", source );
1739                 WriteBSPFile( source );
1740                 
1741                 /* return to sender */
1742                 return 0;
1743         }
1744         
1745         /* normal convert */
1746         return convertFunc( source );
1747 }
1748
1749
1750
1751 /*
1752 main()
1753 q3map mojo...
1754 */
1755
1756 int main( int argc, char **argv )
1757 {
1758         int             i, r;
1759         double  start, end;
1760         
1761         
1762         /* we want consistent 'randomness' */
1763         srand( 0 );
1764         
1765         /* start timer */
1766         start = I_FloatTime();
1767
1768         /* this was changed to emit version number over the network */
1769         printf( Q3MAP_VERSION "\n" );
1770         
1771         /* set exit call */
1772         atexit( ExitQ3Map );
1773
1774         /* read general options first */
1775         for( i = 1; i < argc; i++ )
1776         {
1777                 /* -connect */
1778                 if( !strcmp( argv[ i ], "-connect" ) )
1779                 {
1780                         argv[ i ] = NULL;
1781                         i++;
1782                         Broadcast_Setup( argv[ i ] );
1783                         argv[ i ] = NULL;
1784                 }
1785                 
1786                 /* verbose */
1787                 else if( !strcmp( argv[ i ], "-v" ) )
1788                 {
1789                         if(!verbose)
1790                         {
1791                                 verbose = qtrue;
1792                                 argv[ i ] = NULL;
1793                         }
1794                 }
1795                 
1796                 /* force */
1797                 else if( !strcmp( argv[ i ], "-force" ) )
1798                 {
1799                         force = qtrue;
1800                         argv[ i ] = NULL;
1801                 }
1802                 
1803                 /* patch subdivisions */
1804                 else if( !strcmp( argv[ i ], "-subdivisions" ) )
1805                 {
1806                         argv[ i ] = NULL;
1807                         i++;
1808                         patchSubdivisions = atoi( argv[ i ] );
1809                         argv[ i ] = NULL;
1810                         if( patchSubdivisions <= 0 )
1811                                 patchSubdivisions = 1;
1812                 }
1813                 
1814                 /* threads */
1815                 else if( !strcmp( argv[ i ], "-threads" ) )
1816                 {
1817                         argv[ i ] = NULL;
1818                         i++;
1819                         numthreads = atoi( argv[ i ] );
1820                         argv[ i ] = NULL;
1821                 }
1822         }
1823
1824         /* init model library */
1825         PicoInit();
1826         PicoSetMallocFunc( safe_malloc );
1827         PicoSetFreeFunc( free );
1828         PicoSetPrintFunc( PicoPrintFunc );
1829         PicoSetLoadFileFunc( PicoLoadFileFunc );
1830         PicoSetFreeFileFunc( free );
1831         
1832         /* set number of threads */
1833         ThreadSetDefault();
1834         
1835         /* generate sinusoid jitter table */
1836         for( i = 0; i < MAX_JITTERS; i++ )
1837         {
1838                 jitters[ i ] = sin( i * 139.54152147 );
1839                 //%     Sys_Printf( "Jitter %4d: %f\n", i, jitters[ i ] );
1840         }
1841         
1842         /* we print out two versions, q3map's main version (since it evolves a bit out of GtkRadiant)
1843            and we put the GtkRadiant version to make it easy to track with what version of Radiant it was built with */
1844         
1845         Sys_Printf( "Q3Map         - v1.0r (c) 1999 Id Software Inc.\n" );
1846         Sys_Printf( "Q3Map (ydnar) - v" Q3MAP_VERSION "\n" );
1847         Sys_Printf( "NetRadiant    - v" RADIANT_VERSION " " __DATE__ " " __TIME__ "\n" );
1848         Sys_Printf( "%s\n", Q3MAP_MOTD );
1849         
1850         /* ydnar: new path initialization */
1851         InitPaths( &argc, argv );
1852
1853         /* set game options */
1854         if (!patchSubdivisions)
1855                 patchSubdivisions = game->patchSubdivisions;
1856         
1857         /* check if we have enough options left to attempt something */
1858         if( argc < 2 )
1859                 Error( "Usage: %s [general options] [options] mapfile", argv[ 0 ] );
1860         
1861         /* fixaas */
1862         if( !strcmp( argv[ 1 ], "-fixaas" ) )
1863                 r = FixAAS( argc - 1, argv + 1 );
1864         
1865         /* analyze */
1866         else if( !strcmp( argv[ 1 ], "-analyze" ) )
1867                 r = AnalyzeBSP( argc - 1, argv + 1 );
1868         
1869         /* info */
1870         else if( !strcmp( argv[ 1 ], "-info" ) )
1871                 r = BSPInfo( argc - 2, argv + 2 );
1872         
1873         /* vis */
1874         else if( !strcmp( argv[ 1 ], "-vis" ) )
1875                 r = VisMain( argc - 1, argv + 1 );
1876         
1877         /* light */
1878         else if( !strcmp( argv[ 1 ], "-light" ) )
1879                 r = LightMain( argc - 1, argv + 1 );
1880         
1881         /* vlight */
1882         else if( !strcmp( argv[ 1 ], "-vlight" ) )
1883         {
1884                 Sys_Printf( "WARNING: VLight is no longer supported, defaulting to -light -fast instead\n\n" );
1885                 argv[ 1 ] = "-fast";    /* eek a hack */
1886                 r = LightMain( argc, argv );
1887         }
1888         
1889         /* ydnar: lightmap export */
1890         else if( !strcmp( argv[ 1 ], "-export" ) )
1891                 r = ExportLightmapsMain( argc - 1, argv + 1 );
1892         
1893         /* ydnar: lightmap import */
1894         else if( !strcmp( argv[ 1 ], "-import" ) )
1895                 r = ImportLightmapsMain( argc - 1, argv + 1 );
1896         
1897         /* ydnar: bsp scaling */
1898         else if( !strcmp( argv[ 1 ], "-scale" ) )
1899                 r = ScaleBSPMain( argc - 1, argv + 1 );
1900         
1901         /* ydnar: bsp conversion */
1902         else if( !strcmp( argv[ 1 ], "-convert" ) )
1903                 r = ConvertBSPMain( argc - 1, argv + 1 );
1904         
1905         /* div0: minimap */
1906         else if( !strcmp( argv[ 1 ], "-minimap" ) )
1907                 r = MiniMapBSPMain(argc - 1, argv + 1);
1908
1909         /* ydnar: otherwise create a bsp */
1910         else
1911                 r = BSPMain( argc, argv );
1912         
1913         /* emit time */
1914         end = I_FloatTime();
1915         Sys_Printf( "%9.0f seconds elapsed\n", end - start );
1916         
1917         /* shut down connection */
1918         Broadcast_Shutdown();
1919         
1920         /* return any error code */
1921         return r;
1922 }