2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
\r
5 This file is part of GtkRadiant.
\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
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
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
25 extern qboolean onlyents;
\r
28 mapbrush_t mapbrushes[MAX_MAP_BRUSHES];
\r
30 int nummapbrushsides;
\r
31 side_t brushsides[MAX_MAP_SIDES];
\r
32 brush_texture_t side_brushtextures[MAX_MAP_SIDES];
\r
35 plane_t mapplanes[MAX_MAP_PLANES];
\r
37 #define PLANE_HASHES 1024
\r
38 plane_t *planehash[PLANE_HASHES];
\r
40 vec3_t map_mins, map_maxs;
\r
42 // undefine to make plane finding use linear sort
\r
45 void TestExpandBrushes (void);
\r
55 =============================================================================
\r
59 =============================================================================
\r
68 int PlaneTypeForNormal (vec3_t normal)
\r
72 // NOTE: should these have an epsilon around 1.0?
\r
73 if (normal[0] == 1.0 || normal[0] == -1.0)
\r
75 if (normal[1] == 1.0 || normal[1] == -1.0)
\r
77 if (normal[2] == 1.0 || normal[2] == -1.0)
\r
80 ax = fabs(normal[0]);
\r
81 ay = fabs(normal[1]);
\r
82 az = fabs(normal[2]);
\r
84 if (ax >= ay && ax >= az)
\r
86 if (ay >= ax && ay >= az)
\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
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
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
122 void AddPlaneToHash (plane_t *p)
\r
126 hash = (int)fabs(p->dist) / 8;
\r
127 hash &= (PLANE_HASHES-1);
\r
129 p->hash_chain = planehash[hash];
\r
130 planehash[hash] = p;
\r
135 CreateNewFloatPlane
\r
138 int CreateNewFloatPlane (vec3_t normal, vec_t dist)
\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
148 p = &mapplanes[nummapplanes];
\r
149 VectorCopy (normal, p->normal);
\r
151 p->type = (p+1)->type = PlaneTypeForNormal (p->normal);
\r
153 VectorSubtract (vec3_origin, normal, (p+1)->normal);
\r
154 (p+1)->dist = -dist;
\r
158 // allways put axial planes facing positive first
\r
161 if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0)
\r
168 AddPlaneToHash (p);
\r
169 AddPlaneToHash (p+1);
\r
170 return nummapplanes - 1;
\r
174 AddPlaneToHash (p);
\r
175 AddPlaneToHash (p+1);
\r
176 return nummapplanes - 2;
\r
184 void SnapVector (vec3_t normal)
\r
188 for (i=0 ; i<3 ; i++)
\r
190 if ( fabs(normal[i] - 1) < NORMAL_EPSILON )
\r
192 VectorClear (normal);
\r
196 if ( fabs(normal[i] - -1) < NORMAL_EPSILON )
\r
198 VectorClear (normal);
\r
210 void SnapPlane (vec3_t normal, vec_t *dist)
\r
212 SnapVector (normal);
\r
214 if (fabs(*dist-Q_rint(*dist)) < DIST_EPSILON)
\r
215 *dist = Q_rint(*dist);
\r
224 #ifndef USE_HASHING
\r
225 int FindFloatPlane (vec3_t normal, vec_t dist)
\r
230 SnapPlane (normal, &dist);
\r
231 for (i=0, p=mapplanes ; i<nummapplanes ; i++, p++)
\r
233 if (PlaneEqual (p, normal, dist))
\r
237 return CreateNewFloatPlane (normal, dist);
\r
240 int FindFloatPlane (vec3_t normal, vec_t dist)
\r
246 SnapPlane (normal, &dist);
\r
247 hash = (int)fabs(dist) / 8;
\r
248 hash &= (PLANE_HASHES-1);
\r
250 // search the border bins as well
\r
251 for (i=-1 ; i<=1 ; i++)
\r
253 h = (hash+i)&(PLANE_HASHES-1);
\r
254 for (p = planehash[h] ; p ; p=p->hash_chain)
\r
256 if (PlaneEqual (p, normal, dist))
\r
257 return p-mapplanes;
\r
261 return CreateNewFloatPlane (normal, dist);
\r
270 int PlaneFromPoints (int *p0, int *p1, int *p2)
\r
272 vec3_t t1, t2, normal;
\r
275 VectorSubtract (p0, p1, t1);
\r
276 VectorSubtract (p2, p1, t2);
\r
277 CrossProduct (t1, t2, normal);
\r
278 VectorNormalize (normal, normal);
\r
280 dist = DotProduct (p0, normal);
\r
282 return FindFloatPlane (normal, dist);
\r
286 //====================================================================
\r
294 int BrushContents (mapbrush_t *b)
\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
306 s = &b->original_sides[i];
\r
307 trans |= texinfo[s->texinfo].flags;
\r
308 if (s->contents != contents)
\r
310 Sys_Printf ("Entity %i, Brush %i: mixed face contents\n"
\r
311 , b->entitynum, b->brushnum);
\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
320 contents |= CONTENTS_TRANSLUCENT;
\r
321 if (contents & CONTENTS_SOLID)
\r
323 contents &= ~CONTENTS_SOLID;
\r
324 contents |= CONTENTS_WINDOW;
\r
332 //============================================================================
\r
338 Adds any additional planes necessary to allow the brush to be expanded
\r
339 against axial bounding boxes
\r
342 void AddBrushBevels (mapbrush_t *b)
\r
345 int i, j, k, l, order;
\r
347 brush_texture_t tdtemp;
\r
356 // add the axial planes
\r
359 for (axis=0 ; axis <3 ; axis++)
\r
361 for (dir=-1 ; dir <= 1 ; dir+=2, order++)
\r
363 // see if the plane is allready present
\r
364 for (i=0, s=b->original_sides ; i<b->numsides ; i++,s++)
\r
366 if (mapplanes[s->planenum].normal[axis] == dir)
\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
376 VectorClear (normal);
\r
377 normal[axis] = dir;
\r
379 dist = b->maxs[axis];
\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
389 // if the plane is not in it canonical order, swap it
\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
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
405 // add the edge bevels
\r
407 if (b->numsides == 6)
\r
408 return; // pure axial
\r
410 // test the non-axial plane edges
\r
411 for (i=6 ; i<b->numsides ; i++)
\r
413 s = b->original_sides + i;
\r
417 for (j=0 ; j<w->numpoints ; j++)
\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
424 for (k=0 ; k<3 ; k++)
\r
425 if ( vec[k] == -1 || vec[k] == 1)
\r
428 continue; // only test non-axial edges
\r
430 // try the six possible slanted axials from this edge
\r
431 for (axis=0 ; axis <3 ; axis++)
\r
433 for (dir=-1 ; dir <= 1 ; dir+=2)
\r
435 // construct a plane
\r
436 VectorClear (vec2);
\r
438 CrossProduct (vec, vec2, normal);
\r
439 if (VectorNormalize (normal, normal) < 0.5)
\r
441 dist = DotProduct (w->p[j], normal);
\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
447 // if this plane has allready been used, skip it
\r
448 if (PlaneEqual (&mapplanes[b->original_sides[k].planenum]
\r
452 w2 = b->original_sides[k].winding;
\r
455 for (l=0 ; l<w2->numpoints ; l++)
\r
457 d = DotProduct (w2->p[l], normal) - dist;
\r
459 break; // point in front
\r
461 if (l != w2->numpoints)
\r
465 if (k != b->numsides)
\r
466 continue; // wasn't part of the outer hull
\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
489 makes basewindigs for sides and mins / maxs for the brush
\r
492 qboolean MakeBrushWindings (mapbrush_t *ob)
\r
499 ClearBounds (ob->mins, ob->maxs);
\r
501 for (i=0 ; i<ob->numsides ; i++)
\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
509 if (ob->original_sides[j].bevel)
\r
511 plane = &mapplanes[ob->original_sides[j].planenum^1];
\r
512 ChopWindingInPlace (&w, plane->normal, plane->dist, 0); //CLIP_EPSILON);
\r
515 side = &ob->original_sides[i];
\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
525 for (i=0 ; i<3 ; i++)
\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
542 void ParseBrush (entity_t *mapent)
\r
549 brush_texture_t td;
\r
550 int planepts[3][3];
\r
552 if (nummapbrushes == MAX_MAP_BRUSHES)
\r
553 Error ("nummapbrushes == MAX_MAP_BRUSHES");
\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
562 if (!GetToken (true))
\r
564 if (!strcmp (token, "}") )
\r
567 if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
\r
568 Error ("MAX_MAP_BRUSHSIDES");
\r
569 side = &brushsides[nummapbrushsides];
\r
571 // read the three point plane definition
\r
572 for (i=0 ; i<3 ; i++)
\r
576 if (strcmp (token, "(") )
\r
577 Error ("parsing brush");
\r
579 for (j=0 ; j<3 ; j++)
\r
582 planepts[i][j] = atoi(token);
\r
586 if (strcmp (token, ")") )
\r
587 Error ("parsing brush");
\r
593 // read the texturedef
\r
596 strcpy (td.name, token);
\r
599 td.shift[0] = atoi(token);
\r
601 td.shift[1] = atoi(token);
\r
603 td.rotate = atoi(token);
\r
605 td.scale[0] = atof(token);
\r
607 td.scale[1] = atof(token);
\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
616 if (TokenAvailable())
\r
619 side->contents = atoi(token);
\r
621 side->surf = td.flags = atoi(token);
\r
623 td.value = atoi(token);
\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
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
637 // hints and skips are never detail, and have no content
\r
638 if (side->surf & (SURF_HINT|SURF_SKIP) )
\r
640 side->contents = 0;
\r
641 side->surf &= ~CONTENTS_DETAIL;
\r
646 // find the plane number
\r
648 planenum = PlaneFromPoints (planepts[0], planepts[1], planepts[2]);
\r
649 if (planenum == -1)
\r
651 Sys_Printf ("Entity %i, Brush %i: plane with no normal\n"
\r
652 , b->entitynum, b->brushnum);
\r
657 // see if the plane has been used already
\r
659 for (k=0 ; k<b->numsides ; k++)
\r
661 s2 = b->original_sides + k;
\r
662 if (s2->planenum == planenum)
\r
664 Sys_Printf ("Entity %i, Brush %i: duplicate plane\n"
\r
665 , b->entitynum, b->brushnum);
\r
668 if ( s2->planenum == (planenum^1) )
\r
670 Sys_Printf ("Entity %i, Brush %i: mirrored plane\n"
\r
671 , b->entitynum, b->brushnum);
\r
675 if (k != b->numsides)
\r
676 continue; // duplicated
\r
682 side = b->original_sides + b->numsides;
\r
683 side->planenum = planenum;
\r
684 side->texinfo = TexinfoForBrushTexture (&mapplanes[planenum],
\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
691 nummapbrushsides++;
\r
695 // get the content for the entire brush
\r
696 b->contents = BrushContents (b);
\r
698 // allow detail brushes to be removed
\r
699 if (nodetail && (b->contents & CONTENTS_DETAIL) )
\r
705 // allow water brushes to be removed
\r
706 if (nowater && (b->contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)) )
\r
712 // create windings for sides and bounds for brush
\r
713 MakeBrushWindings (b);
\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
720 for (i=0 ; i<b->numsides ; i++)
\r
721 b->original_sides[i].texinfo = TEXINFO_NODE;
\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
731 if (b->contents & CONTENTS_ORIGIN)
\r
736 if (num_entities == 1)
\r
738 Error ("Entity %i, Brush %i: origin brushes not allowed in world"
\r
739 , b->entitynum, b->brushnum);
\r
743 VectorAdd (b->mins, b->maxs, origin);
\r
744 VectorScale (origin, 0.5, origin);
\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
749 VectorCopy (origin, entities[b->entitynum].origin);
\r
751 // don't keep this brush
\r
757 AddBrushBevels (b);
\r
760 mapent->numbrushes++;
\r
767 Takes all of the brushes from the current entity and
\r
768 adds them to the world's brush list.
\r
770 Used by func_group and func_areaportal
\r
773 void MoveBrushesToWorld (entity_t *mapent)
\r
780 // this is pretty gross, because the brushes are expected to be
\r
781 // in linear order for each entity
\r
783 newbrushes = mapent->numbrushes;
\r
784 worldbrushes = entities[0].numbrushes;
\r
786 temp = malloc(newbrushes*sizeof(mapbrush_t));
\r
787 memcpy (temp, mapbrushes + mapent->firstbrush, newbrushes*sizeof(mapbrush_t));
\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
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
799 // copy the new brushes down
\r
800 memcpy (mapbrushes + worldbrushes, temp, sizeof(mapbrush_t) * newbrushes);
\r
803 entities[0].numbrushes += newbrushes;
\r
804 for (i=1 ; i<num_entities ; i++)
\r
805 entities[i].firstbrush += newbrushes;
\r
808 mapent->numbrushes = 0;
\r
816 qboolean ParseMapEntity (void)
\r
822 int startbrush, startsides;
\r
826 if (!GetToken (true))
\r
829 if (strcmp (token, "{") )
\r
830 Error ("ParseEntity: { not found");
\r
832 if (num_entities == MAX_MAP_ENTITIES)
\r
833 Error ("num_entities == MAX_MAP_ENTITIES");
\r
835 startbrush = nummapbrushes;
\r
836 startsides = nummapbrushsides;
\r
838 mapent = &entities[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
848 if (!GetToken (true))
\r
849 Error ("ParseEntity: EOF without closing brace");
\r
850 if (!strcmp (token, "}") )
\r
852 if (!strcmp (token, "{") )
\r
853 ParseBrush (mapent);
\r
857 e->next = mapent->epairs;
\r
858 mapent->epairs = e;
\r
862 GetVectorForKey (mapent, "origin", mapent->origin);
\r
865 // if there was an origin brush, offset all of the planes and texinfo
\r
867 if (mapent->origin[0] || mapent->origin[1] || mapent->origin[2])
\r
869 for (i=0 ; i<mapent->numbrushes ; i++)
\r
871 b = &mapbrushes[mapent->firstbrush + i];
\r
872 for (j=0 ; j<b->numsides ; j++)
\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
881 MakeBrushWindings (b);
\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
889 MoveBrushesToWorld (mapent);
\r
890 mapent->numbrushes = 0;
\r
894 // areaportal entities move their brushes, but don't eliminate
\r
896 if (!strcmp ("func_areaportal", ValueForKey (mapent, "classname")))
\r
900 if (mapent->numbrushes != 1)
\r
901 Error ("Entity %i: func_areaportal can only be a single brush", num_entities-1);
\r
903 b = &mapbrushes[nummapbrushes-1];
\r
904 b->contents = CONTENTS_AREAPORTAL;
\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
917 //===================================================================
\r
924 void LoadMapFile (char *filename)
\r
928 Sys_FPrintf( SYS_VRB, "--- LoadMapFile ---\n");
\r
930 LoadScriptFile (filename);
\r
932 nummapbrushsides = 0;
\r
935 while (ParseMapEntity ())
\r
939 ClearBounds (map_mins, map_maxs);
\r
940 for (i=0 ; i<entities[0].numbrushes ; i++)
\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
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
959 // TestExpandBrushes ();
\r
963 //====================================================================
\r
970 Expands all the brush planes and saves a new map out
\r
973 void TestExpandBrushes (void)
\r
979 char *name = "expanded.map";
\r
983 Sys_Printf ("writing %s\n", name);
\r
984 f = fopen (name, "wb");
\r
986 Error ("Can't write %s\b", name);
\r
988 fprintf (f, "{\n\"classname\" \"worldspawn\"\n");
\r
990 for (bn=0 ; bn<nummapbrushes ; bn++)
\r
992 brush = &mapbrushes[bn];
\r
993 fprintf (f, "{\n");
\r
994 for (i=0 ; i<brush->numsides ; i++)
\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
1001 w = BaseWindingForPlane (mapplanes[s->planenum].normal, dist);
\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
1007 fprintf (f, "%s 0 0 0 1 1\n", texinfo[s->texinfo].texture);
\r
1010 fprintf (f, "}\n");
\r
1012 fprintf (f, "}\n");
\r
1016 Error ("can't proceed after expanding brushes");
\r