]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake2/q2map/map.c
q3map2: -fs_nobasepath implies -fs_nomagicpath
[xonotic/netradiant.git] / tools / quake2 / q2map / map.c
1 /*
2    Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3    For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21 // map.c
22
23 #include "qbsp.h"
24
25 extern qboolean onlyents;
26
27 int nummapbrushes;
28 mapbrush_t mapbrushes[MAX_MAP_BRUSHES];
29
30 int nummapbrushsides;
31 side_t brushsides[MAX_MAP_SIDES];
32 brush_texture_t side_brushtextures[MAX_MAP_SIDES];
33
34 int nummapplanes;
35 plane_t mapplanes[MAX_MAP_PLANES];
36
37 #define PLANE_HASHES    1024
38 plane_t     *planehash[PLANE_HASHES];
39
40 vec3_t map_mins, map_maxs;
41
42 // undefine to make plane finding use linear sort
43 #define USE_HASHING
44
45 void TestExpandBrushes( void );
46
47 int c_boxbevels;
48 int c_edgebevels;
49
50 int c_areaportals;
51
52 int c_clipbrushes;
53
54 /*
55    =============================================================================
56
57    PLANE FINDING
58
59    =============================================================================
60  */
61
62
63 /*
64    =================
65    PlaneTypeForNormal
66    =================
67  */
68 int PlaneTypeForNormal( vec3_t normal ){
69         vec_t ax, ay, az;
70
71 // NOTE: should these have an epsilon around 1.0?
72         if ( normal[0] == 1.0 || normal[0] == -1.0 ) {
73                 return PLANE_X;
74         }
75         if ( normal[1] == 1.0 || normal[1] == -1.0 ) {
76                 return PLANE_Y;
77         }
78         if ( normal[2] == 1.0 || normal[2] == -1.0 ) {
79                 return PLANE_Z;
80         }
81
82         ax = fabs( normal[0] );
83         ay = fabs( normal[1] );
84         az = fabs( normal[2] );
85
86         if ( ax >= ay && ax >= az ) {
87                 return PLANE_ANYX;
88         }
89         if ( ay >= ax && ay >= az ) {
90                 return PLANE_ANYY;
91         }
92         return PLANE_ANYZ;
93 }
94
95 /*
96    ================
97    PlaneEqual
98    ================
99  */
100 #define NORMAL_EPSILON  0.00001
101 #define DIST_EPSILON    0.01
102 qboolean    PlaneEqual( plane_t *p, vec3_t normal, vec_t dist ){
103 #if 1
104         if (
105                 fabs( p->normal[0] - normal[0] ) < NORMAL_EPSILON
106                 && fabs( p->normal[1] - normal[1] ) < NORMAL_EPSILON
107                 && fabs( p->normal[2] - normal[2] ) < NORMAL_EPSILON
108                 && fabs( p->dist - dist ) < DIST_EPSILON ) {
109                 return true;
110         }
111 #else
112         if ( p->normal[0] == normal[0]
113                  && p->normal[1] == normal[1]
114                  && p->normal[2] == normal[2]
115                  && p->dist == dist ) {
116                 return true;
117         }
118 #endif
119         return false;
120 }
121
122 /*
123    ================
124    AddPlaneToHash
125    ================
126  */
127 void    AddPlaneToHash( plane_t *p ){
128         int hash;
129
130         hash = (int)fabs( p->dist ) / 8;
131         hash &= ( PLANE_HASHES - 1 );
132
133         p->hash_chain = planehash[hash];
134         planehash[hash] = p;
135 }
136
137 /*
138    ================
139    CreateNewFloatPlane
140    ================
141  */
142 int CreateNewFloatPlane( vec3_t normal, vec_t dist ){
143         plane_t *p, temp;
144
145         if ( VectorLength( normal ) < 0.5 ) {
146                 Error( "FloatPlane: bad normal" );
147         }
148         // create a new plane
149         if ( nummapplanes + 2 > MAX_MAP_PLANES ) {
150                 Error( "MAX_MAP_PLANES" );
151         }
152
153         p = &mapplanes[nummapplanes];
154         VectorCopy( normal, p->normal );
155         p->dist = dist;
156         p->type = ( p + 1 )->type = PlaneTypeForNormal( p->normal );
157
158         VectorSubtract( vec3_origin, normal, ( p + 1 )->normal );
159         ( p + 1 )->dist = -dist;
160
161         nummapplanes += 2;
162
163         // allways put axial planes facing positive first
164         if ( p->type < 3 ) {
165                 if ( p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0 ) {
166                         // flip order
167                         temp = *p;
168                         *p = *( p + 1 );
169                         *( p + 1 ) = temp;
170
171                         AddPlaneToHash( p );
172                         AddPlaneToHash( p + 1 );
173                         return nummapplanes - 1;
174                 }
175         }
176
177         AddPlaneToHash( p );
178         AddPlaneToHash( p + 1 );
179         return nummapplanes - 2;
180 }
181
182 /*
183    ==============
184    SnapVector
185    ==============
186  */
187 void    SnapVector( vec3_t normal ){
188         int i;
189
190         for ( i = 0 ; i < 3 ; i++ )
191         {
192                 if ( fabs( normal[i] - 1 ) < NORMAL_EPSILON ) {
193                         VectorClear( normal );
194                         normal[i] = 1;
195                         break;
196                 }
197                 if ( fabs( normal[i] - -1 ) < NORMAL_EPSILON ) {
198                         VectorClear( normal );
199                         normal[i] = -1;
200                         break;
201                 }
202         }
203 }
204
205 /*
206    ==============
207    SnapPlane
208    ==============
209  */
210 void    SnapPlane( vec3_t normal, vec_t *dist ){
211         SnapVector( normal );
212
213         if ( fabs( *dist - Q_rint( *dist ) ) < DIST_EPSILON ) {
214                 *dist = Q_rint( *dist );
215         }
216 }
217
218 /*
219    =============
220    FindFloatPlane
221
222    =============
223  */
224 #ifndef USE_HASHING
225 int     FindFloatPlane( vec3_t normal, vec_t dist ){
226         int i;
227         plane_t *p;
228
229         SnapPlane( normal, &dist );
230         for ( i = 0, p = mapplanes ; i < nummapplanes ; i++, p++ )
231         {
232                 if ( PlaneEqual( p, normal, dist ) ) {
233                         return i;
234                 }
235         }
236
237         return CreateNewFloatPlane( normal, dist );
238 }
239 #else
240 int     FindFloatPlane( vec3_t normal, vec_t dist ){
241         int i;
242         plane_t *p;
243         int hash, h;
244
245         SnapPlane( normal, &dist );
246         hash = (int)fabs( dist ) / 8;
247         hash &= ( PLANE_HASHES - 1 );
248
249         // search the border bins as well
250         for ( i = -1 ; i <= 1 ; i++ )
251         {
252                 h = ( hash + i ) & ( PLANE_HASHES - 1 );
253                 for ( p = planehash[h] ; p ; p = p->hash_chain )
254                 {
255                         if ( PlaneEqual( p, normal, dist ) ) {
256                                 return p - mapplanes;
257                         }
258                 }
259         }
260
261         return CreateNewFloatPlane( normal, dist );
262 }
263 #endif
264
265 /*
266    ================
267    PlaneFromPoints
268    ================
269  */
270 int PlaneFromPoints( int *p0, int *p1, int *p2 ){
271         vec3_t t1, t2, normal;
272         vec_t dist;
273
274         VectorSubtract( p0, p1, t1 );
275         VectorSubtract( p2, p1, t2 );
276         CrossProduct( t1, t2, normal );
277         VectorNormalize( normal, normal );
278
279         dist = DotProduct( p0, normal );
280
281         return FindFloatPlane( normal, dist );
282 }
283
284
285 //====================================================================
286
287
288 /*
289    ===========
290    BrushContents
291    ===========
292  */
293 int BrushContents( mapbrush_t *b ){
294         int contents;
295         side_t      *s;
296         int i;
297         int trans;
298
299         s = &b->original_sides[0];
300         contents = s->contents;
301         trans = texinfo[s->texinfo].flags;
302         for ( i = 1 ; i < b->numsides ; i++, s++ )
303         {
304                 s = &b->original_sides[i];
305                 trans |= texinfo[s->texinfo].flags;
306                 if ( s->contents != contents ) {
307                         Sys_Printf( "Entity %i, Brush %i: mixed face contents\n"
308                                                 , b->entitynum, b->brushnum );
309                         break;
310                 }
311         }
312
313         // if any side is translucent, mark the contents
314         // and change solid to window
315         if ( trans & ( SURF_TRANS33 | SURF_TRANS66 ) ) {
316                 contents |= CONTENTS_TRANSLUCENT;
317                 if ( contents & CONTENTS_SOLID ) {
318                         contents &= ~CONTENTS_SOLID;
319                         contents |= CONTENTS_WINDOW;
320                 }
321         }
322
323         return contents;
324 }
325
326
327 //============================================================================
328
329 /*
330    =================
331    AddBrushBevels
332
333    Adds any additional planes necessary to allow the brush to be expanded
334    against axial bounding boxes
335    =================
336  */
337 void AddBrushBevels( mapbrush_t *b ){
338         int axis, dir;
339         int i, j, k, l, order;
340         side_t sidetemp;
341         brush_texture_t tdtemp;
342         side_t  *s, *s2;
343         vec3_t normal;
344         float dist;
345         winding_t   *w, *w2;
346         vec3_t vec, vec2;
347         float d;
348
349         //
350         // add the axial planes
351         //
352         order = 0;
353         for ( axis = 0 ; axis < 3 ; axis++ )
354         {
355                 for ( dir = -1 ; dir <= 1 ; dir += 2, order++ )
356                 {
357                         // see if the plane is allready present
358                         for ( i = 0, s = b->original_sides ; i < b->numsides ; i++,s++ )
359                         {
360                                 if ( mapplanes[s->planenum].normal[axis] == dir ) {
361                                         break;
362                                 }
363                         }
364
365                         if ( i == b->numsides ) { // add a new side
366                                 if ( nummapbrushsides == MAX_MAP_BRUSHSIDES ) {
367                                         Error( "MAX_MAP_BRUSHSIDES" );
368                                 }
369                                 nummapbrushsides++;
370                                 b->numsides++;
371                                 VectorClear( normal );
372                                 normal[axis] = dir;
373                                 if ( dir == 1 ) {
374                                         dist = b->maxs[axis];
375                                 }
376                                 else{
377                                         dist = -b->mins[axis];
378                                 }
379                                 s->planenum = FindFloatPlane( normal, dist );
380                                 s->texinfo = b->original_sides[0].texinfo;
381                                 s->contents = b->original_sides[0].contents;
382                                 s->bevel = true;
383                                 c_boxbevels++;
384                         }
385
386                         // if the plane is not in it canonical order, swap it
387                         if ( i != order ) {
388                                 sidetemp = b->original_sides[order];
389                                 b->original_sides[order] = b->original_sides[i];
390                                 b->original_sides[i] = sidetemp;
391
392                                 j = b->original_sides - brushsides;
393                                 tdtemp = side_brushtextures[j + order];
394                                 side_brushtextures[j + order] = side_brushtextures[j + i];
395                                 side_brushtextures[j + i] = tdtemp;
396                         }
397                 }
398         }
399
400         //
401         // add the edge bevels
402         //
403         if ( b->numsides == 6 ) {
404                 return;     // pure axial
405
406         }
407         // test the non-axial plane edges
408         for ( i = 6 ; i < b->numsides ; i++ )
409         {
410                 s = b->original_sides + i;
411                 w = s->winding;
412                 if ( !w ) {
413                         continue;
414                 }
415                 for ( j = 0 ; j < w->numpoints ; j++ )
416                 {
417                         k = ( j + 1 ) % w->numpoints;
418                         VectorSubtract( w->p[j], w->p[k], vec );
419                         if ( VectorNormalize( vec, vec ) < 0.5 ) {
420                                 continue;
421                         }
422                         SnapVector( vec );
423                         for ( k = 0 ; k < 3 ; k++ )
424                                 if ( vec[k] == -1 || vec[k] == 1 ) {
425                                         break;
426                                 }           // axial
427                         if ( k != 3 ) {
428                                 continue;   // only test non-axial edges
429
430                         }
431                         // try the six possible slanted axials from this edge
432                         for ( axis = 0 ; axis < 3 ; axis++ )
433                         {
434                                 for ( dir = -1 ; dir <= 1 ; dir += 2 )
435                                 {
436                                         // construct a plane
437                                         VectorClear( vec2 );
438                                         vec2[axis] = dir;
439                                         CrossProduct( vec, vec2, normal );
440                                         if ( VectorNormalize( normal, normal ) < 0.5 ) {
441                                                 continue;
442                                         }
443                                         dist = DotProduct( w->p[j], normal );
444
445                                         // if all the points on all the sides are
446                                         // behind this plane, it is a proper edge bevel
447                                         for ( k = 0 ; k < b->numsides ; k++ )
448                                         {
449                                                 // if this plane has allready been used, skip it
450                                                 if ( PlaneEqual( &mapplanes[b->original_sides[k].planenum]
451                                                                                  , normal, dist ) ) {
452                                                         break;
453                                                 }
454
455                                                 w2 = b->original_sides[k].winding;
456                                                 if ( !w2 ) {
457                                                         continue;
458                                                 }
459                                                 for ( l = 0 ; l < w2->numpoints ; l++ )
460                                                 {
461                                                         d = DotProduct( w2->p[l], normal ) - dist;
462                                                         if ( d > 0.1 ) {
463                                                                 break;  // point in front
464                                                         }
465                                                 }
466                                                 if ( l != w2->numpoints ) {
467                                                         break;
468                                                 }
469                                         }
470
471                                         if ( k != b->numsides ) {
472                                                 continue;   // wasn't part of the outer hull
473                                         }
474                                         // add this plane
475                                         if ( nummapbrushsides == MAX_MAP_BRUSHSIDES ) {
476                                                 Error( "MAX_MAP_BRUSHSIDES" );
477                                         }
478                                         nummapbrushsides++;
479                                         s2 = &b->original_sides[b->numsides];
480                                         s2->planenum = FindFloatPlane( normal, dist );
481                                         s2->texinfo = b->original_sides[0].texinfo;
482                                         s2->contents = b->original_sides[0].contents;
483                                         s2->bevel = true;
484                                         c_edgebevels++;
485                                         b->numsides++;
486                                 }
487                         }
488                 }
489         }
490 }
491
492
493 /*
494    ================
495    MakeBrushWindings
496
497    makes basewindigs for sides and mins / maxs for the brush
498    ================
499  */
500 qboolean MakeBrushWindings( mapbrush_t *ob ){
501         int i, j;
502         winding_t   *w;
503         side_t      *side;
504         plane_t     *plane;
505
506         ClearBounds( ob->mins, ob->maxs );
507
508         for ( i = 0 ; i < ob->numsides ; i++ )
509         {
510                 plane = &mapplanes[ob->original_sides[i].planenum];
511                 w = BaseWindingForPlane( plane->normal, plane->dist );
512                 for ( j = 0 ; j < ob->numsides && w; j++ )
513                 {
514                         if ( i == j ) {
515                                 continue;
516                         }
517                         if ( ob->original_sides[j].bevel ) {
518                                 continue;
519                         }
520                         plane = &mapplanes[ob->original_sides[j].planenum ^ 1];
521                         ChopWindingInPlace( &w, plane->normal, plane->dist, 0 ); //CLIP_EPSILON);
522                 }
523
524                 side = &ob->original_sides[i];
525                 side->winding = w;
526                 if ( w ) {
527                         side->visible = true;
528                         for ( j = 0 ; j < w->numpoints ; j++ )
529                                 AddPointToBounds( w->p[j], ob->mins, ob->maxs );
530                 }
531         }
532
533         for ( i = 0 ; i < 3 ; i++ )
534         {
535                 if ( ob->mins[0] < -4096 || ob->maxs[0] > 4096 ) {
536                         Sys_Printf( "entity %i, brush %i: bounds out of range\n", ob->entitynum, ob->brushnum );
537                 }
538                 if ( ob->mins[0] > 4096 || ob->maxs[0] < -4096 ) {
539                         Sys_Printf( "entity %i, brush %i: no visible sides on brush\n", ob->entitynum, ob->brushnum );
540                 }
541         }
542
543         return true;
544 }
545
546
547 /*
548    =================
549    ParseBrush
550    =================
551  */
552 void ParseBrush( entity_t *mapent ){
553         mapbrush_t      *b;
554         int i,j, k;
555         int mt;
556         side_t      *side, *s2;
557         int planenum;
558         brush_texture_t td;
559         int planepts[3][3];
560
561         if ( nummapbrushes == MAX_MAP_BRUSHES ) {
562                 Error( "nummapbrushes == MAX_MAP_BRUSHES" );
563         }
564
565         b = &mapbrushes[nummapbrushes];
566         b->original_sides = &brushsides[nummapbrushsides];
567         b->entitynum = num_entities - 1;
568         b->brushnum = nummapbrushes - mapent->firstbrush;
569
570         do
571         {
572                 if ( !GetToken( true ) ) {
573                         break;
574                 }
575                 if ( !strcmp( token, "}" ) ) {
576                         break;
577                 }
578
579                 if ( nummapbrushsides == MAX_MAP_BRUSHSIDES ) {
580                         Error( "MAX_MAP_BRUSHSIDES" );
581                 }
582                 side = &brushsides[nummapbrushsides];
583
584                 // read the three point plane definition
585                 for ( i = 0 ; i < 3 ; i++ )
586                 {
587                         if ( i != 0 ) {
588                                 GetToken( true );
589                         }
590                         if ( strcmp( token, "(" ) ) {
591                                 Error( "parsing brush" );
592                         }
593
594                         for ( j = 0 ; j < 3 ; j++ )
595                         {
596                                 GetToken( false );
597                                 planepts[i][j] = atoi( token );
598                         }
599
600                         GetToken( false );
601                         if ( strcmp( token, ")" ) ) {
602                                 Error( "parsing brush" );
603                         }
604
605                 }
606
607
608                 //
609                 // read the texturedef
610                 //
611                 GetToken( false );
612                 strcpy( td.name, token );
613
614                 GetToken( false );
615                 td.shift[0] = atoi( token );
616                 GetToken( false );
617                 td.shift[1] = atoi( token );
618                 GetToken( false );
619                 td.rotate = atoi( token );
620                 GetToken( false );
621                 td.scale[0] = atof( token );
622                 GetToken( false );
623                 td.scale[1] = atof( token );
624
625                 // find default flags and values
626                 mt = FindMiptex( td.name );
627                 td.flags = textureref[mt].flags;
628                 td.value = textureref[mt].value;
629                 side->contents = textureref[mt].contents;
630                 side->surf = td.flags = textureref[mt].flags;
631
632                 if ( TokenAvailable() ) {
633                         GetToken( false );
634                         side->contents = atoi( token );
635                         GetToken( false );
636                         side->surf = td.flags = atoi( token );
637                         GetToken( false );
638                         td.value = atoi( token );
639                 }
640
641                 // translucent objects are automatically classified as detail
642                 if ( side->surf & ( SURF_TRANS33 | SURF_TRANS66 ) ) {
643                         side->contents |= CONTENTS_DETAIL;
644                 }
645                 if ( side->contents & ( CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP ) ) {
646                         side->contents |= CONTENTS_DETAIL;
647                 }
648                 if ( fulldetail ) {
649                         side->contents &= ~CONTENTS_DETAIL;
650                 }
651                 if ( !( side->contents & ( ( LAST_VISIBLE_CONTENTS - 1 )
652                                                                    | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP | CONTENTS_MIST )  ) ) {
653                         side->contents |= CONTENTS_SOLID;
654                 }
655
656                 // hints and skips are never detail, and have no content
657                 if ( side->surf & ( SURF_HINT | SURF_SKIP ) ) {
658                         side->contents = 0;
659                         side->surf &= ~CONTENTS_DETAIL;
660                 }
661
662
663                 //
664                 // find the plane number
665                 //
666                 planenum = PlaneFromPoints( planepts[0], planepts[1], planepts[2] );
667                 if ( planenum == -1 ) {
668                         Sys_Printf( "Entity %i, Brush %i: plane with no normal\n"
669                                                 , b->entitynum, b->brushnum );
670                         continue;
671                 }
672
673                 //
674                 // see if the plane has been used already
675                 //
676                 for ( k = 0 ; k < b->numsides ; k++ )
677                 {
678                         s2 = b->original_sides + k;
679                         if ( s2->planenum == planenum ) {
680                                 Sys_Printf( "Entity %i, Brush %i: duplicate plane\n"
681                                                         , b->entitynum, b->brushnum );
682                                 break;
683                         }
684                         if ( s2->planenum == ( planenum ^ 1 ) ) {
685                                 Sys_Printf( "Entity %i, Brush %i: mirrored plane\n"
686                                                         , b->entitynum, b->brushnum );
687                                 break;
688                         }
689                 }
690                 if ( k != b->numsides ) {
691                         continue;       // duplicated
692
693                 }
694                 //
695                 // keep this side
696                 //
697
698                 side = b->original_sides + b->numsides;
699                 side->planenum = planenum;
700                 side->texinfo = TexinfoForBrushTexture( &mapplanes[planenum],
701                                                                                                 &td, vec3_origin );
702
703                 // save the td off in case there is an origin brush and we
704                 // have to recalculate the texinfo
705                 side_brushtextures[nummapbrushsides] = td;
706
707                 nummapbrushsides++;
708                 b->numsides++;
709         } while ( 1 );
710
711         // get the content for the entire brush
712         b->contents = BrushContents( b );
713
714         // allow detail brushes to be removed
715         if ( nodetail && ( b->contents & CONTENTS_DETAIL ) ) {
716                 b->numsides = 0;
717                 return;
718         }
719
720         // allow water brushes to be removed
721         if ( nowater && ( b->contents & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) ) {
722                 b->numsides = 0;
723                 return;
724         }
725
726         // create windings for sides and bounds for brush
727         MakeBrushWindings( b );
728
729         // brushes that will not be visible at all will never be
730         // used as bsp splitters
731         if ( b->contents & ( CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP ) ) {
732                 c_clipbrushes++;
733                 for ( i = 0 ; i < b->numsides ; i++ )
734                         b->original_sides[i].texinfo = TEXINFO_NODE;
735         }
736
737         //
738         // origin brushes are removed, but they set
739         // the rotation origin for the rest of the brushes
740         // in the entity.  After the entire entity is parsed,
741         // the planenums and texinfos will be adjusted for
742         // the origin brush
743         //
744         if ( b->contents & CONTENTS_ORIGIN ) {
745                 char string[32];
746                 vec3_t origin;
747
748                 if ( num_entities == 1 ) {
749                         Error( "Entity %i, Brush %i: origin brushes not allowed in world"
750                                    , b->entitynum, b->brushnum );
751                         return;
752                 }
753
754                 VectorAdd( b->mins, b->maxs, origin );
755                 VectorScale( origin, 0.5, origin );
756
757                 sprintf( string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2] );
758                 SetKeyValue( &entities[b->entitynum], "origin", string );
759
760                 VectorCopy( origin, entities[b->entitynum].origin );
761
762                 // don't keep this brush
763                 b->numsides = 0;
764
765                 return;
766         }
767
768         AddBrushBevels( b );
769
770         nummapbrushes++;
771         mapent->numbrushes++;
772 }
773
774 /*
775    ================
776    MoveBrushesToWorld
777
778    Takes all of the brushes from the current entity and
779    adds them to the world's brush list.
780
781    Used by func_group and func_areaportal
782    ================
783  */
784 void MoveBrushesToWorld( entity_t *mapent ){
785         int newbrushes;
786         int worldbrushes;
787         mapbrush_t  *temp;
788         int i;
789
790         // this is pretty gross, because the brushes are expected to be
791         // in linear order for each entity
792
793         newbrushes = mapent->numbrushes;
794         worldbrushes = entities[0].numbrushes;
795
796         temp = malloc( newbrushes * sizeof( mapbrush_t ) );
797         memcpy( temp, mapbrushes + mapent->firstbrush, newbrushes * sizeof( mapbrush_t ) );
798
799 #if 0       // let them keep their original brush numbers
800         for ( i = 0 ; i < newbrushes ; i++ )
801                 temp[i].entitynum = 0;
802 #endif
803
804         // make space to move the brushes (overlapped copy)
805         memmove( mapbrushes + worldbrushes + newbrushes,
806                          mapbrushes + worldbrushes,
807                          sizeof( mapbrush_t ) * ( nummapbrushes - worldbrushes - newbrushes ) );
808
809         // copy the new brushes down
810         memcpy( mapbrushes + worldbrushes, temp, sizeof( mapbrush_t ) * newbrushes );
811
812         // fix up indexes
813         entities[0].numbrushes += newbrushes;
814         for ( i = 1 ; i < num_entities ; i++ )
815                 entities[i].firstbrush += newbrushes;
816         free( temp );
817
818         mapent->numbrushes = 0;
819 }
820
821 /*
822    ================
823    ParseMapEntity
824    ================
825  */
826 qboolean    ParseMapEntity( void ){
827         entity_t    *mapent;
828         epair_t     *e;
829         side_t      *s;
830         int i, j;
831         int startbrush, startsides;
832         vec_t newdist;
833         mapbrush_t  *b;
834
835         if ( !GetToken( true ) ) {
836                 return false;
837         }
838
839         if ( strcmp( token, "{" ) ) {
840                 Error( "ParseEntity: { not found" );
841         }
842
843         if ( num_entities == MAX_MAP_ENTITIES ) {
844                 Error( "num_entities == MAX_MAP_ENTITIES" );
845         }
846
847         startbrush = nummapbrushes;
848         startsides = nummapbrushsides;
849
850         mapent = &entities[num_entities];
851         num_entities++;
852         memset( mapent, 0, sizeof( *mapent ) );
853         mapent->firstbrush = nummapbrushes;
854         mapent->numbrushes = 0;
855 //      mapent->portalareas[0] = -1;
856 //      mapent->portalareas[1] = -1;
857
858         do
859         {
860                 if ( !GetToken( true ) ) {
861                         Error( "ParseEntity: EOF without closing brace" );
862                 }
863                 if ( !strcmp( token, "}" ) ) {
864                         break;
865                 }
866                 if ( !strcmp( token, "{" ) ) {
867                         ParseBrush( mapent );
868                 }
869                 else
870                 {
871                         e = ParseEpair();
872                         e->next = mapent->epairs;
873                         mapent->epairs = e;
874                 }
875         } while ( 1 );
876
877         GetVectorForKey( mapent, "origin", mapent->origin );
878
879         //
880         // if there was an origin brush, offset all of the planes and texinfo
881         //
882         if ( mapent->origin[0] || mapent->origin[1] || mapent->origin[2] ) {
883                 for ( i = 0 ; i < mapent->numbrushes ; i++ )
884                 {
885                         b = &mapbrushes[mapent->firstbrush + i];
886                         for ( j = 0 ; j < b->numsides ; j++ )
887                         {
888                                 s = &b->original_sides[j];
889                                 newdist = mapplanes[s->planenum].dist -
890                                                   DotProduct( mapplanes[s->planenum].normal, mapent->origin );
891                                 s->planenum = FindFloatPlane( mapplanes[s->planenum].normal, newdist );
892                                 s->texinfo = TexinfoForBrushTexture( &mapplanes[s->planenum],
893                                                                                                          &side_brushtextures[s - brushsides], mapent->origin );
894                         }
895                         MakeBrushWindings( b );
896                 }
897         }
898
899         // group entities are just for editor convenience
900         // toss all brushes into the world entity
901         if ( !strcmp( "func_group", ValueForKey( mapent, "classname" ) ) ) {
902                 MoveBrushesToWorld( mapent );
903                 mapent->numbrushes = 0;
904                 return true;
905         }
906
907         // areaportal entities move their brushes, but don't eliminate
908         // the entity
909         if ( !strcmp( "func_areaportal", ValueForKey( mapent, "classname" ) ) ) {
910                 char str[128];
911
912                 if ( mapent->numbrushes != 1 ) {
913                         Error( "Entity %i: func_areaportal can only be a single brush", num_entities - 1 );
914                 }
915
916                 b = &mapbrushes[nummapbrushes - 1];
917                 b->contents = CONTENTS_AREAPORTAL;
918                 c_areaportals++;
919                 mapent->areaportalnum = c_areaportals;
920                 // set the portal number as "style"
921                 sprintf( str, "%i", c_areaportals );
922                 SetKeyValue( mapent, "style", str );
923                 MoveBrushesToWorld( mapent );
924                 return true;
925         }
926
927         return true;
928 }
929
930 //===================================================================
931
932 /*
933    ================
934    LoadMapFile
935    ================
936  */
937 void LoadMapFile( char *filename ){
938         int i;
939
940         Sys_FPrintf( SYS_VRB, "--- LoadMapFile ---\n" );
941
942         LoadScriptFile( filename );
943
944         nummapbrushsides = 0;
945         num_entities = 0;
946
947         while ( ParseMapEntity() )
948         {
949         }
950
951         ClearBounds( map_mins, map_maxs );
952         for ( i = 0 ; i < entities[0].numbrushes ; i++ )
953         {
954                 if ( mapbrushes[i].mins[0] > 4096 ) {
955                         continue;   // no valid points
956                 }
957                 AddPointToBounds( mapbrushes[i].mins, map_mins, map_maxs );
958                 AddPointToBounds( mapbrushes[i].maxs, map_mins, map_maxs );
959         }
960
961         Sys_FPrintf( SYS_VRB, "%5i brushes\n", nummapbrushes );
962         Sys_FPrintf( SYS_VRB, "%5i clipbrushes\n", c_clipbrushes );
963         Sys_FPrintf( SYS_VRB, "%5i total sides\n", nummapbrushsides );
964         Sys_FPrintf( SYS_VRB, "%5i boxbevels\n", c_boxbevels );
965         Sys_FPrintf( SYS_VRB, "%5i edgebevels\n", c_edgebevels );
966         Sys_FPrintf( SYS_VRB, "%5i entities\n", num_entities );
967         Sys_FPrintf( SYS_VRB, "%5i planes\n", nummapplanes );
968         Sys_FPrintf( SYS_VRB, "%5i areaportals\n", c_areaportals );
969         Sys_FPrintf( SYS_VRB, "size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", map_mins[0],map_mins[1],map_mins[2],
970                                  map_maxs[0],map_maxs[1],map_maxs[2] );
971
972 //      TestExpandBrushes ();
973 }
974
975
976 //====================================================================
977
978
979 /*
980    ================
981    TestExpandBrushes
982
983    Expands all the brush planes and saves a new map out
984    ================
985  */
986 void TestExpandBrushes( void ){
987         FILE    *f;
988         side_t  *s;
989         int i, j, bn;
990         winding_t   *w;
991         char    *name = "expanded.map";
992         mapbrush_t  *brush;
993         vec_t dist;
994
995         Sys_Printf( "writing %s\n", name );
996         f = fopen( name, "wb" );
997         if ( !f ) {
998                 Error( "Can't write %s\b", name );
999         }
1000
1001         fprintf( f, "{\n\"classname\" \"worldspawn\"\n" );
1002
1003         for ( bn = 0 ; bn < nummapbrushes ; bn++ )
1004         {
1005                 brush = &mapbrushes[bn];
1006                 fprintf( f, "{\n" );
1007                 for ( i = 0 ; i < brush->numsides ; i++ )
1008                 {
1009                         s = brush->original_sides + i;
1010                         dist = mapplanes[s->planenum].dist;
1011                         for ( j = 0 ; j < 3 ; j++ )
1012                                 dist += fabs( 16 * mapplanes[s->planenum].normal[j] );
1013
1014                         w = BaseWindingForPlane( mapplanes[s->planenum].normal, dist );
1015
1016                         fprintf( f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2] );
1017                         fprintf( f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2] );
1018                         fprintf( f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2] );
1019
1020                         fprintf( f, "%s 0 0 0 1 1\n", texinfo[s->texinfo].texture );
1021                         FreeWinding( w );
1022                 }
1023                 fprintf( f, "}\n" );
1024         }
1025         fprintf( f, "}\n" );
1026
1027         fclose( f );
1028
1029         Error( "can't proceed after expanding brushes" );
1030 }