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