]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/q3map2/main.c
-minimap is now a main option... to be used on already compiled BSPs
[xonotic/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 /* borrowed from light.c */
70 void WriteTGA24( char *filename, byte *data, int width, int height, qboolean flip );
71 typedef struct minimap_s
72 {
73         bspModel_t *model;
74         int width;
75         int height;
76         int samples;
77         float sharpen;
78         float sharpen_boxmult;
79         float sharpen_centermult;
80         float *data1f;
81         float *sharpendata1f;
82         vec3_t mins, size;
83 }
84 minimap_t;
85 static minimap_t minimap;
86
87 qboolean BrushIntersectionWithLine(bspBrush_t *brush, vec3_t start, vec3_t dir, float *t_in, float *t_out)
88 {
89         int i;
90         qboolean in = qfalse, out = qfalse;
91         bspBrushSide_t *sides = &bspBrushSides[brush->firstSide];
92
93         for(i = 0; i < brush->numSides; ++i)
94         {
95                 bspPlane_t *p = &bspPlanes[sides[i].planeNum];
96                 float sn = DotProduct(start, p->normal);
97                 float dn = DotProduct(dir, p->normal);
98                 if(dn == 0)
99                 {
100                         if(sn > p->dist)
101                                 return qfalse; // outside!
102                 }
103                 else
104                 {
105                         float t = (p->dist - sn) / dn;
106                         if(dn < 0)
107                         {
108                                 if(!in || t > *t_in)
109                                 {
110                                         *t_in = t;
111                                         in = qtrue;
112                                         // as t_in can only increase, and t_out can only decrease, early out
113                                         if(out && *t_in >= *t_out)
114                                                 return qfalse;
115                                 }
116                         }
117                         else
118                         {
119                                 if(!out || t < *t_out)
120                                 {
121                                         *t_out = t;
122                                         out = qtrue;
123                                         // as t_in can only increase, and t_out can only decrease, early out
124                                         if(in && *t_in >= *t_out)
125                                                 return qfalse;
126                                 }
127                         }
128                 }
129         }
130         return in && out;
131 }
132
133 static float MiniMapSample(float x, float y)
134 {
135         vec3_t org, dir;
136         int i, bi;
137         float t0, t1;
138         float samp;
139         bspBrush_t *b;
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                         bspBrushSide_t *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 static void GenerateMiniMapRunner(int y)
181 {
182         int x, i;
183         float *p = &minimap.data1f[y * minimap.width];
184         float ymin = minimap.mins[1] + minimap.size[1] * (y / (float) minimap.height);
185         float dx   =                   minimap.size[0]      / (float) minimap.width;
186         float dy   =                   minimap.size[1]      / (float) minimap.height;
187
188         for(x = 0; x < minimap.width; ++x)
189         {
190                 float xmin = minimap.mins[0] + minimap.size[0] * (x / (float) minimap.width);
191                 float val = 0;
192
193                 for(i = 0; i < minimap.samples; ++i)
194                 {
195                         float thisval = MiniMapSample(
196                                 xmin + Random() * dx,
197                                 ymin + Random() * dy
198                         );
199                         val += thisval;
200                 }
201                 val /= minimap.samples * minimap.size[2];
202                 *p++ = val;
203         }
204 }
205
206 static void GenerateMiniMapRunnerNoSamples(int y)
207 {
208         int x;
209         float *p = &minimap.data1f[y * minimap.width];
210         float ymin = minimap.mins[1] + minimap.size[1] * (y / (float) minimap.height);
211
212         for(x = 0; x < minimap.width; ++x)
213         {
214                 float xmin = minimap.mins[0] + minimap.size[0] * (x / (float) minimap.width);
215                 *p++ = MiniMapSample(xmin, ymin) / minimap.size[2];
216         }
217 }
218
219 static void SharpenMiniMapRunner(int y)
220 {
221         int x;
222         qboolean up = (y > 0);
223         qboolean down = (y < minimap.height - 1);
224         float *p = &minimap.data1f[y * minimap.width];
225         float *q = &minimap.sharpendata1f[y * minimap.width];
226
227         for(x = 0; x < minimap.width; ++x)
228         {
229                 qboolean left = (x > 0);
230                 qboolean right = (x < minimap.width - 1);
231                 float val = p[0] * minimap.sharpen_centermult;
232
233                 if(left && up)
234                         val += p[-1 -minimap.width] * minimap.sharpen_boxmult;
235                 if(left && down)
236                         val += p[-1 +minimap.width] * minimap.sharpen_boxmult;
237                 if(right && up)
238                         val += p[+1 -minimap.width] * minimap.sharpen_boxmult;
239                 if(right && down)
240                         val += p[+1 +minimap.width] * minimap.sharpen_boxmult;
241                         
242                 if(left)
243                         val += p[-1] * minimap.sharpen_boxmult;
244                 if(right)
245                         val += p[+1] * minimap.sharpen_boxmult;
246                 if(up)
247                         val += p[-minimap.width] * minimap.sharpen_boxmult;
248                 if(down)
249                         val += p[+minimap.width] * minimap.sharpen_boxmult;
250
251                 ++p;
252                 *q++ = val;
253         }
254 }
255
256 int MiniMapBSPMain( int argc, char **argv )
257 {
258         char minimapFilename[1024];
259         byte *data3b, *p;
260         float *q;
261         int x, y;
262
263         /* arg checking */
264         if( argc < 2 )
265         {
266                 Sys_Printf( "Usage: q3map [-v] -minimap [-size n] [-sharpen n] [-samples f] [-o filename.tga] <mapname>\n" );
267                 return 0;
268         }
269
270         strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
271         StripExtension( source );
272         DefaultExtension( source, ".bsp" );
273
274         strcpy( minimapFilename, ExpandArg( argv[ argc - 1 ] ) );
275         StripExtension( minimapFilename );
276         DefaultExtension( minimapFilename, ".tga" );
277
278         minimap.width = minimap.height = 512;
279         minimap.samples = 1;
280         minimap.sharpen = 1;
281         if(minimap.sharpen)
282         {
283                 minimap.sharpen_centermult = 8 * minimap.sharpen + 1;
284                 minimap.sharpen_boxmult    =    -minimap.sharpen;
285         }
286
287         minimap.data1f = safe_malloc(minimap.width * minimap.height * sizeof(*minimap.data1f));
288         data3b = safe_malloc(minimap.width * minimap.height * 3);
289         if(minimap.sharpen >= 0)
290                 minimap.sharpendata1f = safe_malloc(minimap.width * minimap.height * sizeof(*minimap.data1f));
291
292         /* load the bsp */
293         Sys_Printf( "Loading %s\n", source );
294         LoadBSPFile( source );
295
296         minimap.model = &bspModels[0];
297         VectorCopy(minimap.model->mins, minimap.mins);
298         VectorSubtract(minimap.model->maxs, minimap.model->mins, minimap.size);
299
300         SetupBrushes();
301
302         if(minimap.samples <= 1)
303         {
304                 Sys_Printf( "\n--- GenerateMiniMap (%d) ---\n", minimap.height );
305                 RunThreadsOnIndividual(minimap.height, qtrue, GenerateMiniMapRunnerNoSamples);
306         }
307         else
308         {
309                 Sys_Printf( "\n--- GenerateMiniMap (%d) ---\n", minimap.height );
310                 RunThreadsOnIndividual(minimap.height, qtrue, GenerateMiniMapRunner);
311         }
312
313         if(minimap.sharpendata1f)
314         {
315                 Sys_Printf( "\n--- SharpenMiniMap (%d) ---\n", minimap.height );
316                 RunThreadsOnIndividual(minimap.height, qtrue, SharpenMiniMapRunner);
317                 q = minimap.sharpendata1f;
318         }
319         else
320         {
321                 q = minimap.data1f;
322         }
323
324         Sys_Printf( "\nConverting...");
325         p = data3b;
326         for(y = 0; y < minimap.height; ++y)
327                 for(x = 0; x < minimap.width; ++x)
328                 {
329                         float v = *q++;
330                         byte b;
331                         if(v < 0) v = 0;
332                         if(v > 255.0/256.0) v = 255.0/256.0;
333                         b = v * 256;
334                         *p++ = b;
335                         *p++ = b;
336                         *p++ = b;
337                 }
338
339         Sys_Printf( " writing to %s...", minimapFilename );
340         WriteTGA24(minimapFilename, data3b, minimap.width, minimap.height, qfalse);
341
342         Sys_Printf( " done.\n" );
343
344         /* return to sender */
345         return 0;
346 }
347
348
349
350
351
352 /*
353 MD4BlockChecksum()
354 calculates an md4 checksum for a block of data
355 */
356
357 static int MD4BlockChecksum( void *buffer, int length )
358 {
359         return Com_BlockChecksum(buffer, length);
360 }
361
362 /*
363 FixAAS()
364 resets an aas checksum to match the given BSP
365 */
366
367 int FixAAS( int argc, char **argv )
368 {
369         int                     length, checksum;
370         void            *buffer;
371         FILE            *file;
372         char            aas[ 1024 ], **ext;
373         char            *exts[] =
374                                 {
375                                         ".aas",
376                                         "_b0.aas",
377                                         "_b1.aas",
378                                         NULL
379                                 };
380         
381         
382         /* arg checking */
383         if( argc < 2 )
384         {
385                 Sys_Printf( "Usage: q3map -fixaas [-v] <mapname>\n" );
386                 return 0;
387         }
388         
389         /* do some path mangling */
390         strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
391         StripExtension( source );
392         DefaultExtension( source, ".bsp" );
393         
394         /* note it */
395         Sys_Printf( "--- FixAAS ---\n" );
396         
397         /* load the bsp */
398         Sys_Printf( "Loading %s\n", source );
399         length = LoadFile( source, &buffer );
400         
401         /* create bsp checksum */
402         Sys_Printf( "Creating checksum...\n" );
403         checksum = LittleLong( MD4BlockChecksum( buffer, length ) );
404         
405         /* write checksum to aas */
406         ext = exts;
407         while( *ext )
408         {
409                 /* mangle name */
410                 strcpy( aas, source );
411                 StripExtension( aas );
412                 strcat( aas, *ext );
413                 Sys_Printf( "Trying %s\n", aas );
414                 ext++;
415                 
416                 /* fix it */
417                 file = fopen( aas, "r+b" );
418                 if( !file )
419                         continue;
420                 if( fwrite( &checksum, 4, 1, file ) != 1 )
421                         Error( "Error writing checksum to %s", aas );
422                 fclose( file );
423         }
424         
425         /* return to sender */
426         return 0;
427 }
428
429
430
431 /*
432 AnalyzeBSP() - ydnar
433 analyzes a Quake engine BSP file
434 */
435
436 typedef struct abspHeader_s
437 {
438         char                    ident[ 4 ];
439         int                             version;
440         
441         bspLump_t               lumps[ 1 ];     /* unknown size */
442 }
443 abspHeader_t;
444
445 typedef struct abspLumpTest_s
446 {
447         int                             radix, minCount;
448         char                    *name;
449 }
450 abspLumpTest_t;
451
452 int AnalyzeBSP( int argc, char **argv )
453 {
454         abspHeader_t                    *header;
455         int                                             size, i, version, offset, length, lumpInt, count;
456         char                                    ident[ 5 ];
457         void                                    *lump;
458         float                                   lumpFloat;
459         char                                    lumpString[ 1024 ], source[ 1024 ];
460         qboolean                                lumpSwap = qfalse;
461         abspLumpTest_t                  *lumpTest;
462         static abspLumpTest_t   lumpTests[] =
463                                                         {
464                                                                 { sizeof( bspPlane_t ),                 6,              "IBSP LUMP_PLANES" },
465                                                                 { sizeof( bspBrush_t ),                 1,              "IBSP LUMP_BRUSHES" },
466                                                                 { 8,                                                    6,              "IBSP LUMP_BRUSHSIDES" },
467                                                                 { sizeof( bspBrushSide_t ),             6,              "RBSP LUMP_BRUSHSIDES" },
468                                                                 { sizeof( bspModel_t ),                 1,              "IBSP LUMP_MODELS" },
469                                                                 { sizeof( bspNode_t ),                  2,              "IBSP LUMP_NODES" },
470                                                                 { sizeof( bspLeaf_t ),                  1,              "IBSP LUMP_LEAFS" },
471                                                                 { 104,                                                  3,              "IBSP LUMP_DRAWSURFS" },
472                                                                 { 44,                                                   3,              "IBSP LUMP_DRAWVERTS" },
473                                                                 { 4,                                                    6,              "IBSP LUMP_DRAWINDEXES" },
474                                                                 { 128 * 128 * 3,                                1,              "IBSP LUMP_LIGHTMAPS" },
475                                                                 { 256 * 256 * 3,                                1,              "IBSP LUMP_LIGHTMAPS (256 x 256)" },
476                                                                 { 512 * 512 * 3,                                1,              "IBSP LUMP_LIGHTMAPS (512 x 512)" },
477                                                                 { 0, 0, NULL }
478                                                         };
479         
480         
481         /* arg checking */
482         if( argc < 1 )
483         {
484                 Sys_Printf( "Usage: q3map -analyze [-lumpswap] [-v] <mapname>\n" );
485                 return 0;
486         }
487         
488         /* process arguments */
489         for( i = 1; i < (argc - 1); i++ )
490         {
491                 /* -format map|ase|... */
492                 if( !strcmp( argv[ i ],  "-lumpswap" ) )
493                 {
494                         Sys_Printf( "Swapped lump structs enabled\n" );
495                         lumpSwap = qtrue;
496                 }
497         }
498         
499         /* clean up map name */
500         strcpy( source, ExpandArg( argv[ i ] ) );
501         Sys_Printf( "Loading %s\n", source );
502         
503         /* load the file */
504         size = LoadFile( source, (void**) &header );
505         if( size == 0 || header == NULL )
506         {
507                 Sys_Printf( "Unable to load %s.\n", source );
508                 return -1;
509         }
510         
511         /* analyze ident/version */
512         memcpy( ident, header->ident, 4 );
513         ident[ 4 ] = '\0';
514         version = LittleLong( header->version );
515         
516         Sys_Printf( "Identity:      %s\n", ident );
517         Sys_Printf( "Version:       %d\n", version );
518         Sys_Printf( "---------------------------------------\n" );
519         
520         /* analyze each lump */
521         for( i = 0; i < 100; i++ )
522         {
523                 /* call of duty swapped lump pairs */
524                 if( lumpSwap )
525                 {
526                         offset = LittleLong( header->lumps[ i ].length );
527                         length = LittleLong( header->lumps[ i ].offset );
528                 }
529                 
530                 /* standard lump pairs */
531                 else
532                 {
533                         offset = LittleLong( header->lumps[ i ].offset );
534                         length = LittleLong( header->lumps[ i ].length );
535                 }
536                 
537                 /* extract data */
538                 lump = (byte*) header + offset;
539                 lumpInt = LittleLong( (int) *((int*) lump) );
540                 lumpFloat = LittleFloat( (float) *((float*) lump) );
541                 memcpy( lumpString, (char*) lump, (length < 1024 ? length : 1024) );
542                 lumpString[ 1024 ] = '\0';
543                 
544                 /* print basic lump info */
545                 Sys_Printf( "Lump:          %d\n", i );
546                 Sys_Printf( "Offset:        %d bytes\n", offset );
547                 Sys_Printf( "Length:        %d bytes\n", length );
548                 
549                 /* only operate on valid lumps */
550                 if( length > 0 )
551                 {
552                         /* print data in 4 formats */
553                         Sys_Printf( "As hex:        %08X\n", lumpInt );
554                         Sys_Printf( "As int:        %d\n", lumpInt );
555                         Sys_Printf( "As float:      %f\n", lumpFloat );
556                         Sys_Printf( "As string:     %s\n", lumpString );
557                         
558                         /* guess lump type */
559                         if( lumpString[ 0 ] == '{' && lumpString[ 2 ] == '"' )
560                                 Sys_Printf( "Type guess:    IBSP LUMP_ENTITIES\n" );
561                         else if( strstr( lumpString, "textures/" ) )
562                                 Sys_Printf( "Type guess:    IBSP LUMP_SHADERS\n" );
563                         else
564                         {
565                                 /* guess based on size/count */
566                                 for( lumpTest = lumpTests; lumpTest->radix > 0; lumpTest++ )
567                                 {
568                                         if( (length % lumpTest->radix) != 0 )
569                                                 continue;
570                                         count = length / lumpTest->radix;
571                                         if( count < lumpTest->minCount )
572                                                 continue;
573                                         Sys_Printf( "Type guess:    %s (%d x %d)\n", lumpTest->name, count, lumpTest->radix );
574                                 }
575                         }
576                 }
577                 
578                 Sys_Printf( "---------------------------------------\n" );
579                 
580                 /* end of file */
581                 if( offset + length >= size )
582                         break;
583         }
584         
585         /* last stats */
586         Sys_Printf( "Lump count:    %d\n", i + 1 );
587         Sys_Printf( "File size:     %d bytes\n", size );
588         
589         /* return to caller */
590         return 0;
591 }
592
593
594
595 /*
596 BSPInfo()
597 emits statistics about the bsp file
598 */
599
600 int BSPInfo( int count, char **fileNames )
601 {
602         int                     i;
603         char            source[ 1024 ], ext[ 64 ];
604         int                     size;
605         FILE            *f;
606         
607         
608         /* dummy check */
609         if( count < 1 )
610         {
611                 Sys_Printf( "No files to dump info for.\n");
612                 return -1;
613         }
614         
615         /* enable info mode */
616         infoMode = qtrue;
617         
618         /* walk file list */
619         for( i = 0; i < count; i++ )
620         {
621                 Sys_Printf( "---------------------------------\n" );
622                 
623                 /* mangle filename and get size */
624                 strcpy( source, fileNames[ i ] );
625                 ExtractFileExtension( source, ext );
626                 if( !Q_stricmp( ext, "map" ) )
627                         StripExtension( source );
628                 DefaultExtension( source, ".bsp" );
629                 f = fopen( source, "rb" );
630                 if( f )
631                 {
632                         size = Q_filelength (f);
633                         fclose( f );
634                 }
635                 else
636                         size = 0;
637                 
638                 /* load the bsp file and print lump sizes */
639                 Sys_Printf( "%s\n", source );
640                 LoadBSPFile( source );          
641                 PrintBSPFileSizes();
642                 
643                 /* print sizes */
644                 Sys_Printf( "\n" );
645                 Sys_Printf( "          total         %9d\n", size );
646                 Sys_Printf( "                        %9d KB\n", size / 1024 );
647                 Sys_Printf( "                        %9d MB\n", size / (1024 * 1024) );
648                 
649                 Sys_Printf( "---------------------------------\n" );
650         }
651         
652         /* return count */
653         return i;
654 }
655
656
657 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)
658 {
659         vec4_t scoeffs, tcoeffs;
660         float md;
661         m4x4_t solvematrix;
662
663         vec3_t norm;
664         vec3_t dab, dac;
665         VectorSubtract(bxyz, axyz, dab);
666         VectorSubtract(cxyz, axyz, dac);
667         CrossProduct(dab, dac, norm);
668         
669         // assume:
670         //   s = f(x, y, z)
671         //   s(v + norm) = s(v) when n ortho xyz
672         
673         // s(v) = DotProduct(v, scoeffs) + scoeffs[3]
674
675         // solve:
676         //   scoeffs * (axyz, 1) == ast[0]
677         //   scoeffs * (bxyz, 1) == bst[0]
678         //   scoeffs * (cxyz, 1) == cst[0]
679         //   scoeffs * (norm, 0) == 0
680         // scoeffs * [axyz, 1 | bxyz, 1 | cxyz, 1 | norm, 0] = [ast[0], bst[0], cst[0], 0]
681         solvematrix[0] = axyz[0];
682         solvematrix[4] = axyz[1];
683         solvematrix[8] = axyz[2];
684         solvematrix[12] = 1;
685         solvematrix[1] = bxyz[0];
686         solvematrix[5] = bxyz[1];
687         solvematrix[9] = bxyz[2];
688         solvematrix[13] = 1;
689         solvematrix[2] = cxyz[0];
690         solvematrix[6] = cxyz[1];
691         solvematrix[10] = cxyz[2];
692         solvematrix[14] = 1;
693         solvematrix[3] = norm[0];
694         solvematrix[7] = norm[1];
695         solvematrix[11] = norm[2];
696         solvematrix[15] = 0;
697
698         md = m4_det(solvematrix);
699         if(md*md < 1e-10)
700         {
701                 Sys_Printf("Cannot invert some matrix, some texcoords aren't extrapolated!");
702                 return;
703         }
704
705         m4x4_invert(solvematrix);
706
707         scoeffs[0] = ast[0];
708         scoeffs[1] = bst[0];
709         scoeffs[2] = cst[0];
710         scoeffs[3] = 0;
711         m4x4_transform_vec4(solvematrix, scoeffs);
712         tcoeffs[0] = ast[1];
713         tcoeffs[1] = bst[1];
714         tcoeffs[2] = cst[1];
715         tcoeffs[3] = 0;
716         m4x4_transform_vec4(solvematrix, tcoeffs);
717
718         ast_out[0] = scoeffs[0] * axyz_new[0] + scoeffs[1] * axyz_new[1] + scoeffs[2] * axyz_new[2] + scoeffs[3];
719         ast_out[1] = tcoeffs[0] * axyz_new[0] + tcoeffs[1] * axyz_new[1] + tcoeffs[2] * axyz_new[2] + tcoeffs[3];
720         bst_out[0] = scoeffs[0] * bxyz_new[0] + scoeffs[1] * bxyz_new[1] + scoeffs[2] * bxyz_new[2] + scoeffs[3];
721         bst_out[1] = tcoeffs[0] * bxyz_new[0] + tcoeffs[1] * bxyz_new[1] + tcoeffs[2] * bxyz_new[2] + tcoeffs[3];
722         cst_out[0] = scoeffs[0] * cxyz_new[0] + scoeffs[1] * cxyz_new[1] + scoeffs[2] * cxyz_new[2] + scoeffs[3];
723         cst_out[1] = tcoeffs[0] * cxyz_new[0] + tcoeffs[1] * cxyz_new[1] + tcoeffs[2] * cxyz_new[2] + tcoeffs[3];
724 }
725
726 /*
727 ScaleBSPMain()
728 amaze and confuse your enemies with wierd scaled maps!
729 */
730
731 int ScaleBSPMain( int argc, char **argv )
732 {
733         int                     i, j;
734         float           f, a;
735         vec3_t scale;
736         vec3_t          vec;
737         char            str[ 1024 ];
738         int uniform, axis;
739         qboolean texscale;
740         float *old_xyzst = NULL;
741         
742         
743         /* arg checking */
744         if( argc < 3 )
745         {
746                 Sys_Printf( "Usage: q3map [-v] -scale [-tex] <value> <mapname>\n" );
747                 return 0;
748         }
749         
750         /* get scale */
751         scale[2] = scale[1] = scale[0] = atof( argv[ argc - 2 ] );
752         if(argc >= 4)
753                 scale[1] = scale[0] = atof( argv[ argc - 3 ] );
754         if(argc >= 5)
755                 scale[0] = atof( argv[ argc - 4 ] );
756
757         texscale = !strcmp(argv[1], "-tex");
758         
759         uniform = ((scale[0] == scale[1]) && (scale[1] == scale[2]));
760
761         if( scale[0] == 0.0f || scale[1] == 0.0f || scale[2] == 0.0f )
762         {
763                 Sys_Printf( "Usage: q3map [-v] -scale [-tex] <value> <mapname>\n" );
764                 Sys_Printf( "Non-zero scale value required.\n" );
765                 return 0;
766         }
767         
768         /* do some path mangling */
769         strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
770         StripExtension( source );
771         DefaultExtension( source, ".bsp" );
772         
773         /* load the bsp */
774         Sys_Printf( "Loading %s\n", source );
775         LoadBSPFile( source );
776         ParseEntities();
777         
778         /* note it */
779         Sys_Printf( "--- ScaleBSP ---\n" );
780         Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );
781         
782         /* scale entity keys */
783         for( i = 0; i < numBSPEntities && i < numEntities; i++ )
784         {
785                 /* scale origin */
786                 GetVectorForKey( &entities[ i ], "origin", vec );
787                 if( (vec[ 0 ] || vec[ 1 ] || vec[ 2 ]) )
788                 {
789                         vec[0] *= scale[0];
790                         vec[1] *= scale[1];
791                         vec[2] *= scale[2];
792                         sprintf( str, "%f %f %f", vec[ 0 ], vec[ 1 ], vec[ 2 ] );
793                         SetKeyValue( &entities[ i ], "origin", str );
794                 }
795
796                 a = FloatForKey( &entities[ i ], "angle" );
797                 if(a == -1 || a == -2) // z scale
798                         axis = 2;
799                 else if(fabs(sin(DEG2RAD(a))) < 0.707)
800                         axis = 0;
801                 else
802                         axis = 1;
803                 
804                 /* scale door lip */
805                 f = FloatForKey( &entities[ i ], "lip" );
806                 if( f )
807                 {
808                         f *= scale[axis];
809                         sprintf( str, "%f", f );
810                         SetKeyValue( &entities[ i ], "lip", str );
811                 }
812                 
813                 /* scale plat height */
814                 f = FloatForKey( &entities[ i ], "height" );
815                 if( f )
816                 {
817                         f *= scale[2];
818                         sprintf( str, "%f", f );
819                         SetKeyValue( &entities[ i ], "height", str );
820                 }
821
822                 // TODO maybe allow a definition file for entities to specify which values are scaled how?
823         }
824         
825         /* scale models */
826         for( i = 0; i < numBSPModels; i++ )
827         {
828                 bspModels[ i ].mins[0] *= scale[0];
829                 bspModels[ i ].mins[1] *= scale[1];
830                 bspModels[ i ].mins[2] *= scale[2];
831                 bspModels[ i ].maxs[0] *= scale[0];
832                 bspModels[ i ].maxs[1] *= scale[1];
833                 bspModels[ i ].maxs[2] *= scale[2];
834         }
835         
836         /* scale nodes */
837         for( i = 0; i < numBSPNodes; i++ )
838         {
839                 bspNodes[ i ].mins[0] *= scale[0];
840                 bspNodes[ i ].mins[1] *= scale[1];
841                 bspNodes[ i ].mins[2] *= scale[2];
842                 bspNodes[ i ].maxs[0] *= scale[0];
843                 bspNodes[ i ].maxs[1] *= scale[1];
844                 bspNodes[ i ].maxs[2] *= scale[2];
845         }
846         
847         /* scale leafs */
848         for( i = 0; i < numBSPLeafs; i++ )
849         {
850                 bspLeafs[ i ].mins[0] *= scale[0];
851                 bspLeafs[ i ].mins[1] *= scale[1];
852                 bspLeafs[ i ].mins[2] *= scale[2];
853                 bspLeafs[ i ].maxs[0] *= scale[0];
854                 bspLeafs[ i ].maxs[1] *= scale[1];
855                 bspLeafs[ i ].maxs[2] *= scale[2];
856         }
857         
858         if(texscale)
859         {
860                 Sys_Printf("Using texture unlocking (and probably breaking texture alignment a lot)\n");
861                 old_xyzst = safe_malloc(sizeof(*old_xyzst) * numBSPDrawVerts * 5);
862                 for(i = 0; i < numBSPDrawVerts; i++)
863                 {
864                         old_xyzst[5*i+0] = bspDrawVerts[i].xyz[0];
865                         old_xyzst[5*i+1] = bspDrawVerts[i].xyz[1];
866                         old_xyzst[5*i+2] = bspDrawVerts[i].xyz[2];
867                         old_xyzst[5*i+3] = bspDrawVerts[i].st[0];
868                         old_xyzst[5*i+4] = bspDrawVerts[i].st[1];
869                 }
870         }
871
872         /* scale drawverts */
873         for( i = 0; i < numBSPDrawVerts; i++ )
874         {
875                 bspDrawVerts[i].xyz[0] *= scale[0];
876                 bspDrawVerts[i].xyz[1] *= scale[1];
877                 bspDrawVerts[i].xyz[2] *= scale[2];
878                 bspDrawVerts[i].normal[0] /= scale[0];
879                 bspDrawVerts[i].normal[1] /= scale[1];
880                 bspDrawVerts[i].normal[2] /= scale[2];
881                 VectorNormalize(bspDrawVerts[i].normal, bspDrawVerts[i].normal);
882         }
883
884         if(texscale)
885         {
886                 for(i = 0; i < numBSPDrawSurfaces; i++)
887                 {
888                         switch(bspDrawSurfaces[i].surfaceType)
889                         {
890                                 case SURFACE_FACE:
891                                 case SURFACE_META:
892                                         if(bspDrawSurfaces[i].numIndexes % 3)
893                                                 Error("Not a triangulation!");
894                                         for(j = bspDrawSurfaces[i].firstIndex; j < bspDrawSurfaces[i].firstIndex + bspDrawSurfaces[i].numIndexes; j += 3)
895                                         {
896                                                 int ia = bspDrawIndexes[j] + bspDrawSurfaces[i].firstVert, ib = bspDrawIndexes[j+1] + bspDrawSurfaces[i].firstVert, ic = bspDrawIndexes[j+2] + bspDrawSurfaces[i].firstVert;
897                                                 bspDrawVert_t *a = &bspDrawVerts[ia], *b = &bspDrawVerts[ib], *c = &bspDrawVerts[ic];
898                                                 float *oa = &old_xyzst[ia*5], *ob = &old_xyzst[ib*5], *oc = &old_xyzst[ic*5];
899                                                 // extrapolate:
900                                                 //   a->xyz -> oa
901                                                 //   b->xyz -> ob
902                                                 //   c->xyz -> oc
903                                                 ExtrapolateTexcoords(
904                                                         &oa[0], &oa[3],
905                                                         &ob[0], &ob[3],
906                                                         &oc[0], &oc[3],
907                                                         a->xyz, a->st,
908                                                         b->xyz, b->st,
909                                                         c->xyz, c->st);
910                                         }
911                                         break;
912                         }
913                 }
914         }
915         
916         /* scale planes */
917         if(uniform)
918         {
919                 for( i = 0; i < numBSPPlanes; i++ )
920                 {
921                         bspPlanes[ i ].dist *= scale[0];
922                 }
923         }
924         else
925         {
926                 for( i = 0; i < numBSPPlanes; i++ )
927                 {
928                         bspPlanes[ i ].normal[0] /= scale[0];
929                         bspPlanes[ i ].normal[1] /= scale[1];
930                         bspPlanes[ i ].normal[2] /= scale[2];
931                         f = 1/VectorLength(bspPlanes[i].normal);
932                         VectorScale(bspPlanes[i].normal, f, bspPlanes[i].normal);
933                         bspPlanes[ i ].dist *= f;
934                 }
935         }
936         
937         /* scale gridsize */
938         GetVectorForKey( &entities[ 0 ], "gridsize", vec );
939         if( (vec[ 0 ] + vec[ 1 ] + vec[ 2 ]) == 0.0f )
940                 VectorCopy( gridSize, vec );
941         vec[0] *= scale[0];
942         vec[1] *= scale[1];
943         vec[2] *= scale[2];
944         sprintf( str, "%f %f %f", vec[ 0 ], vec[ 1 ], vec[ 2 ] );
945         SetKeyValue( &entities[ 0 ], "gridsize", str );
946
947         /* inject command line parameters */
948         InjectCommandLine(argv, 0, argc - 1);
949         
950         /* write the bsp */
951         UnparseEntities();
952         StripExtension( source );
953         DefaultExtension( source, "_s.bsp" );
954         Sys_Printf( "Writing %s\n", source );
955         WriteBSPFile( source );
956         
957         /* return to sender */
958         return 0;
959 }
960
961
962
963 /*
964 ConvertBSPMain()
965 main argument processing function for bsp conversion
966 */
967
968 int ConvertBSPMain( int argc, char **argv )
969 {
970         int             i;
971         int             (*convertFunc)( char * );
972         game_t  *convertGame;
973         
974         
975         /* set default */
976         convertFunc = ConvertBSPToASE;
977         convertGame = NULL;
978         
979         /* arg checking */
980         if( argc < 1 )
981         {
982                 Sys_Printf( "Usage: q3map -scale <value> [-v] <mapname>\n" );
983                 return 0;
984         }
985         
986         /* process arguments */
987         for( i = 1; i < (argc - 1); i++ )
988         {
989                 /* -format map|ase|... */
990                 if( !strcmp( argv[ i ],  "-format" ) )
991                 {
992                         i++;
993                         if( !Q_stricmp( argv[ i ], "ase" ) )
994                                 convertFunc = ConvertBSPToASE;
995                         else if( !Q_stricmp( argv[ i ], "map" ) )
996                                 convertFunc = ConvertBSPToMap;
997                         else
998                         {
999                                 convertGame = GetGame( argv[ i ] );
1000                                 if( convertGame == NULL )
1001                                         Sys_Printf( "Unknown conversion format \"%s\". Defaulting to ASE.\n", argv[ i ] );
1002                         }
1003                 }
1004                 else if( !strcmp( argv[ i ],  "-ne" ) )
1005                 {
1006                         normalEpsilon = atof( argv[ i + 1 ] );
1007                         i++;
1008                         Sys_Printf( "Normal epsilon set to %f\n", normalEpsilon );
1009                 }
1010                 else if( !strcmp( argv[ i ],  "-de" ) )
1011                 {
1012                         distanceEpsilon = atof( argv[ i + 1 ] );
1013                         i++;
1014                         Sys_Printf( "Distance epsilon set to %f\n", distanceEpsilon );
1015                 }
1016                 else if( !strcmp( argv[ i ],  "-shadersasbitmap" ) )
1017                         shadersAsBitmap = qtrue;
1018         }
1019         
1020         /* clean up map name */
1021         strcpy( source, ExpandArg( argv[ i ] ) );
1022         StripExtension( source );
1023         DefaultExtension( source, ".bsp" );
1024         
1025         LoadShaderInfo();
1026         
1027         Sys_Printf( "Loading %s\n", source );
1028         
1029         /* ydnar: load surface file */
1030         //%     LoadSurfaceExtraFile( source );
1031         
1032         LoadBSPFile( source );
1033         
1034         /* parse bsp entities */
1035         ParseEntities();
1036         
1037         /* bsp format convert? */
1038         if( convertGame != NULL )
1039         {
1040                 /* set global game */
1041                 game = convertGame;
1042                 
1043                 /* write bsp */
1044                 StripExtension( source );
1045                 DefaultExtension( source, "_c.bsp" );
1046                 Sys_Printf( "Writing %s\n", source );
1047                 WriteBSPFile( source );
1048                 
1049                 /* return to sender */
1050                 return 0;
1051         }
1052         
1053         /* normal convert */
1054         return convertFunc( source );
1055 }
1056
1057
1058
1059 /*
1060 main()
1061 q3map mojo...
1062 */
1063
1064 int main( int argc, char **argv )
1065 {
1066         int             i, r;
1067         double  start, end;
1068         
1069         
1070         /* we want consistent 'randomness' */
1071         srand( 0 );
1072         
1073         /* start timer */
1074         start = I_FloatTime();
1075
1076         /* this was changed to emit version number over the network */
1077         printf( Q3MAP_VERSION "\n" );
1078         
1079         /* set exit call */
1080         atexit( ExitQ3Map );
1081
1082         /* read general options first */
1083         for( i = 1; i < argc; i++ )
1084         {
1085                 /* -connect */
1086                 if( !strcmp( argv[ i ], "-connect" ) )
1087                 {
1088                         argv[ i ] = NULL;
1089                         i++;
1090                         Broadcast_Setup( argv[ i ] );
1091                         argv[ i ] = NULL;
1092                 }
1093                 
1094                 /* verbose */
1095                 else if( !strcmp( argv[ i ], "-v" ) )
1096                 {
1097                         if(!verbose)
1098                         {
1099                                 verbose = qtrue;
1100                                 argv[ i ] = NULL;
1101                         }
1102                 }
1103                 
1104                 /* force */
1105                 else if( !strcmp( argv[ i ], "-force" ) )
1106                 {
1107                         force = qtrue;
1108                         argv[ i ] = NULL;
1109                 }
1110                 
1111                 /* patch subdivisions */
1112                 else if( !strcmp( argv[ i ], "-subdivisions" ) )
1113                 {
1114                         argv[ i ] = NULL;
1115                         i++;
1116                         patchSubdivisions = atoi( argv[ i ] );
1117                         argv[ i ] = NULL;
1118                         if( patchSubdivisions <= 0 )
1119                                 patchSubdivisions = 1;
1120                 }
1121                 
1122                 /* threads */
1123                 else if( !strcmp( argv[ i ], "-threads" ) )
1124                 {
1125                         argv[ i ] = NULL;
1126                         i++;
1127                         numthreads = atoi( argv[ i ] );
1128                         argv[ i ] = NULL;
1129                 }
1130         }
1131         
1132         /* init model library */
1133         PicoInit();
1134         PicoSetMallocFunc( safe_malloc );
1135         PicoSetFreeFunc( free );
1136         PicoSetPrintFunc( PicoPrintFunc );
1137         PicoSetLoadFileFunc( PicoLoadFileFunc );
1138         PicoSetFreeFileFunc( free );
1139         
1140         /* set number of threads */
1141         ThreadSetDefault();
1142         
1143         /* generate sinusoid jitter table */
1144         for( i = 0; i < MAX_JITTERS; i++ )
1145         {
1146                 jitters[ i ] = sin( i * 139.54152147 );
1147                 //%     Sys_Printf( "Jitter %4d: %f\n", i, jitters[ i ] );
1148         }
1149         
1150         /* we print out two versions, q3map's main version (since it evolves a bit out of GtkRadiant)
1151            and we put the GtkRadiant version to make it easy to track with what version of Radiant it was built with */
1152         
1153         Sys_Printf( "Q3Map         - v1.0r (c) 1999 Id Software Inc.\n" );
1154         Sys_Printf( "Q3Map (ydnar) - v" Q3MAP_VERSION "\n" );
1155         Sys_Printf( "NetRadiant    - v" RADIANT_VERSION " " __DATE__ " " __TIME__ "\n" );
1156         Sys_Printf( "%s\n", Q3MAP_MOTD );
1157         
1158         /* ydnar: new path initialization */
1159         InitPaths( &argc, argv );
1160
1161         /* set game options */
1162         if (!patchSubdivisions)
1163                 patchSubdivisions = game->patchSubdivisions;
1164         
1165         /* check if we have enough options left to attempt something */
1166         if( argc < 2 )
1167                 Error( "Usage: %s [general options] [options] mapfile", argv[ 0 ] );
1168         
1169         /* fixaas */
1170         if( !strcmp( argv[ 1 ], "-fixaas" ) )
1171                 r = FixAAS( argc - 1, argv + 1 );
1172         
1173         /* analyze */
1174         else if( !strcmp( argv[ 1 ], "-analyze" ) )
1175                 r = AnalyzeBSP( argc - 1, argv + 1 );
1176         
1177         /* info */
1178         else if( !strcmp( argv[ 1 ], "-info" ) )
1179                 r = BSPInfo( argc - 2, argv + 2 );
1180         
1181         /* vis */
1182         else if( !strcmp( argv[ 1 ], "-vis" ) )
1183                 r = VisMain( argc - 1, argv + 1 );
1184         
1185         /* light */
1186         else if( !strcmp( argv[ 1 ], "-light" ) )
1187                 r = LightMain( argc - 1, argv + 1 );
1188         
1189         /* vlight */
1190         else if( !strcmp( argv[ 1 ], "-vlight" ) )
1191         {
1192                 Sys_Printf( "WARNING: VLight is no longer supported, defaulting to -light -fast instead\n\n" );
1193                 argv[ 1 ] = "-fast";    /* eek a hack */
1194                 r = LightMain( argc, argv );
1195         }
1196         
1197         /* ydnar: lightmap export */
1198         else if( !strcmp( argv[ 1 ], "-export" ) )
1199                 r = ExportLightmapsMain( argc - 1, argv + 1 );
1200         
1201         /* ydnar: lightmap import */
1202         else if( !strcmp( argv[ 1 ], "-import" ) )
1203                 r = ImportLightmapsMain( argc - 1, argv + 1 );
1204         
1205         /* ydnar: bsp scaling */
1206         else if( !strcmp( argv[ 1 ], "-scale" ) )
1207                 r = ScaleBSPMain( argc - 1, argv + 1 );
1208         
1209         /* ydnar: bsp conversion */
1210         else if( !strcmp( argv[ 1 ], "-convert" ) )
1211                 r = ConvertBSPMain( argc - 1, argv + 1 );
1212         
1213         /* div0: minimap */
1214         else if( !strcmp( argv[ 1 ], "-minimap" ) )
1215                 r = MiniMapBSPMain(argc - 1, argv + 1);
1216
1217         /* ydnar: otherwise create a bsp */
1218         else
1219                 r = BSPMain( argc, argv );
1220         
1221         /* emit time */
1222         end = I_FloatTime();
1223         Sys_Printf( "%9.0f seconds elapsed\n", end - start );
1224         
1225         /* shut down connection */
1226         Broadcast_Shutdown();
1227         
1228         /* return any error code */
1229         return r;
1230 }