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 )
1199 /* parse the brush out of the map */
1200 ParseRawBrush( onlyLights );
1202 /* only go this far? */
1206 /* set some defaults */
1207 buildBrush->portalareas[ 0 ] = -1;
1208 buildBrush->portalareas[ 1 ] = -1;
1209 buildBrush->entityNum = numMapEntities - 1;
1210 buildBrush->brushNum = entitySourceBrushes;
1212 /* if there are mirrored planes, the entire brush is invalid */
1213 if( !RemoveDuplicateBrushPlanes( buildBrush ) )
1216 /* get the content for the entire brush */
1217 SetBrushContents( buildBrush );
1219 /* allow detail brushes to be removed */
1220 if( nodetail && (buildBrush->compileFlags & C_DETAIL) )
1222 //% FreeBrush( buildBrush );
1226 /* allow liquid brushes to be removed */
1227 if( nowater && (buildBrush->compileFlags & C_LIQUID ) )
1229 //% FreeBrush( buildBrush );
1233 /* ydnar: allow hint brushes to be removed */
1234 if( noHint && (buildBrush->compileFlags & C_HINT) )
1236 //% FreeBrush( buildBrush );
1240 /* finish the brush */
1241 FinishBrush(noCollapseGroups);
1247 MoveBrushesToWorld()
1248 takes all of the brushes from the current entity and
1249 adds them to the world's brush list
1250 (used by func_group)
1253 void AdjustBrushesForOrigin( entity_t *ent );
1254 void MoveBrushesToWorld( entity_t *ent )
1259 /* we need to undo the common/origin adjustment, and instead shift them by the entity key origin */
1260 VectorScale(ent->origin, -1, ent->originbrush_origin);
1261 AdjustBrushesForOrigin(ent);
1262 VectorClear(ent->originbrush_origin);
1265 for( b = ent->brushes; b != NULL; b = next )
1267 /* get next brush */
1270 /* link opaque brushes to head of list, translucent brushes to end */
1271 if( b->opaque || entities[ 0 ].lastBrush == NULL )
1273 b->next = entities[ 0 ].brushes;
1274 entities[ 0 ].brushes = b;
1275 if( entities[ 0 ].lastBrush == NULL )
1276 entities[ 0 ].lastBrush = b;
1281 entities[ 0 ].lastBrush->next = b;
1282 entities[ 0 ].lastBrush = b;
1285 ent->brushes = NULL;
1287 /* ydnar: move colormod brushes */
1288 if( ent->colorModBrushes != NULL )
1290 for( b = ent->colorModBrushes; b->nextColorModBrush != NULL; b = b->nextColorModBrush );
1292 b->nextColorModBrush = entities[ 0 ].colorModBrushes;
1293 entities[ 0 ].colorModBrushes = ent->colorModBrushes;
1295 ent->colorModBrushes = NULL;
1299 if( ent->patches != NULL )
1301 for( pm = ent->patches; pm->next != NULL; pm = pm->next );
1303 pm->next = entities[ 0 ].patches;
1304 entities[ 0 ].patches = ent->patches;
1306 ent->patches = NULL;
1313 AdjustBrushesForOrigin()
1316 void AdjustBrushesForOrigin( entity_t *ent )
1325 /* walk brush list */
1326 for( b = ent->brushes; b != NULL; b = b->next )
1328 /* offset brush planes */
1329 for( i = 0; i < b->numsides; i++)
1331 /* get brush side */
1334 /* offset side plane */
1335 newdist = mapplanes[ s->planenum ].dist - DotProduct( mapplanes[ s->planenum ].normal, ent->originbrush_origin );
1337 /* find a new plane */
1338 s->planenum = FindFloatPlane( mapplanes[ s->planenum ].normal, newdist, 0, NULL );
1341 /* rebuild brush windings (ydnar: just offsetting the winding above should be fine) */
1342 CreateBrushWindings( b );
1345 /* walk patch list */
1346 for( p = ent->patches; p != NULL; p = p->next )
1348 for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )
1349 VectorSubtract( p->mesh.verts[ i ].xyz, ent->originbrush_origin, p->mesh.verts[ i ].xyz );
1356 SetEntityBounds() - ydnar
1357 finds the bounds of an entity's brushes (necessary for terrain-style generic metashaders)
1360 void SetEntityBounds( entity_t *e )
1371 /* walk the entity's brushes/patches and determine bounds */
1372 ClearBounds( mins, maxs );
1373 for( b = e->brushes; b; b = b->next )
1375 AddPointToBounds( b->mins, mins, maxs );
1376 AddPointToBounds( b->maxs, mins, maxs );
1378 for( p = e->patches; p; p = p->next )
1380 for( i = 0; i < (p->mesh.width * p->mesh.height); i++ )
1381 AddPointToBounds( p->mesh.verts[ i ].xyz, mins, maxs );
1384 /* try to find explicit min/max key */
1385 value = ValueForKey( e, "min" );
1386 if( value[ 0 ] != '\0' )
1387 GetVectorForKey( e, "min", mins );
1388 value = ValueForKey( e, "max" );
1389 if( value[ 0 ] != '\0' )
1390 GetVectorForKey( e, "max", maxs );
1392 /* store the bounds */
1393 for( b = e->brushes; b; b = b->next )
1395 VectorCopy( mins, b->eMins );
1396 VectorCopy( maxs, b->eMaxs );
1398 for( p = e->patches; p; p = p->next )
1400 VectorCopy( mins, p->eMins );
1401 VectorCopy( maxs, p->eMaxs );
1408 LoadEntityIndexMap() - ydnar
1409 based on LoadAlphaMap() from terrain.c, a little more generic
1412 void LoadEntityIndexMap( entity_t *e )
1414 int i, size, numLayers, w, h;
1415 const char *value, *indexMapFilename, *shader;
1416 char ext[ MAX_QPATH ], offset[ 4096 ], *search, *space;
1418 unsigned int *pixels32;
1424 /* this only works with bmodel ents */
1425 if( e->brushes == NULL && e->patches == NULL )
1428 /* determine if there is an index map (support legacy "alphamap" key as well) */
1429 value = ValueForKey( e, "_indexmap" );
1430 if( value[ 0 ] == '\0' )
1431 value = ValueForKey( e, "alphamap" );
1432 if( value[ 0 ] == '\0' )
1434 indexMapFilename = value;
1436 /* get number of layers (support legacy "layers" key as well) */
1437 value = ValueForKey( e, "_layers" );
1438 if( value[ 0 ] == '\0' )
1439 value = ValueForKey( e, "layers" );
1440 if( value[ 0 ] == '\0' )
1442 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_layers\" or \"layers\" key\n", indexMapFilename );
1443 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1446 numLayers = atoi( value );
1449 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has < 1 layer (%d)\n", indexMapFilename, numLayers );
1450 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1454 /* get base shader name (support legacy "shader" key as well) */
1455 value = ValueForKey( mapEnt, "_shader" );
1456 if( value[ 0 ] == '\0' )
1457 value = ValueForKey( e, "shader" );
1458 if( value[ 0 ] == '\0' )
1460 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_shader\" or \"shader\" key\n", indexMapFilename );
1461 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1467 Sys_FPrintf( SYS_VRB, "Entity %d (%s) has shader index map \"%s\"\n", mapEnt->mapEntityNum, ValueForKey( e, "classname" ), indexMapFilename );
1469 /* get index map file extension */
1470 ExtractFileExtension( indexMapFilename, ext );
1472 /* handle tga image */
1473 if( !Q_stricmp( ext, "tga" ) )
1476 Load32BitImage( indexMapFilename, &pixels32, &w, &h );
1478 /* convert to bytes */
1480 pixels = safe_malloc( size );
1481 for( i = 0; i < size; i++ )
1483 pixels[ i ] = ((pixels32[ i ] & 0xFF) * numLayers) / 256;
1484 if( pixels[ i ] >= numLayers )
1485 pixels[ i ] = numLayers - 1;
1488 /* free the 32 bit image */
1494 Load256Image( indexMapFilename, &pixels, NULL, &w, &h );
1497 //% Sys_Printf( "-------------------------------" );
1499 /* fix up out-of-range values */
1501 for( i = 0; i < size; i++ )
1503 if( pixels[ i ] >= numLayers )
1504 pixels[ i ] = numLayers - 1;
1507 //% if( (i % w) == 0 )
1508 //% Sys_Printf( "\n" );
1509 //% Sys_Printf( "%c", pixels[ i ] + '0' );
1513 //% Sys_Printf( "\n-------------------------------\n" );
1516 /* the index map must be at least 2x2 pixels */
1517 if( w < 2 || h < 2 )
1519 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" is smaller than 2x2 pixels\n", indexMapFilename );
1520 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1525 /* create a new index map */
1526 im = safe_malloc( sizeof( *im ) );
1527 memset( im, 0, sizeof( *im ) );
1532 im->numLayers = numLayers;
1533 strcpy( im->name, indexMapFilename );
1534 strcpy( im->shader, shader );
1535 im->pixels = pixels;
1537 /* get height offsets */
1538 value = ValueForKey( mapEnt, "_offsets" );
1539 if( value[ 0 ] == '\0' )
1540 value = ValueForKey( e, "offsets" );
1541 if( value[ 0 ] != '\0' )
1543 /* value is a space-seperated set of numbers */
1544 strcpy( offset, value );
1547 /* get each value */
1548 for( i = 0; i < 256 && *search != '\0'; i++ )
1550 space = strstr( search, " " );
1553 im->offsets[ i ] = atof( search );
1560 /* store the index map in every brush/patch in the entity */
1561 for( b = e->brushes; b != NULL; b = b->next )
1563 for( p = e->patches; p != NULL; p = p->next )
1575 parses a single entity out of a map file
1578 static qboolean ParseMapEntity( qboolean onlyLights, qboolean noCollapseGroups )
1581 const char *classname, *value;
1582 float lightmapScale, shadeAngle;
1583 int lightmapSampleSize;
1584 char shader[ MAX_QPATH ];
1585 shaderInfo_t *celShader = NULL;
1589 int castShadows, recvShadows;
1593 if( !GetToken( qtrue ) )
1596 /* conformance check */
1597 if( strcmp( token, "{" ) )
1599 Sys_Printf( "WARNING: ParseEntity: { not found, found %s on line %d - last entity was at: <%4.2f, %4.2f, %4.2f>...\n"
1600 "Continuing to process map, but resulting BSP may be invalid.\n",
1601 token, scriptline, entities[ numEntities ].origin[ 0 ], entities[ numEntities ].origin[ 1 ], entities[ numEntities ].origin[ 2 ] );
1606 AUTOEXPAND_BY_REALLOC(entities, numEntities, allocatedEntities, 32);
1609 entitySourceBrushes = 0;
1610 mapEnt = &entities[ numEntities ];
1612 memset( mapEnt, 0, sizeof( *mapEnt ) );
1614 /* ydnar: true entity numbering */
1615 mapEnt->mapEntityNum = numMapEntities;
1621 /* get initial token */
1622 if( !GetToken( qtrue ) )
1624 Sys_Printf( "WARNING: ParseEntity: EOF without closing brace\n"
1625 "Continuing to process map, but resulting BSP may be invalid.\n" );
1629 if( !strcmp( token, "}" ) )
1632 if( !strcmp( token, "{" ) )
1634 /* parse a brush or patch */
1635 if( !GetToken( qtrue ) )
1639 if( !strcmp( token, "patchDef2" ) )
1642 ParsePatch( onlyLights );
1644 else if( !strcmp( token, "terrainDef" ) )
1647 Sys_Printf( "WARNING: Terrain entity parsing not supported in this build.\n" ); /* ydnar */
1649 else if( !strcmp( token, "brushDef" ) )
1651 if( g_bBrushPrimit == BPRIMIT_OLDBRUSHES )
1652 Error( "Old brush format not allowed in new brush format map" );
1653 g_bBrushPrimit = BPRIMIT_NEWBRUSHES;
1655 /* parse brush primitive */
1656 ParseBrush( onlyLights, noCollapseGroups );
1660 if( g_bBrushPrimit == BPRIMIT_NEWBRUSHES )
1661 Error( "New brush format not allowed in old brush format map" );
1662 g_bBrushPrimit = BPRIMIT_OLDBRUSHES;
1664 /* parse old brush format */
1666 ParseBrush( onlyLights, noCollapseGroups );
1668 entitySourceBrushes++;
1672 /* parse a key / value pair */
1675 /* ydnar: 2002-07-06 fixed wolf bug with empty epairs */
1676 if( ep->key[ 0 ] != '\0' && ep->value[ 0 ] != '\0' )
1678 ep->next = mapEnt->epairs;
1679 mapEnt->epairs = ep;
1684 /* ydnar: get classname */
1685 classname = ValueForKey( mapEnt, "classname" );
1687 /* ydnar: only lights? */
1688 if( onlyLights && Q_strncasecmp( classname, "light", 5 ) )
1694 /* ydnar: determine if this is a func_group */
1695 if( !Q_stricmp( "func_group", classname ) )
1700 /* worldspawn (and func_groups) default to cast/recv shadows in worldspawn group */
1701 if( funcGroup || mapEnt->mapEntityNum == 0 )
1703 //% Sys_Printf( "World: %d\n", mapEnt->mapEntityNum );
1704 castShadows = WORLDSPAWN_CAST_SHADOWS;
1705 recvShadows = WORLDSPAWN_RECV_SHADOWS;
1708 /* other entities don't cast any shadows, but recv worldspawn shadows */
1711 //% Sys_Printf( "Entity: %d\n", mapEnt->mapEntityNum );
1712 castShadows = ENTITY_CAST_SHADOWS;
1713 recvShadows = ENTITY_RECV_SHADOWS;
1716 /* get explicit shadow flags */
1717 GetEntityShadowFlags( mapEnt, NULL, &castShadows, &recvShadows );
1719 /* vortex: added _ls key (short name of lightmapscale) */
1720 /* ydnar: get lightmap scaling value for this entity */
1721 lightmapScale = 0.0f;
1722 if( strcmp( "", ValueForKey( mapEnt, "lightmapscale" ) ) ||
1723 strcmp( "", ValueForKey( mapEnt, "_lightmapscale" ) ) ||
1724 strcmp( "", ValueForKey( mapEnt, "_ls" ) ) )
1726 /* get lightmap scale from entity */
1727 lightmapScale = FloatForKey( mapEnt, "lightmapscale" );
1728 if( lightmapScale <= 0.0f )
1729 lightmapScale = FloatForKey( mapEnt, "_lightmapscale" );
1730 if( lightmapScale <= 0.0f )
1731 lightmapScale = FloatForKey( mapEnt, "_ls" );
1732 if( lightmapScale < 0.0f )
1733 lightmapScale = 0.0f;
1734 if( lightmapScale > 0.0f )
1735 Sys_Printf( "Entity %d (%s) has lightmap scale of %.4f\n", mapEnt->mapEntityNum, classname, lightmapScale );
1738 /* ydnar: get cel shader :) for this entity */
1739 value = ValueForKey( mapEnt, "_celshader" );
1740 if( value[ 0 ] == '\0' )
1741 value = ValueForKey( &entities[ 0 ], "_celshader" );
1742 if( value[ 0 ] != '\0' )
1744 if(strcmp(value, "none"))
1746 sprintf( shader, "textures/%s", value );
1747 celShader = ShaderInfoForShader( shader );
1748 Sys_Printf( "Entity %d (%s) has cel shader %s\n", mapEnt->mapEntityNum, classname, celShader->shader );
1756 celShader = (*globalCelShader ? ShaderInfoForShader(globalCelShader) : NULL);
1758 /* jal : entity based _shadeangle */
1760 if ( strcmp( "", ValueForKey( mapEnt, "_shadeangle" ) ) )
1761 shadeAngle = FloatForKey( mapEnt, "_shadeangle" );
1762 /* vortex' aliases */
1763 else if ( strcmp( "", ValueForKey( mapEnt, "_smoothnormals" ) ) )
1764 shadeAngle = FloatForKey( mapEnt, "_smoothnormals" );
1765 else if ( strcmp( "", ValueForKey( mapEnt, "_sn" ) ) )
1766 shadeAngle = FloatForKey( mapEnt, "_sn" );
1767 else if ( strcmp( "", ValueForKey( mapEnt, "_smooth" ) ) )
1768 shadeAngle = FloatForKey( mapEnt, "_smooth" );
1770 if( shadeAngle < 0.0f )
1773 if( shadeAngle > 0.0f )
1774 Sys_Printf( "Entity %d (%s) has shading angle of %.4f\n", mapEnt->mapEntityNum, classname, shadeAngle );
1776 /* jal : entity based _samplesize */
1777 lightmapSampleSize = 0;
1778 if ( strcmp( "", ValueForKey( mapEnt, "_lightmapsamplesize" ) ) )
1779 lightmapSampleSize = IntForKey( mapEnt, "_lightmapsamplesize" );
1780 else if ( strcmp( "", ValueForKey( mapEnt, "_samplesize" ) ) )
1781 lightmapSampleSize = IntForKey( mapEnt, "_samplesize" );
1783 if( lightmapSampleSize < 0 )
1784 lightmapSampleSize = 0;
1786 if( lightmapSampleSize > 0 )
1787 Sys_Printf( "Entity %d (%s) has lightmap sample size of %d\n", mapEnt->mapEntityNum, classname, lightmapSampleSize );
1789 /* attach stuff to everything in the entity */
1790 for( brush = mapEnt->brushes; brush != NULL; brush = brush->next )
1792 brush->entityNum = mapEnt->mapEntityNum;
1793 brush->castShadows = castShadows;
1794 brush->recvShadows = recvShadows;
1795 brush->lightmapSampleSize = lightmapSampleSize;
1796 brush->lightmapScale = lightmapScale;
1797 brush->celShader = celShader;
1798 brush->shadeAngleDegrees = shadeAngle;
1801 for( patch = mapEnt->patches; patch != NULL; patch = patch->next )
1803 patch->entityNum = mapEnt->mapEntityNum;
1804 patch->castShadows = castShadows;
1805 patch->recvShadows = recvShadows;
1806 patch->lightmapSampleSize = lightmapSampleSize;
1807 patch->lightmapScale = lightmapScale;
1808 patch->celShader = celShader;
1811 /* ydnar: gs mods: set entity bounds */
1812 SetEntityBounds( mapEnt );
1814 /* ydnar: gs mods: load shader index map (equivalent to old terrain alphamap) */
1815 LoadEntityIndexMap( mapEnt );
1817 /* get entity origin and adjust brushes */
1818 GetVectorForKey( mapEnt, "origin", mapEnt->origin );
1819 if( mapEnt->originbrush_origin[ 0 ] || mapEnt->originbrush_origin[ 1 ] || mapEnt->originbrush_origin[ 2 ] )
1820 AdjustBrushesForOrigin( mapEnt );
1822 /* group_info entities are just for editor grouping (fixme: leak!) */
1823 if( !noCollapseGroups && !Q_stricmp( "group_info", classname ) )
1829 /* group entities are just for editor convenience, toss all brushes into worldspawn */
1830 if( !noCollapseGroups && funcGroup )
1832 MoveBrushesToWorld( mapEnt );
1845 loads a map file into a list of entities
1848 void LoadMapFile( char *filename, qboolean onlyLights, qboolean noCollapseGroups )
1852 int oldNumEntities = 0, numMapBrushes;
1856 Sys_FPrintf( SYS_VRB, "--- LoadMapFile ---\n" );
1857 Sys_Printf( "Loading %s\n", filename );
1860 file = SafeOpenRead( filename );
1863 /* load the map file */
1864 LoadScriptFile( filename, -1 );
1868 oldNumEntities = numEntities;
1873 numMapDrawSurfs = 0;
1875 g_bBrushPrimit = BPRIMIT_UNDEFINED;
1877 /* allocate a very large temporary brush for building the brushes as they are loaded */
1878 buildBrush = AllocBrush( MAX_BUILD_SIDES );
1880 /* parse the map file */
1881 while( ParseMapEntity( onlyLights, noCollapseGroups ) );
1886 /* emit some statistics */
1887 Sys_FPrintf( SYS_VRB, "%9d light entities\n", numEntities - oldNumEntities );
1891 /* set map bounds */
1892 ClearBounds( mapMins, mapMaxs );
1893 for( b = entities[ 0 ].brushes; b; b = b->next )
1895 AddPointToBounds( b->mins, mapMins, mapMaxs );
1896 AddPointToBounds( b->maxs, mapMins, mapMaxs );
1899 /* get brush counts */
1900 numMapBrushes = CountBrushList( entities[ 0 ].brushes );
1901 if( (float) c_detail / (float) numMapBrushes < 0.10f && numMapBrushes > 500 )
1902 Sys_Printf( "WARNING: Over 90 percent structural map detected. Compile time may be adversely affected.\n" );
1904 /* emit some statistics */
1905 Sys_FPrintf( SYS_VRB, "%9d total world brushes\n", numMapBrushes );
1906 Sys_FPrintf( SYS_VRB, "%9d detail brushes\n", c_detail );
1907 Sys_FPrintf( SYS_VRB, "%9d patches\n", numMapPatches);
1908 Sys_FPrintf( SYS_VRB, "%9d boxbevels\n", c_boxbevels);
1909 Sys_FPrintf( SYS_VRB, "%9d edgebevels\n", c_edgebevels);
1910 Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );
1911 Sys_FPrintf( SYS_VRB, "%9d planes\n", nummapplanes);
1912 Sys_Printf( "%9d areaportals\n", c_areaportals);
1913 Sys_Printf( "Size: %5.0f, %5.0f, %5.0f to %5.0f, %5.0f, %5.0f\n",
1914 mapMins[ 0 ], mapMins[ 1 ], mapMins[ 2 ],
1915 mapMaxs[ 0 ], mapMaxs[ 1 ], mapMaxs[ 2 ]);
1917 /* write bogus map */
1919 WriteBSPBrushMap( "fakemap.map", entities[ 0 ].brushes );