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