1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 ----------------------------------------------------------------------------------
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
27 ------------------------------------------------------------------------------- */
41 /* FIXME: remove these vars */
43 /* undefine to make plane finding use linear sort (note: really slow) */
45 #define PLANE_HASHES 8192
47 int planehash[ PLANE_HASHES ];
59 ydnar: replaced with variable epsilon for djbob
62 qboolean PlaneEqual( plane_t *p, vec3_t normal, vec_t dist )
67 /* get local copies */
72 // We check equality of each component since we're using '<', not '<='
73 // (the epsilons may be zero). We want to use '<' intead of '<=' to be
74 // consistent with the true meaning of "epsilon", and also because other
75 // parts of the code uses this inequality.
76 if ((p->dist == dist || fabs(p->dist - dist) < de) &&
77 (p->normal[0] == normal[0] || fabs(p->normal[0] - normal[0]) < ne) &&
78 (p->normal[1] == normal[1] || fabs(p->normal[1] - normal[1]) < ne) &&
79 (p->normal[2] == normal[2] || fabs(p->normal[2] - normal[2]) < ne))
92 void AddPlaneToHash( plane_t *p )
97 hash = (PLANE_HASHES - 1) & (int) fabs( p->dist );
99 p->hash_chain = planehash[hash];
100 planehash[hash] = p - mapplanes + 1;
108 int CreateNewFloatPlane (vec3_t normal, vec_t dist)
112 if (VectorLength(normal) < 0.5)
114 Sys_Printf( "FloatPlane: bad normal\n");
118 // create a new plane
119 AUTOEXPAND_BY_REALLOC(mapplanes, nummapplanes+1, allocatedmapplanes, 1024);
121 p = &mapplanes[nummapplanes];
122 VectorCopy (normal, p->normal);
124 p->type = (p+1)->type = PlaneTypeForNormal (p->normal);
126 VectorSubtract (vec3_origin, normal, (p+1)->normal);
131 // allways put axial planes facing positive first
134 if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0)
142 AddPlaneToHash (p+1);
143 return nummapplanes - 1;
148 AddPlaneToHash (p+1);
149 return nummapplanes - 2;
156 Snaps a near-axial normal vector.
157 Returns qtrue if and only if the normal was adjusted.
160 qboolean SnapNormal( vec3_t normal )
162 #if Q3MAP2_EXPERIMENTAL_SNAP_NORMAL_FIX
164 qboolean adjusted = qfalse;
166 // A change from the original SnapNormal() is that we snap each
167 // component that's close to 0. So for example if a normal is
168 // (0.707, 0.707, 0.0000001), it will get snapped to lie perfectly in the
169 // XY plane (its Z component will be set to 0 and its length will be
170 // normalized). The original SnapNormal() didn't snap such vectors - it
171 // only snapped vectors that were near a perfect axis.
173 for (i = 0; i < 3; i++)
175 if (normal[i] != 0.0 && -normalEpsilon < normal[i] && normal[i] < normalEpsilon)
184 VectorNormalize(normal, normal);
191 // I would suggest that you uncomment the following code and look at the
195 Sys_Printf("normalEpsilon is %f\n", normalEpsilon);
200 normal[2] = i * 0.000001;
201 VectorNormalize(normal, normal);
202 if (1.0 - normal[0] >= normalEpsilon) {
203 Sys_Printf("(%f %f %f)\n", normal[0], normal[1], normal[2]);
204 Error("SnapNormal: test completed");
209 // When the normalEpsilon is 0.00001, the loop will break out when normal is
210 // (0.999990 0.000000 0.004469). In other words, this is the vector closest
211 // to axial that will NOT be snapped. Anything closer will be snaped. Now,
212 // 0.004469 is close to 1/225. The length of a circular quarter-arc of radius
213 // 1 is PI/2, or about 1.57. And 0.004469/1.57 is about 0.0028, or about
214 // 1/350. Expressed a different way, 1/350 is also about 0.26/90.
215 // This means is that a normal with an angle that is within 1/4 of a degree
216 // from axial will be "snapped". My belief is that the person who wrote the
217 // code below did not intend it this way. I think the person intended that
218 // the epsilon be measured against the vector components close to 0, not 1.0.
219 // I think the logic should be: if 2 of the normal components are within
220 // epsilon of 0, then the vector can be snapped to be perfectly axial.
221 // We may consider adjusting the epsilon to a larger value when we make this
224 for( i = 0; i < 3; i++ )
226 if( fabs( normal[ i ] - 1 ) < normalEpsilon )
228 VectorClear( normal );
232 if( fabs( normal[ i ] - -1 ) < normalEpsilon )
234 VectorClear( normal );
247 snaps a plane to normal/distance epsilons
250 void SnapPlane( vec3_t normal, vec_t *dist, vec3_t center )
252 // SnapPlane disabled by LordHavoc because it often messes up collision
253 // brushes made from triangles of embedded models, and it has little effect
254 // on anything else (axial planes are usually derived from snapped points)
256 SnapPlane reenabled by namespace because of multiple reports of
257 q3map2-crashes which were triggered by this patch.
259 SnapNormal( normal );
261 // TODO: Rambetter has some serious comments here as well. First off,
262 // in the case where a normal is non-axial, there is nothing special
263 // about integer distances. I would think that snapping a distance might
264 // make sense for axial normals, but I'm not so sure about snapping
265 // non-axial normals. A shift by 0.01 in a plane, multiplied by a clipping
266 // against another plane that is 5 degrees off, and we introduce 0.1 error
267 // easily. A 0.1 error in a vertex is where problems start to happen, such
268 // as disappearing triangles.
270 // Second, assuming we have snapped the normal above, let's say that the
271 // plane we just snapped was defined for some points that are actually
272 // quite far away from normal * dist. Well, snapping the normal in this
273 // case means that we've just moved those points by potentially many units!
274 // Therefore, if we are going to snap the normal, we need to know the
275 // points we're snapping for so that the plane snaps with those points in
276 // mind (points remain close to the plane).
278 // I would like to know exactly which problems SnapPlane() is trying to
279 // solve so that we can better engineer it (I'm not saying that SnapPlane()
280 // should be removed altogether). Fix all this snapping code at some point!
282 if( fabs( *dist - Q_rint( *dist ) ) < distanceEpsilon )
283 *dist = Q_rint( *dist );
288 snaps a plane to normal/distance epsilons, improved code
290 void SnapPlaneImproved(vec3_t normal, vec_t *dist, int numPoints, const vec3_t *points)
294 vec_t distNearestInt;
296 if (SnapNormal(normal))
300 // Adjust the dist so that the provided points don't drift away.
302 for (i = 0; i < numPoints; i++)
304 VectorAdd(center, points[i], center);
306 for (i = 0; i < 3; i++) { center[i] = center[i] / numPoints; }
307 *dist = DotProduct(normal, center);
311 if (VectorIsOnAxis(normal))
313 // Only snap distance if the normal is an axis. Otherwise there
314 // is nothing "natural" about snapping the distance to an integer.
315 distNearestInt = Q_rint(*dist);
316 if (-distanceEpsilon < *dist - distNearestInt && *dist - distNearestInt < distanceEpsilon)
318 *dist = distNearestInt;
327 ydnar: changed to allow a number of test points to be supplied that
328 must be within an epsilon distance of the plane
331 int FindFloatPlane( vec3_t innormal, vec_t dist, int numPoints, vec3_t *points ) // NOTE: this has a side effect on the normal. Good or bad?
342 VectorCopy(innormal, normal);
343 #if Q3MAP2_EXPERIMENTAL_SNAP_PLANE_FIX
344 SnapPlaneImproved(normal, &dist, numPoints, (const vec3_t *) points);
346 SnapPlane( normal, &dist );
349 hash = (PLANE_HASHES - 1) & (int) fabs( dist );
351 /* search the border bins as well */
352 for( i = -1; i <= 1; i++ )
354 h = (hash + i) & (PLANE_HASHES - 1);
355 for( pidx = planehash[ h ] - 1; pidx != -1; pidx = mapplanes[pidx].hash_chain - 1 )
357 p = &mapplanes[pidx];
359 /* do standard plane compare */
360 if( !PlaneEqual( p, normal, dist ) )
363 /* ydnar: uncomment the following line for old-style plane finding */
364 //% return p - mapplanes;
366 /* ydnar: test supplied points against this plane */
367 for( j = 0; j < numPoints; j++ )
369 // NOTE: When dist approaches 2^16, the resolution of 32 bit floating
370 // point number is greatly decreased. The distanceEpsilon cannot be
371 // very small when world coordinates extend to 2^16. Making the
372 // dot product here in 64 bit land will not really help the situation
373 // because the error will already be carried in dist.
374 d = DotProduct( points[ j ], p->normal ) - p->dist;
376 if (d != 0.0 && d >= distanceEpsilon)
377 break; // Point is too far from plane.
380 /* found a matching plane */
382 return p - mapplanes;
386 /* none found, so create a new one */
387 return CreateNewFloatPlane( normal, dist );
397 VectorCopy(innormal, normal);
398 #if Q3MAP2_EXPERIMENTAL_SNAP_PLANE_FIX
399 SnapPlaneImproved(normal, &dist, numPoints, (const vec3_t *) points);
401 SnapPlane( normal, &dist );
403 for( i = 0, p = mapplanes; i < nummapplanes; i++, p++ )
405 if( !PlaneEqual( p, normal, dist ) )
408 /* ydnar: uncomment the following line for old-style plane finding */
411 /* ydnar: test supplied points against this plane */
412 for( j = 0; j < numPoints; j++ )
414 d = DotProduct( points[ j ], p->normal ) - p->dist;
415 if( fabs( d ) > distanceEpsilon )
419 /* found a matching plane */
422 // TODO: Note that the non-USE_HASHING code does not compute epsilons
423 // for the provided points. It should do that. I think this code
424 // is unmaintained because nobody sets USE_HASHING to off.
427 return CreateNewFloatPlane( normal, dist );
436 takes 3 points and finds the plane they lie in
439 int MapPlaneFromPoints( vec3_t *p )
441 #if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES
442 vec3_accu_t paccu[3];
443 vec3_accu_t t1, t2, normalAccu;
447 VectorCopyRegularToAccu(p[0], paccu[0]);
448 VectorCopyRegularToAccu(p[1], paccu[1]);
449 VectorCopyRegularToAccu(p[2], paccu[2]);
451 VectorSubtractAccu(paccu[0], paccu[1], t1);
452 VectorSubtractAccu(paccu[2], paccu[1], t2);
453 CrossProductAccu(t1, t2, normalAccu);
454 VectorNormalizeAccu(normalAccu, normalAccu);
455 // TODO: A 32 bit float for the plane distance isn't enough resolution
456 // if the plane is 2^16 units away from the origin (the "epsilon" approaches
457 // 0.01 in that case).
458 dist = (vec_t) DotProductAccu(paccu[0], normalAccu);
459 VectorCopyAccuToRegular(normalAccu, normal);
461 return FindFloatPlane(normal, dist, 3, p);
463 vec3_t t1, t2, normal;
467 /* calc plane normal */
468 VectorSubtract( p[ 0 ], p[ 1 ], t1 );
469 VectorSubtract( p[ 2 ], p[ 1 ], t2 );
470 CrossProduct( t1, t2, normal );
471 VectorNormalize( normal, normal );
473 /* calc plane distance */
474 dist = DotProduct( p[ 0 ], normal );
476 /* store the plane */
477 return FindFloatPlane( normal, dist, 3, p );
485 the content flags and compile flags on all sides of a brush should be the same
488 void SetBrushContents( brush_t *b )
490 int contentFlags, compileFlags;
496 /* get initial compile flags from first side */
498 contentFlags = s->contentFlags;
499 compileFlags = s->compileFlags;
500 b->contentShader = s->shaderInfo;
503 /* get the content/compile flags for every side in the brush */
504 for( i = 1; i < b->numsides; i++, s++ )
507 if( s->shaderInfo == NULL )
509 if( s->contentFlags != contentFlags || s->compileFlags != compileFlags )
512 contentFlags |= s->contentFlags;
513 compileFlags |= s->compileFlags;
516 /* ydnar: getting rid of this stupid warning */
518 //% Sys_FPrintf( SYS_VRB,"Entity %i, Brush %i: mixed face contentFlags\n", b->entitynum, b->brushnum );
520 /* check for detail & structural */
521 if( (compileFlags & C_DETAIL) && (compileFlags & C_STRUCTURAL) )
523 xml_Select( "Mixed detail and structural (defaulting to structural)", mapEnt->mapEntityNum, entitySourceBrushes, qfalse );
524 compileFlags &= ~C_DETAIL;
527 /* the fulldetail flag will cause detail brushes to be treated like normal brushes */
529 compileFlags &= ~C_DETAIL;
531 /* all translucent brushes that aren't specifically made structural will be detail */
532 if( (compileFlags & C_TRANSLUCENT) && !(compileFlags & C_STRUCTURAL) )
533 compileFlags |= C_DETAIL;
536 if( compileFlags & C_DETAIL )
548 if( compileFlags & C_TRANSLUCENT )
554 if( compileFlags & C_AREAPORTAL )
557 /* set brush flags */
558 b->contentFlags = contentFlags;
559 b->compileFlags = compileFlags;
566 adds any additional planes necessary to allow the brush being
567 built to be expanded against axial bounding boxes
568 ydnar 2003-01-20: added mrelusive fixes
571 void AddBrushBevels( void )
574 int i, j, k, l, order;
584 // add the axial planes
587 for ( axis = 0; axis < 3; axis++ ) {
588 for ( dir = -1; dir <= 1; dir += 2, order++ ) {
589 // see if the plane is allready present
590 for ( i = 0, s = buildBrush->sides; i < buildBrush->numsides; i++, s++ )
592 /* ydnar: testing disabling of mre code */
595 if ( mapplanes[s->planenum].normal[axis] >= 0.9999f ) {
600 if ( mapplanes[s->planenum].normal[axis] <= -0.9999f ) {
605 if( (dir > 0 && mapplanes[ s->planenum ].normal[ axis ] == 1.0f ) ||
606 (dir < 0 && mapplanes[ s->planenum ].normal[ axis ] == -1.0f) )
611 if ( i == buildBrush->numsides ) {
613 if ( buildBrush->numsides == MAX_BUILD_SIDES ) {
614 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue);
616 memset( s, 0, sizeof( *s ) );
617 buildBrush->numsides++;
618 VectorClear (normal);
623 /* ydnar: adding bevel plane snapping for fewer bsp planes */
625 dist = floor( buildBrush->maxs[ axis ] / bevelSnap ) * bevelSnap;
627 dist = buildBrush->maxs[ axis ];
631 /* ydnar: adding bevel plane snapping for fewer bsp planes */
633 dist = -ceil( buildBrush->mins[ axis ] / bevelSnap ) * bevelSnap;
635 dist = -buildBrush->mins[ axis ];
638 s->planenum = FindFloatPlane( normal, dist, 0, NULL );
639 s->contentFlags = buildBrush->sides[ 0 ].contentFlags;
644 // if the plane is not in it canonical order, swap it
646 sidetemp = buildBrush->sides[order];
647 buildBrush->sides[order] = buildBrush->sides[i];
648 buildBrush->sides[i] = sidetemp;
654 // add the edge bevels
656 if ( buildBrush->numsides == 6 ) {
657 return; // pure axial
660 // test the non-axial plane edges
661 for ( i = 6; i < buildBrush->numsides; i++ ) {
662 s = buildBrush->sides + i;
667 for ( j = 0; j < w->numpoints; j++) {
668 k = (j+1)%w->numpoints;
669 VectorSubtract( w->p[j], w->p[k], vec );
670 if ( VectorNormalize( vec, vec ) < 0.5f ) {
674 for ( k = 0; k < 3; k++ ) {
675 if ( vec[k] == -1.0f || vec[k] == 1.0f || (vec[k] == 0.0f && vec[(k+1)%3] == 0.0f) ) {
680 continue; // only test non-axial edges
684 //% Sys_Printf( "-------------\n" );
686 // try the six possible slanted axials from this edge
687 for ( axis = 0; axis < 3; axis++ ) {
688 for ( dir = -1; dir <= 1; dir += 2 ) {
692 CrossProduct( vec, vec2, normal );
693 if ( VectorNormalize( normal, normal ) < 0.5f ) {
696 dist = DotProduct( w->p[j], normal );
698 // if all the points on all the sides are
699 // behind this plane, it is a proper edge bevel
700 for ( k = 0; k < buildBrush->numsides; k++ ) {
702 // if this plane has allready been used, skip it
703 if ( PlaneEqual( &mapplanes[buildBrush->sides[k].planenum], normal, dist ) ) {
707 w2 = buildBrush->sides[k].winding;
712 for ( l = 0; l < w2->numpoints; l++ ) {
713 d = DotProduct( w2->p[l], normal ) - dist;
715 break; // point in front
721 // if some point was at the front
722 if ( l != w2->numpoints ) {
726 // if no points at the back then the winding is on the bevel plane
727 if ( minBack > -0.1f ) {
728 //% Sys_Printf( "On bevel plane\n" );
733 if ( k != buildBrush->numsides ) {
734 continue; // wasn't part of the outer hull
738 //% Sys_Printf( "n = %f %f %f\n", normal[ 0 ], normal[ 1 ], normal[ 2 ] );
741 if( buildBrush->numsides == MAX_BUILD_SIDES ) {
742 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue);
744 s2 = &buildBrush->sides[buildBrush->numsides];
745 buildBrush->numsides++;
746 memset( s2, 0, sizeof( *s2 ) );
748 s2->planenum = FindFloatPlane( normal, dist, 1, &w->p[ j ] );
749 s2->contentFlags = buildBrush->sides[0].contentFlags;
762 produces a final brush based on the buildBrush->sides array
763 and links it to the current entity
766 static void MergeOrigin(entity_t *ent, vec3_t origin)
771 /* we have not parsed the brush completely yet... */
772 GetVectorForKey( ent, "origin", ent->origin );
774 VectorMA(origin, -1, ent->originbrush_origin, adjustment);
775 VectorAdd(adjustment, ent->origin, ent->origin);
776 VectorCopy(origin, ent->originbrush_origin);
778 sprintf(string, "%f %f %f", ent->origin[0], ent->origin[1], ent->origin[2]);
779 SetKeyValue(ent, "origin", string);
782 brush_t *FinishBrush( qboolean noCollapseGroups )
787 /* create windings for sides and bounds for brush */
788 if ( !CreateBrushWindings( buildBrush ) )
791 /* origin brushes are removed, but they set the rotation origin for the rest of the brushes in the entity.
792 after the entire entity is parsed, the planenums and texinfos will be adjusted for the origin brush */
793 if( buildBrush->compileFlags & C_ORIGIN )
797 Sys_Printf( "Entity %i, Brush %i: origin brush detected\n",
798 mapEnt->mapEntityNum, entitySourceBrushes );
800 if( numEntities == 1 )
802 Sys_Printf( "Entity %i, Brush %i: origin brushes not allowed in world\n",
803 mapEnt->mapEntityNum, entitySourceBrushes );
807 VectorAdd (buildBrush->mins, buildBrush->maxs, origin);
808 VectorScale (origin, 0.5, origin);
810 MergeOrigin(&entities[ numEntities - 1 ], origin);
812 /* don't keep this brush */
816 /* determine if the brush is an area portal */
817 if( buildBrush->compileFlags & C_AREAPORTAL )
819 if( numEntities != 1 )
821 Sys_Printf ("Entity %i, Brush %i: areaportals only allowed in world\n", numEntities - 1, entitySourceBrushes );
826 /* add bevel planes */
827 if(!noCollapseGroups)
831 b = CopyBrush( buildBrush );
833 /* set map entity and brush numbering */
834 b->entityNum = mapEnt->mapEntityNum;
835 b->brushNum = entitySourceBrushes;
840 /* link opaque brushes to head of list, translucent brushes to end */
841 if( b->opaque || mapEnt->lastBrush == NULL )
843 b->next = mapEnt->brushes;
845 if( mapEnt->lastBrush == NULL )
846 mapEnt->lastBrush = b;
851 mapEnt->lastBrush->next = b;
852 mapEnt->lastBrush = b;
855 /* link colorMod volume brushes to the entity directly */
856 if( b->contentShader != NULL &&
857 b->contentShader->colorMod != NULL &&
858 b->contentShader->colorMod->type == CM_VOLUME )
860 b->nextColorModBrush = mapEnt->colorModBrushes;
861 mapEnt->colorModBrushes = b;
864 /* return to sender */
871 TextureAxisFromPlane()
872 determines best orthagonal axis to project a texture onto a wall
873 (must be identical in radiant!)
876 vec3_t baseaxis[18] =
878 {0,0,1}, {1,0,0}, {0,-1,0}, // floor
879 {0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling
880 {1,0,0}, {0,1,0}, {0,0,-1}, // west wall
881 {-1,0,0}, {0,1,0}, {0,0,-1}, // east wall
882 {0,1,0}, {1,0,0}, {0,0,-1}, // south wall
883 {0,-1,0}, {1,0,0}, {0,0,-1} // north wall
886 void TextureAxisFromPlane( plane_t *pln, vec3_t xv, vec3_t yv )
895 for (i=0 ; i<6 ; i++)
897 dot = DotProduct (pln->normal, baseaxis[i*3]);
898 if( dot > best + 0.0001f ) /* ydnar: bug 637 fix, suggested by jmonroe */
905 VectorCopy (baseaxis[bestaxis*3+1], xv);
906 VectorCopy (baseaxis[bestaxis*3+2], yv);
913 creates world-to-texture mapping vecs for crappy quake plane arrangements
916 void QuakeTextureVecs( plane_t *plane, vec_t shift[ 2 ], vec_t rotate, vec_t scale[ 2 ], vec_t mappingVecs[ 2 ][ 4 ] )
920 vec_t ang, sinv, cosv;
925 TextureAxisFromPlane(plane, vecs[0], vecs[1]);
934 { sinv = 0 ; cosv = 1; }
935 else if (rotate == 90)
936 { sinv = 1 ; cosv = 0; }
937 else if (rotate == 180)
938 { sinv = 0 ; cosv = -1; }
939 else if (rotate == 270)
940 { sinv = -1 ; cosv = 0; }
943 ang = rotate / 180 * Q_PI;
962 for (i=0 ; i<2 ; i++) {
963 ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
964 nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
969 for (i=0 ; i<2 ; i++)
970 for (j=0 ; j<3 ; j++)
971 mappingVecs[i][j] = vecs[i][j] / scale[i];
973 mappingVecs[0][3] = shift[0];
974 mappingVecs[1][3] = shift[1];
981 parses the sides into buildBrush->sides[], nothing else.
982 no validation, back plane removal, etc.
985 added brush epairs parsing ( ignoring actually )
987 added exclusive brush primitive parsing
989 support for old brush format back in
990 NOTE: it would be "cleaner" to have seperate functions to parse between old and new brushes
993 static void ParseRawBrush( qboolean onlyLights )
996 vec3_t planePoints[ 3 ];
1002 char name[ MAX_QPATH ];
1003 char shader[ MAX_QPATH ];
1008 buildBrush->numsides = 0;
1009 buildBrush->detail = qfalse;
1012 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
1018 if( !GetToken( qtrue ) )
1020 if( !strcmp( token, "}" ) )
1023 /* ttimo : bp: here we may have to jump over brush epairs (only used in editor) */
1024 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
1028 if( strcmp( token, "(" ) )
1037 /* test side count */
1038 if( buildBrush->numsides >= MAX_BUILD_SIDES )
1039 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue );
1042 side = &buildBrush->sides[ buildBrush->numsides ];
1043 memset( side, 0, sizeof( *side ) );
1044 buildBrush->numsides++;
1046 /* read the three point plane definition */
1047 Parse1DMatrix( 3, planePoints[ 0 ] );
1048 Parse1DMatrix( 3, planePoints[ 1 ] );
1049 Parse1DMatrix( 3, planePoints[ 2 ] );
1051 /* bp: read the texture matrix */
1052 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
1053 Parse2DMatrix( 2, 3, (float*) side->texMat );
1055 /* read shader name */
1057 strcpy( name, token );
1060 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
1063 shift[ 0 ] = atof( token );
1065 shift[ 1 ] = atof( token );
1067 rotate = atof( token );
1069 scale[ 0 ] = atof( token );
1071 scale[ 1 ] = atof( token );
1074 /* set default flags and values */
1075 sprintf( shader, "textures/%s", name );
1077 si = &shaderInfo[ 0 ];
1079 si = ShaderInfoForShader( shader );
1080 side->shaderInfo = si;
1081 side->surfaceFlags = si->surfaceFlags;
1082 side->contentFlags = si->contentFlags;
1083 side->compileFlags = si->compileFlags;
1084 side->value = si->value;
1086 /* ydnar: gs mods: bias texture shift */
1087 if( si->globalTexture == qfalse )
1089 shift[ 0 ] -= (floor( shift[ 0 ] / si->shaderWidth ) * si->shaderWidth);
1090 shift[ 1 ] -= (floor( shift[ 1 ] / si->shaderHeight ) * si->shaderHeight);
1094 historically, there are 3 integer values at the end of a brushside line in a .map file.
1095 in quake 3, the only thing that mattered was the first of these three values, which
1096 was previously the content flags. and only then did a single bit matter, the detail
1097 bit. because every game has its own special flags for specifying detail, the
1098 traditionally game-specified CONTENTS_DETAIL flag was overridden for Q3Map 2.3.0
1099 by C_DETAIL, defined in q3map2.h. the value is exactly as it was before, but
1100 is stored in compileFlags, as opposed to contentFlags, for multiple-game
1104 if( TokenAvailable() )
1106 /* get detail bit from map content flags */
1108 flags = atoi( token );
1109 if( flags & C_DETAIL )
1110 side->compileFlags |= C_DETAIL;
1114 //% td.flags = atoi( token );
1116 //% td.value = atoi( token );
1119 /* find the plane number */
1120 planenum = MapPlaneFromPoints( planePoints );
1121 side->planenum = planenum;
1123 /* bp: get the texture mapping for this texturedef / plane combination */
1124 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
1125 QuakeTextureVecs( &mapplanes[ planenum ], shift, rotate, scale, side->vecs );
1129 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
1140 RemoveDuplicateBrushPlanes
1141 returns false if the brush has a mirrored set of planes,
1142 meaning it encloses no volume.
1143 also removes planes without any normal
1146 qboolean RemoveDuplicateBrushPlanes( brush_t *b )
1153 for ( i = 1 ; i < b->numsides ; i++ ) {
1155 // check for a degenerate plane
1156 if ( sides[i].planenum == -1) {
1157 xml_Select( "degenerate plane", b->entityNum, b->brushNum, qfalse );
1159 for ( k = i + 1 ; k < b->numsides ; k++ ) {
1160 sides[k-1] = sides[k];
1167 // check for duplication and mirroring
1168 for ( j = 0 ; j < i ; j++ ) {
1169 if ( sides[i].planenum == sides[j].planenum ) {
1170 xml_Select( "duplicate plane", b->entityNum, b->brushNum, qfalse );
1171 // remove the second duplicate
1172 for ( k = i + 1 ; k < b->numsides ; k++ ) {
1173 sides[k-1] = sides[k];
1180 if ( sides[i].planenum == (sides[j].planenum ^ 1) ) {
1181 // mirror plane, brush is invalid
1182 xml_Select( "mirrored plane", b->entityNum, b->brushNum, qfalse );
1194 parses a brush out of a map file and sets it up
1197 static void ParseBrush( qboolean onlyLights, qboolean noCollapseGroups )
1202 /* parse the brush out of the map */
1203 ParseRawBrush( onlyLights );
1205 /* only go this far? */
1209 /* set some defaults */
1210 buildBrush->portalareas[ 0 ] = -1;
1211 buildBrush->portalareas[ 1 ] = -1;
1212 buildBrush->entityNum = numMapEntities - 1;
1213 buildBrush->brushNum = entitySourceBrushes;
1215 /* if there are mirrored planes, the entire brush is invalid */
1216 if( !RemoveDuplicateBrushPlanes( buildBrush ) )
1219 /* get the content for the entire brush */
1220 SetBrushContents( buildBrush );
1222 /* allow detail brushes to be removed */
1223 if( nodetail && (buildBrush->compileFlags & C_DETAIL) )
1225 //% FreeBrush( buildBrush );
1229 /* allow liquid brushes to be removed */
1230 if( nowater && (buildBrush->compileFlags & C_LIQUID ) )
1232 //% FreeBrush( buildBrush );
1236 /* ydnar: allow hint brushes to be removed */
1237 if( noHint && (buildBrush->compileFlags & C_HINT) )
1239 //% FreeBrush( buildBrush );
1243 /* finish the brush */
1244 b = FinishBrush(noCollapseGroups);
1250 MoveBrushesToWorld()
1251 takes all of the brushes from the current entity and
1252 adds them to the world's brush list
1253 (used by func_group)
1256 void AdjustBrushesForOrigin( entity_t *ent );
1257 void MoveBrushesToWorld( entity_t *ent )
1262 /* we need to undo the common/origin adjustment, and instead shift them by the entity key origin */
1263 VectorScale(ent->origin, -1, ent->originbrush_origin);
1264 AdjustBrushesForOrigin(ent);
1265 VectorClear(ent->originbrush_origin);
1268 for( b = ent->brushes; b != NULL; b = next )
1270 /* get next brush */
1273 /* link opaque brushes to head of list, translucent brushes to end */
1274 if( b->opaque || entities[ 0 ].lastBrush == NULL )
1276 b->next = entities[ 0 ].brushes;
1277 entities[ 0 ].brushes = b;
1278 if( entities[ 0 ].lastBrush == NULL )
1279 entities[ 0 ].lastBrush = b;
1284 entities[ 0 ].lastBrush->next = b;
1285 entities[ 0 ].lastBrush = b;
1288 ent->brushes = NULL;
1290 /* ydnar: move colormod brushes */
1291 if( ent->colorModBrushes != NULL )
1293 for( b = ent->colorModBrushes; b->nextColorModBrush != NULL; b = b->nextColorModBrush );
1295 b->nextColorModBrush = entities[ 0 ].colorModBrushes;
1296 entities[ 0 ].colorModBrushes = ent->colorModBrushes;
1298 ent->colorModBrushes = NULL;
1302 if( ent->patches != NULL )
1304 for( pm = ent->patches; pm->next != NULL; pm = pm->next );
1306 pm->next = entities[ 0 ].patches;
1307 entities[ 0 ].patches = ent->patches;
1309 ent->patches = NULL;
1316 AdjustBrushesForOrigin()
1319 void AdjustBrushesForOrigin( entity_t *ent )
1328 /* walk brush list */
1329 for( b = ent->brushes; b != NULL; b = b->next )
1331 /* offset brush planes */
1332 for( i = 0; i < b->numsides; i++)
1334 /* get brush side */
1337 /* offset side plane */
1338 newdist = mapplanes[ s->planenum ].dist - DotProduct( mapplanes[ s->planenum ].normal, ent->originbrush_origin );
1340 /* find a new plane */
1341 s->planenum = FindFloatPlane( mapplanes[ s->planenum ].normal, newdist, 0, NULL );
1344 /* rebuild brush windings (ydnar: just offsetting the winding above should be fine) */
1345 CreateBrushWindings( b );
1348 /* walk patch list */
1349 for( p = ent->patches; p != NULL; p = p->next )
1351 for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )
1352 VectorSubtract( p->mesh.verts[ i ].xyz, ent->originbrush_origin, p->mesh.verts[ i ].xyz );
1359 SetEntityBounds() - ydnar
1360 finds the bounds of an entity's brushes (necessary for terrain-style generic metashaders)
1363 void SetEntityBounds( entity_t *e )
1374 /* walk the entity's brushes/patches and determine bounds */
1375 ClearBounds( mins, maxs );
1376 for( b = e->brushes; b; b = b->next )
1378 AddPointToBounds( b->mins, mins, maxs );
1379 AddPointToBounds( b->maxs, mins, maxs );
1381 for( p = e->patches; p; p = p->next )
1383 for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )
1384 AddPointToBounds( p->mesh.verts[ i ].xyz, mins, maxs );
1387 /* try to find explicit min/max key */
1388 value = ValueForKey( e, "min" );
1389 if( value[ 0 ] != '\0' )
1390 GetVectorForKey( e, "min", mins );
1391 value = ValueForKey( e, "max" );
1392 if( value[ 0 ] != '\0' )
1393 GetVectorForKey( e, "max", maxs );
1395 /* store the bounds */
1396 for( b = e->brushes; b; b = b->next )
1398 VectorCopy( mins, b->eMins );
1399 VectorCopy( maxs, b->eMaxs );
1401 for( p = e->patches; p; p = p->next )
1403 VectorCopy( mins, p->eMins );
1404 VectorCopy( maxs, p->eMaxs );
1411 LoadEntityIndexMap() - ydnar
1412 based on LoadAlphaMap() from terrain.c, a little more generic
1415 void LoadEntityIndexMap( entity_t *e )
1417 int i, size, numLayers, w, h;
1418 const char *value, *indexMapFilename, *shader;
1419 char ext[ MAX_QPATH ], offset[ 4096 ], *search, *space;
1421 unsigned int *pixels32;
1427 /* this only works with bmodel ents */
1428 if( e->brushes == NULL && e->patches == NULL )
1431 /* determine if there is an index map (support legacy "alphamap" key as well) */
1432 value = ValueForKey( e, "_indexmap" );
1433 if( value[ 0 ] == '\0' )
1434 value = ValueForKey( e, "alphamap" );
1435 if( value[ 0 ] == '\0' )
1437 indexMapFilename = value;
1439 /* get number of layers (support legacy "layers" key as well) */
1440 value = ValueForKey( e, "_layers" );
1441 if( value[ 0 ] == '\0' )
1442 value = ValueForKey( e, "layers" );
1443 if( value[ 0 ] == '\0' )
1445 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_layers\" or \"layers\" key\n", indexMapFilename );
1446 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1449 numLayers = atoi( value );
1452 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has < 1 layer (%d)\n", indexMapFilename, numLayers );
1453 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1457 /* get base shader name (support legacy "shader" key as well) */
1458 value = ValueForKey( mapEnt, "_shader" );
1459 if( value[ 0 ] == '\0' )
1460 value = ValueForKey( e, "shader" );
1461 if( value[ 0 ] == '\0' )
1463 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_shader\" or \"shader\" key\n", indexMapFilename );
1464 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1470 Sys_FPrintf( SYS_VRB, "Entity %d (%s) has shader index map \"%s\"\n", mapEnt->mapEntityNum, ValueForKey( e, "classname" ), indexMapFilename );
1472 /* get index map file extension */
1473 ExtractFileExtension( indexMapFilename, ext );
1475 /* handle tga image */
1476 if( !Q_stricmp( ext, "tga" ) )
1479 Load32BitImage( indexMapFilename, &pixels32, &w, &h );
1481 /* convert to bytes */
1483 pixels = safe_malloc( size );
1484 for( i = 0; i < size; i++ )
1486 pixels[ i ] = ((pixels32[ i ] & 0xFF) * numLayers) / 256;
1487 if( pixels[ i ] >= numLayers )
1488 pixels[ i ] = numLayers - 1;
1491 /* free the 32 bit image */
1497 Load256Image( indexMapFilename, &pixels, NULL, &w, &h );
1500 //% Sys_Printf( "-------------------------------" );
1502 /* fix up out-of-range values */
1504 for( i = 0; i < size; i++ )
1506 if( pixels[ i ] >= numLayers )
1507 pixels[ i ] = numLayers - 1;
1510 //% if( (i % w) == 0 )
1511 //% Sys_Printf( "\n" );
1512 //% Sys_Printf( "%c", pixels[ i ] + '0' );
1516 //% Sys_Printf( "\n-------------------------------\n" );
1519 /* the index map must be at least 2x2 pixels */
1520 if( w < 2 || h < 2 )
1522 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" is smaller than 2x2 pixels\n", indexMapFilename );
1523 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1528 /* create a new index map */
1529 im = safe_malloc( sizeof( *im ) );
1530 memset( im, 0, sizeof( *im ) );
1535 im->numLayers = numLayers;
1536 strcpy( im->name, indexMapFilename );
1537 strcpy( im->shader, shader );
1538 im->pixels = pixels;
1540 /* get height offsets */
1541 value = ValueForKey( mapEnt, "_offsets" );
1542 if( value[ 0 ] == '\0' )
1543 value = ValueForKey( e, "offsets" );
1544 if( value[ 0 ] != '\0' )
1546 /* value is a space-seperated set of numbers */
1547 strcpy( offset, value );
1550 /* get each value */
1551 for( i = 0; i < 256 && *search != '\0'; i++ )
1553 space = strstr( search, " " );
1556 im->offsets[ i ] = atof( search );
1563 /* store the index map in every brush/patch in the entity */
1564 for( b = e->brushes; b != NULL; b = b->next )
1566 for( p = e->patches; p != NULL; p = p->next )
1578 parses a single entity out of a map file
1581 static qboolean ParseMapEntity( qboolean onlyLights, qboolean noCollapseGroups )
1584 const char *classname, *value;
1585 float lightmapScale, shadeAngle;
1586 int lightmapSampleSize;
1587 char shader[ MAX_QPATH ];
1588 shaderInfo_t *celShader = NULL;
1592 int castShadows, recvShadows;
1596 if( !GetToken( qtrue ) )
1599 /* conformance check */
1600 if( strcmp( token, "{" ) )
1602 Sys_Printf( "WARNING: ParseEntity: { not found, found %s on line %d - last entity was at: <%4.2f, %4.2f, %4.2f>...\n"
1603 "Continuing to process map, but resulting BSP may be invalid.\n",
1604 token, scriptline, entities[ numEntities ].origin[ 0 ], entities[ numEntities ].origin[ 1 ], entities[ numEntities ].origin[ 2 ] );
1609 if( numEntities >= MAX_MAP_ENTITIES )
1610 Error( "numEntities == MAX_MAP_ENTITIES" );
1613 entitySourceBrushes = 0;
1614 mapEnt = &entities[ numEntities ];
1616 memset( mapEnt, 0, sizeof( *mapEnt ) );
1618 /* ydnar: true entity numbering */
1619 mapEnt->mapEntityNum = numMapEntities;
1625 /* get initial token */
1626 if( !GetToken( qtrue ) )
1628 Sys_Printf( "WARNING: ParseEntity: EOF without closing brace\n"
1629 "Continuing to process map, but resulting BSP may be invalid.\n" );
1633 if( !strcmp( token, "}" ) )
1636 if( !strcmp( token, "{" ) )
1638 /* parse a brush or patch */
1639 if( !GetToken( qtrue ) )
1643 if( !strcmp( token, "patchDef2" ) )
1646 ParsePatch( onlyLights );
1648 else if( !strcmp( token, "terrainDef" ) )
1651 Sys_Printf( "WARNING: Terrain entity parsing not supported in this build.\n" ); /* ydnar */
1653 else if( !strcmp( token, "brushDef" ) )
1655 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
1656 Error( "Old brush format not allowed in new brush format map" );
1657 g_bBrushPrimit = BPRIMIT_NEWBRUSHES;
1659 /* parse brush primitive */
1660 ParseBrush( onlyLights, noCollapseGroups );
1664 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
1665 Error( "New brush format not allowed in old brush format map" );
1666 g_bBrushPrimit = BPRIMIT_OLDBRUSHES;
1668 /* parse old brush format */
1670 ParseBrush( onlyLights, noCollapseGroups );
1672 entitySourceBrushes++;
1676 /* parse a key / value pair */
1679 /* ydnar: 2002-07-06 fixed wolf bug with empty epairs */
1680 if( ep->key[ 0 ] != '\0' && ep->value[ 0 ] != '\0' )
1682 ep->next = mapEnt->epairs;
1683 mapEnt->epairs = ep;
1688 /* ydnar: get classname */
1689 classname = ValueForKey( mapEnt, "classname" );
1691 /* ydnar: only lights? */
1692 if( onlyLights && Q_strncasecmp( classname, "light", 5 ) )
1698 /* ydnar: determine if this is a func_group */
1699 if( !Q_stricmp( "func_group", classname ) )
1704 /* worldspawn (and func_groups) default to cast/recv shadows in worldspawn group */
1705 if( funcGroup || mapEnt->mapEntityNum == 0 )
1707 //% Sys_Printf( "World: %d\n", mapEnt->mapEntityNum );
1708 castShadows = WORLDSPAWN_CAST_SHADOWS;
1709 recvShadows = WORLDSPAWN_RECV_SHADOWS;
1712 /* other entities don't cast any shadows, but recv worldspawn shadows */
1715 //% Sys_Printf( "Entity: %d\n", mapEnt->mapEntityNum );
1716 castShadows = ENTITY_CAST_SHADOWS;
1717 recvShadows = ENTITY_RECV_SHADOWS;
1720 /* get explicit shadow flags */
1721 GetEntityShadowFlags( mapEnt, NULL, &castShadows, &recvShadows );
1723 /* vortex: added _ls key (short name of lightmapscale) */
1724 /* ydnar: get lightmap scaling value for this entity */
1725 lightmapScale = 0.0f;
1726 if( strcmp( "", ValueForKey( mapEnt, "lightmapscale" ) ) ||
1727 strcmp( "", ValueForKey( mapEnt, "_lightmapscale" ) ) ||
1728 strcmp( "", ValueForKey( mapEnt, "_ls" ) ) )
1730 /* get lightmap scale from entity */
1731 lightmapScale = FloatForKey( mapEnt, "lightmapscale" );
1732 if( lightmapScale <= 0.0f )
1733 lightmapScale = FloatForKey( mapEnt, "_lightmapscale" );
1734 if( lightmapScale <= 0.0f )
1735 lightmapScale = FloatForKey( mapEnt, "_ls" );
1736 if( lightmapScale < 0.0f )
1737 lightmapScale = 0.0f;
1738 if( lightmapScale > 0.0f )
1739 Sys_Printf( "Entity %d (%s) has lightmap scale of %.4f\n", mapEnt->mapEntityNum, classname, lightmapScale );
1742 /* ydnar: get cel shader :) for this entity */
1743 value = ValueForKey( mapEnt, "_celshader" );
1744 if( value[ 0 ] == '\0' )
1745 value = ValueForKey( &entities[ 0 ], "_celshader" );
1746 if( value[ 0 ] != '\0' )
1748 if(strcmp(value, "none"))
1750 sprintf( shader, "textures/%s", value );
1751 celShader = ShaderInfoForShader( shader );
1752 Sys_Printf( "Entity %d (%s) has cel shader %s\n", mapEnt->mapEntityNum, classname, celShader->shader );
1760 celShader = (*globalCelShader ? ShaderInfoForShader(globalCelShader) : NULL);
1762 /* jal : entity based _shadeangle */
1764 if ( strcmp( "", ValueForKey( mapEnt, "_shadeangle" ) ) )
1765 shadeAngle = FloatForKey( mapEnt, "_shadeangle" );
1766 /* vortex' aliases */
1767 else if ( strcmp( "", ValueForKey( mapEnt, "_smoothnormals" ) ) )
1768 shadeAngle = FloatForKey( mapEnt, "_smoothnormals" );
1769 else if ( strcmp( "", ValueForKey( mapEnt, "_sn" ) ) )
1770 shadeAngle = FloatForKey( mapEnt, "_sn" );
1771 else if ( strcmp( "", ValueForKey( mapEnt, "_smooth" ) ) )
1772 shadeAngle = FloatForKey( mapEnt, "_smooth" );
1774 if( shadeAngle < 0.0f )
1777 if( shadeAngle > 0.0f )
1778 Sys_Printf( "Entity %d (%s) has shading angle of %.4f\n", mapEnt->mapEntityNum, classname, shadeAngle );
1780 /* jal : entity based _samplesize */
1781 lightmapSampleSize = 0;
1782 if ( strcmp( "", ValueForKey( mapEnt, "_lightmapsamplesize" ) ) )
1783 lightmapSampleSize = IntForKey( mapEnt, "_lightmapsamplesize" );
1784 else if ( strcmp( "", ValueForKey( mapEnt, "_samplesize" ) ) )
1785 lightmapSampleSize = IntForKey( mapEnt, "_samplesize" );
1787 if( lightmapSampleSize < 0 )
1788 lightmapSampleSize = 0;
1790 if( lightmapSampleSize > 0 )
1791 Sys_Printf( "Entity %d (%s) has lightmap sample size of %d\n", mapEnt->mapEntityNum, classname, lightmapSampleSize );
1793 /* attach stuff to everything in the entity */
1794 for( brush = mapEnt->brushes; brush != NULL; brush = brush->next )
1796 brush->entityNum = mapEnt->mapEntityNum;
1797 brush->castShadows = castShadows;
1798 brush->recvShadows = recvShadows;
1799 brush->lightmapSampleSize = lightmapSampleSize;
1800 brush->lightmapScale = lightmapScale;
1801 brush->celShader = celShader;
1802 brush->shadeAngleDegrees = shadeAngle;
1805 for( patch = mapEnt->patches; patch != NULL; patch = patch->next )
1807 patch->entityNum = mapEnt->mapEntityNum;
1808 patch->castShadows = castShadows;
1809 patch->recvShadows = recvShadows;
1810 patch->lightmapSampleSize = lightmapSampleSize;
1811 patch->lightmapScale = lightmapScale;
1812 patch->celShader = celShader;
1815 /* ydnar: gs mods: set entity bounds */
1816 SetEntityBounds( mapEnt );
1818 /* ydnar: gs mods: load shader index map (equivalent to old terrain alphamap) */
1819 LoadEntityIndexMap( mapEnt );
1821 /* get entity origin and adjust brushes */
1822 GetVectorForKey( mapEnt, "origin", mapEnt->origin );
1823 if( mapEnt->originbrush_origin[ 0 ] || mapEnt->originbrush_origin[ 1 ] || mapEnt->originbrush_origin[ 2 ] )
1824 AdjustBrushesForOrigin( mapEnt );
1826 /* group_info entities are just for editor grouping (fixme: leak!) */
1827 if( !noCollapseGroups && !Q_stricmp( "group_info", classname ) )
1833 /* group entities are just for editor convenience, toss all brushes into worldspawn */
1834 if( !noCollapseGroups && funcGroup )
1836 MoveBrushesToWorld( mapEnt );
1849 loads a map file into a list of entities
1852 void LoadMapFile( char *filename, qboolean onlyLights, qboolean noCollapseGroups )
1856 int oldNumEntities = 0, numMapBrushes;
1860 Sys_FPrintf( SYS_VRB, "--- LoadMapFile ---\n" );
1861 Sys_Printf( "Loading %s\n", filename );
1864 file = SafeOpenRead( filename );
1867 /* load the map file */
1868 LoadScriptFile( filename, -1 );
1872 oldNumEntities = numEntities;
1877 numMapDrawSurfs = 0;
1879 g_bBrushPrimit = BPRIMIT_UNDEFINED;
1881 /* allocate a very large temporary brush for building the brushes as they are loaded */
1882 buildBrush = AllocBrush( MAX_BUILD_SIDES );
1884 /* parse the map file */
1885 while( ParseMapEntity( onlyLights, noCollapseGroups ) );
1890 /* emit some statistics */
1891 Sys_FPrintf( SYS_VRB, "%9d light entities\n", numEntities - oldNumEntities );
1895 /* set map bounds */
1896 ClearBounds( mapMins, mapMaxs );
1897 for( b = entities[ 0 ].brushes; b; b = b->next )
1899 AddPointToBounds( b->mins, mapMins, mapMaxs );
1900 AddPointToBounds( b->maxs, mapMins, mapMaxs );
1903 /* get brush counts */
1904 numMapBrushes = CountBrushList( entities[ 0 ].brushes );
1905 if( (float) c_detail / (float) numMapBrushes < 0.10f && numMapBrushes > 500 )
1906 Sys_Printf( "WARNING: Over 90 percent structural map detected. Compile time may be adversely affected.\n" );
1908 /* emit some statistics */
1909 Sys_FPrintf( SYS_VRB, "%9d total world brushes\n", numMapBrushes );
1910 Sys_FPrintf( SYS_VRB, "%9d detail brushes\n", c_detail );
1911 Sys_FPrintf( SYS_VRB, "%9d patches\n", numMapPatches);
1912 Sys_FPrintf( SYS_VRB, "%9d boxbevels\n", c_boxbevels);
1913 Sys_FPrintf( SYS_VRB, "%9d edgebevels\n", c_edgebevels);
1914 Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );
1915 Sys_FPrintf( SYS_VRB, "%9d planes\n", nummapplanes);
1916 Sys_Printf( "%9d areaportals\n", c_areaportals);
1917 Sys_Printf( "Size: %5.0f, %5.0f, %5.0f to %5.0f, %5.0f, %5.0f\n",
1918 mapMins[ 0 ], mapMins[ 1 ], mapMins[ 2 ],
1919 mapMaxs[ 0 ], mapMaxs[ 1 ], mapMaxs[ 2 ]);
1921 /* write bogus map */
1923 WriteBSPBrushMap( "fakemap.map", entities[ 0 ].brushes );