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 ){
66 /* get local copies */
71 // We check equality of each component since we're using '<', not '<='
72 // (the epsilons may be zero). We want to use '<' intead of '<=' to be
73 // consistent with the true meaning of "epsilon", and also because other
74 // parts of the code uses this inequality.
75 if ( ( p->dist == dist || fabs( p->dist - dist ) < de ) &&
76 ( p->normal[0] == normal[0] || fabs( p->normal[0] - normal[0] ) < ne ) &&
77 ( p->normal[1] == normal[1] || fabs( p->normal[1] - normal[1] ) < ne ) &&
78 ( p->normal[2] == normal[2] || fabs( p->normal[2] - normal[2] ) < ne ) ) {
92 void AddPlaneToHash( plane_t *p ){
96 hash = ( PLANE_HASHES - 1 ) & (int) fabs( p->dist );
98 p->hash_chain = planehash[hash];
99 planehash[hash] = p - mapplanes + 1;
107 int CreateNewFloatPlane( vec3_t normal, vec_t dist ){
110 if ( VectorLength( normal ) < 0.5 ) {
111 Sys_Printf( "FloatPlane: bad normal\n" );
115 // create a new plane
116 AUTOEXPAND_BY_REALLOC( mapplanes, nummapplanes + 1, allocatedmapplanes, 1024 );
118 p = &mapplanes[nummapplanes];
119 VectorCopy( normal, p->normal );
121 p->type = ( p + 1 )->type = PlaneTypeForNormal( p->normal );
123 VectorSubtract( vec3_origin, normal, ( p + 1 )->normal );
124 ( p + 1 )->dist = -dist;
128 // allways put axial planes facing positive first
130 if ( p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0 ) {
137 AddPlaneToHash( p + 1 );
138 return nummapplanes - 1;
143 AddPlaneToHash( p + 1 );
144 return nummapplanes - 2;
151 Snaps a near-axial normal vector.
152 Returns qtrue if and only if the normal was adjusted.
155 qboolean SnapNormal( vec3_t normal ){
156 #if Q3MAP2_EXPERIMENTAL_SNAP_NORMAL_FIX
158 qboolean adjusted = qfalse;
160 // A change from the original SnapNormal() is that we snap each
161 // component that's close to 0. So for example if a normal is
162 // (0.707, 0.707, 0.0000001), it will get snapped to lie perfectly in the
163 // XY plane (its Z component will be set to 0 and its length will be
164 // normalized). The original SnapNormal() didn't snap such vectors - it
165 // only snapped vectors that were near a perfect axis.
167 for ( i = 0; i < 3; i++ )
169 if ( normal[i] != 0.0 && -normalEpsilon < normal[i] && normal[i] < normalEpsilon ) {
176 VectorNormalize( normal, normal );
183 // I would suggest that you uncomment the following code and look at the
187 Sys_Printf("normalEpsilon is %f\n", normalEpsilon);
192 normal[2] = i * 0.000001;
193 VectorNormalize(normal, normal);
194 if (1.0 - normal[0] >= normalEpsilon) {
195 Sys_Printf("(%f %f %f)\n", normal[0], normal[1], normal[2]);
196 Error("SnapNormal: test completed");
201 // When the normalEpsilon is 0.00001, the loop will break out when normal is
202 // (0.999990 0.000000 0.004469). In other words, this is the vector closest
203 // to axial that will NOT be snapped. Anything closer will be snaped. Now,
204 // 0.004469 is close to 1/225. The length of a circular quarter-arc of radius
205 // 1 is PI/2, or about 1.57. And 0.004469/1.57 is about 0.0028, or about
206 // 1/350. Expressed a different way, 1/350 is also about 0.26/90.
207 // This means is that a normal with an angle that is within 1/4 of a degree
208 // from axial will be "snapped". My belief is that the person who wrote the
209 // code below did not intend it this way. I think the person intended that
210 // the epsilon be measured against the vector components close to 0, not 1.0.
211 // I think the logic should be: if 2 of the normal components are within
212 // epsilon of 0, then the vector can be snapped to be perfectly axial.
213 // We may consider adjusting the epsilon to a larger value when we make this
216 for ( i = 0; i < 3; i++ )
218 if ( fabs( normal[ i ] - 1 ) < normalEpsilon ) {
219 VectorClear( normal );
223 if ( fabs( normal[ i ] - -1 ) < normalEpsilon ) {
224 VectorClear( normal );
237 snaps a plane to normal/distance epsilons
240 void SnapPlane( vec3_t normal, vec_t *dist ){
241 // SnapPlane disabled by LordHavoc because it often messes up collision
242 // brushes made from triangles of embedded models, and it has little effect
243 // on anything else (axial planes are usually derived from snapped points)
245 SnapPlane reenabled by namespace because of multiple reports of
246 q3map2-crashes which were triggered by this patch.
248 SnapNormal( normal );
250 // TODO: Rambetter has some serious comments here as well. First off,
251 // in the case where a normal is non-axial, there is nothing special
252 // about integer distances. I would think that snapping a distance might
253 // make sense for axial normals, but I'm not so sure about snapping
254 // non-axial normals. A shift by 0.01 in a plane, multiplied by a clipping
255 // against another plane that is 5 degrees off, and we introduce 0.1 error
256 // easily. A 0.1 error in a vertex is where problems start to happen, such
257 // as disappearing triangles.
259 // Second, assuming we have snapped the normal above, let's say that the
260 // plane we just snapped was defined for some points that are actually
261 // quite far away from normal * dist. Well, snapping the normal in this
262 // case means that we've just moved those points by potentially many units!
263 // Therefore, if we are going to snap the normal, we need to know the
264 // points we're snapping for so that the plane snaps with those points in
265 // mind (points remain close to the plane).
267 // I would like to know exactly which problems SnapPlane() is trying to
268 // solve so that we can better engineer it (I'm not saying that SnapPlane()
269 // should be removed altogether). Fix all this snapping code at some point!
271 if ( fabs( *dist - Q_rint( *dist ) ) < distanceEpsilon ) {
272 *dist = Q_rint( *dist );
278 snaps a plane to normal/distance epsilons, improved code
280 void SnapPlaneImproved( vec3_t normal, vec_t *dist, int numPoints, const vec3_t *points ){
283 vec_t distNearestInt;
285 if ( SnapNormal( normal ) ) {
286 if ( numPoints > 0 ) {
287 // Adjust the dist so that the provided points don't drift away.
288 VectorClear( center );
289 for ( i = 0; i < numPoints; i++ )
291 VectorAdd( center, points[i], center );
293 for ( i = 0; i < 3; i++ ) { center[i] = center[i] / numPoints; }
294 *dist = DotProduct( normal, center );
298 if ( VectorIsOnAxis( normal ) ) {
299 // Only snap distance if the normal is an axis. Otherwise there
300 // is nothing "natural" about snapping the distance to an integer.
301 distNearestInt = Q_rint( *dist );
302 if ( -distanceEpsilon < *dist - distNearestInt && *dist - distNearestInt < distanceEpsilon ) {
303 *dist = distNearestInt;
312 ydnar: changed to allow a number of test points to be supplied that
313 must be within an epsilon distance of the plane
316 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?
327 VectorCopy( innormal, normal );
328 #if Q3MAP2_EXPERIMENTAL_SNAP_PLANE_FIX
329 if ( !doingModelClip ) {
330 SnapPlaneImproved( normal, &dist, numPoints, (const vec3_t *) points );
332 if ( doingModelClip && snapModelClip ) {
333 SnapPlane( normal, &dist );
336 SnapPlane( normal, &dist );
339 hash = ( PLANE_HASHES - 1 ) & (int) fabs( dist );
341 /* search the border bins as well */
342 for ( i = -1; i <= 1; i++ )
344 h = ( hash + i ) & ( PLANE_HASHES - 1 );
345 for ( pidx = planehash[ h ] - 1; pidx != -1; pidx = mapplanes[pidx].hash_chain - 1 )
347 p = &mapplanes[pidx];
349 /* do standard plane compare */
350 if ( !PlaneEqual( p, normal, dist ) ) {
354 /* ydnar: uncomment the following line for old-style plane finding */
355 //% return p - mapplanes;
357 /* ydnar: test supplied points against this plane */
358 for ( j = 0; j < numPoints; j++ )
360 // NOTE: When dist approaches 2^16, the resolution of 32 bit floating
361 // point number is greatly decreased. The distanceEpsilon cannot be
362 // very small when world coordinates extend to 2^16. Making the
363 // dot product here in 64 bit land will not really help the situation
364 // because the error will already be carried in dist.
365 d = DotProduct( points[ j ], p->normal ) - p->dist;
367 if ( d != 0.0 && d >= distanceEpsilon ) {
368 break; // Point is too far from plane.
372 /* found a matching plane */
373 if ( j >= numPoints ) {
374 return p - mapplanes;
379 /* none found, so create a new one */
380 return CreateNewFloatPlane( normal, dist );
390 VectorCopy( innormal, normal );
391 #if Q3MAP2_EXPERIMENTAL_SNAP_PLANE_FIX
392 SnapPlaneImproved( normal, &dist, numPoints, (const vec3_t *) points );
394 SnapPlane( normal, &dist );
396 for ( i = 0, p = mapplanes; i < nummapplanes; i++, p++ )
398 if ( !PlaneEqual( p, normal, dist ) ) {
402 /* ydnar: uncomment the following line for old-style plane finding */
405 /* ydnar: test supplied points against this plane */
406 for ( j = 0; j < numPoints; j++ )
408 d = DotProduct( points[ j ], p->normal ) - p->dist;
409 if ( fabs( d ) > distanceEpsilon ) {
414 /* found a matching plane */
415 if ( j >= numPoints ) {
418 // TODO: Note that the non-USE_HASHING code does not compute epsilons
419 // for the provided points. It should do that. I think this code
420 // is unmaintained because nobody sets USE_HASHING to off.
423 return CreateNewFloatPlane( normal, dist );
432 takes 3 points and finds the plane they lie in
435 int MapPlaneFromPoints( vec3_t *p ){
436 #if Q3MAP2_EXPERIMENTAL_HIGH_PRECISION_MATH_FIXES
437 vec3_accu_t paccu[3];
438 vec3_accu_t t1, t2, normalAccu;
442 VectorCopyRegularToAccu( p[0], paccu[0] );
443 VectorCopyRegularToAccu( p[1], paccu[1] );
444 VectorCopyRegularToAccu( p[2], paccu[2] );
446 VectorSubtractAccu( paccu[0], paccu[1], t1 );
447 VectorSubtractAccu( paccu[2], paccu[1], t2 );
448 CrossProductAccu( t1, t2, normalAccu );
449 VectorNormalizeAccu( normalAccu, normalAccu );
450 // TODO: A 32 bit float for the plane distance isn't enough resolution
451 // if the plane is 2^16 units away from the origin (the "epsilon" approaches
452 // 0.01 in that case).
453 dist = (vec_t) DotProductAccu( paccu[0], normalAccu );
454 VectorCopyAccuToRegular( normalAccu, normal );
456 return FindFloatPlane( normal, dist, 3, p );
458 vec3_t t1, t2, normal;
462 /* calc plane normal */
463 VectorSubtract( p[ 0 ], p[ 1 ], t1 );
464 VectorSubtract( p[ 2 ], p[ 1 ], t2 );
465 CrossProduct( t1, t2, normal );
466 VectorNormalize( normal, normal );
468 /* calc plane distance */
469 dist = DotProduct( p[ 0 ], normal );
471 /* store the plane */
472 return FindFloatPlane( normal, dist, 3, p );
480 the content flags and compile flags on all sides of a brush should be the same
483 void SetBrushContents( brush_t *b ){
484 int contentFlags, compileFlags;
490 /* get initial compile flags from first side */
492 contentFlags = s->contentFlags;
493 compileFlags = s->compileFlags;
494 b->contentShader = s->shaderInfo;
497 /* get the content/compile flags for every side in the brush */
498 for ( i = 1; i < b->numsides; i++, s++ )
501 if ( s->shaderInfo == NULL ) {
504 //% if( s->contentFlags != contentFlags || s->compileFlags != compileFlags )
507 contentFlags |= s->contentFlags;
508 compileFlags |= s->compileFlags;
511 /* ydnar: getting rid of this stupid warning */
513 //% Sys_FPrintf( SYS_VRB,"Entity %i, Brush %i: mixed face contentFlags\n", b->entitynum, b->brushnum );
515 /* check for detail & structural */
516 if ( ( compileFlags & C_DETAIL ) && ( compileFlags & C_STRUCTURAL ) ) {
517 xml_Select( "Mixed detail and structural (defaulting to structural)", mapEnt->mapEntityNum, entitySourceBrushes, qfalse );
518 compileFlags &= ~C_DETAIL;
521 /* the fulldetail flag will cause detail brushes to be treated like normal brushes */
523 compileFlags &= ~C_DETAIL;
526 /* all translucent brushes that aren't specifically made structural will be detail */
527 if ( ( compileFlags & C_TRANSLUCENT ) && !( compileFlags & C_STRUCTURAL ) ) {
528 compileFlags |= C_DETAIL;
532 if ( compileFlags & C_DETAIL ) {
543 if ( compileFlags & C_TRANSLUCENT ) {
551 if ( compileFlags & C_AREAPORTAL ) {
555 /* set brush flags */
556 b->contentFlags = contentFlags;
557 b->compileFlags = compileFlags;
564 adds any additional planes necessary to allow the brush being
565 built to be expanded against axial bounding boxes
566 ydnar 2003-01-20: added mrelusive fixes
569 void AddBrushBevels( void ){
571 int i, j, k, l, order;
581 // add the axial planes
584 for ( axis = 0; axis < 3; axis++ ) {
585 for ( dir = -1; dir <= 1; dir += 2, order++ ) {
586 // see if the plane is allready present
587 for ( i = 0, s = buildBrush->sides; i < buildBrush->numsides; i++, s++ )
589 /* ydnar: testing disabling of mre code */
592 if ( mapplanes[s->planenum].normal[axis] >= 0.9999f ) {
597 if ( mapplanes[s->planenum].normal[axis] <= -0.9999f ) {
602 if ( ( dir > 0 && mapplanes[ s->planenum ].normal[ axis ] == 1.0f ) ||
603 ( dir < 0 && mapplanes[ s->planenum ].normal[ axis ] == -1.0f ) ) {
609 if ( i == buildBrush->numsides ) {
611 if ( buildBrush->numsides == MAX_BUILD_SIDES ) {
612 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue );
614 memset( s, 0, sizeof( *s ) );
615 buildBrush->numsides++;
616 VectorClear( normal );
620 /* ydnar: adding bevel plane snapping for fewer bsp planes */
621 if ( bevelSnap > 0 ) {
622 dist = floor( buildBrush->maxs[ axis ] / bevelSnap ) * bevelSnap;
625 dist = buildBrush->maxs[ axis ];
630 /* ydnar: adding bevel plane snapping for fewer bsp planes */
631 if ( bevelSnap > 0 ) {
632 dist = -ceil( buildBrush->mins[ axis ] / bevelSnap ) * bevelSnap;
635 dist = -buildBrush->mins[ axis ];
639 s->planenum = FindFloatPlane( normal, dist, 0, NULL );
640 s->contentFlags = buildBrush->sides[ 0 ].contentFlags;
645 // if the plane is not in it canonical order, swap it
647 sidetemp = buildBrush->sides[order];
648 buildBrush->sides[order] = buildBrush->sides[i];
649 buildBrush->sides[i] = sidetemp;
655 // add the edge bevels
657 if ( buildBrush->numsides == 6 ) {
658 return; // pure axial
661 // test the non-axial plane edges
662 for ( i = 6; i < buildBrush->numsides; i++ ) {
663 s = buildBrush->sides + i;
668 for ( j = 0; j < w->numpoints; j++ ) {
669 k = ( j + 1 ) % w->numpoints;
670 VectorSubtract( w->p[j], w->p[k], vec );
671 if ( VectorNormalize( vec, vec ) < 0.5f ) {
675 for ( k = 0; k < 3; k++ ) {
676 if ( vec[k] == -1.0f || vec[k] == 1.0f || ( vec[k] == 0.0f && vec[( k + 1 ) % 3] == 0.0f ) ) {
681 continue; // only test non-axial edges
685 //% Sys_Printf( "-------------\n" );
687 // try the six possible slanted axials from this edge
688 for ( axis = 0; axis < 3; axis++ ) {
689 for ( dir = -1; dir <= 1; dir += 2 ) {
693 CrossProduct( vec, vec2, normal );
694 if ( VectorNormalize( normal, normal ) < 0.5f ) {
697 dist = DotProduct( w->p[j], normal );
699 // if all the points on all the sides are
700 // behind this plane, it is a proper edge bevel
701 for ( k = 0; k < buildBrush->numsides; k++ ) {
703 // if this plane has allready been used, skip it
704 if ( PlaneEqual( &mapplanes[buildBrush->sides[k].planenum], normal, dist ) ) {
708 w2 = buildBrush->sides[k].winding;
713 for ( l = 0; l < w2->numpoints; l++ ) {
714 d = DotProduct( w2->p[l], normal ) - dist;
716 break; // point in front
722 // if some point was at the front
723 if ( l != w2->numpoints ) {
727 // if no points at the back then the winding is on the bevel plane
728 if ( minBack > -0.1f ) {
729 //% Sys_Printf( "On bevel plane\n" );
734 if ( k != buildBrush->numsides ) {
735 continue; // wasn't part of the outer hull
739 //% Sys_Printf( "n = %f %f %f\n", normal[ 0 ], normal[ 1 ], normal[ 2 ] );
742 if ( buildBrush->numsides == MAX_BUILD_SIDES ) {
743 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue );
745 s2 = &buildBrush->sides[buildBrush->numsides];
746 buildBrush->numsides++;
747 memset( s2, 0, sizeof( *s2 ) );
749 s2->planenum = FindFloatPlane( normal, dist, 1, &w->p[ j ] );
750 s2->contentFlags = buildBrush->sides[0].contentFlags;
763 produces a final brush based on the buildBrush->sides array
764 and links it to the current entity
767 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 ){
786 /* create windings for sides and bounds for brush */
787 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 ) {
796 Sys_Printf( "Entity %i, Brush %i: origin brush detected\n",
797 mapEnt->mapEntityNum, entitySourceBrushes );
799 if ( numEntities == 1 ) {
800 Sys_Printf( "Entity %i, Brush %i: origin brushes not allowed in world\n",
801 mapEnt->mapEntityNum, entitySourceBrushes );
805 VectorAdd( buildBrush->mins, buildBrush->maxs, origin );
806 VectorScale( origin, 0.5, origin );
808 MergeOrigin( &entities[ numEntities - 1 ], origin );
810 /* don't keep this brush */
814 /* determine if the brush is an area portal */
815 if ( buildBrush->compileFlags & C_AREAPORTAL ) {
816 if ( numEntities != 1 ) {
817 Sys_Printf( "Entity %i, Brush %i: areaportals only allowed in world\n", numEntities - 1, entitySourceBrushes );
822 /* add bevel planes */
823 if ( !noCollapseGroups ) {
828 b = CopyBrush( buildBrush );
830 /* set map entity and brush numbering */
831 b->entityNum = mapEnt->mapEntityNum;
832 b->brushNum = entitySourceBrushes;
837 /* link opaque brushes to head of list, translucent brushes to end */
838 if ( b->opaque || mapEnt->lastBrush == NULL ) {
839 b->next = mapEnt->brushes;
841 if ( mapEnt->lastBrush == NULL ) {
842 mapEnt->lastBrush = b;
848 mapEnt->lastBrush->next = b;
849 mapEnt->lastBrush = b;
852 /* link colorMod volume brushes to the entity directly */
853 if ( b->contentShader != NULL &&
854 b->contentShader->colorMod != NULL &&
855 b->contentShader->colorMod->type == CM_VOLUME ) {
856 b->nextColorModBrush = mapEnt->colorModBrushes;
857 mapEnt->colorModBrushes = b;
860 /* return to sender */
867 TextureAxisFromPlane()
868 determines best orthagonal axis to project a texture onto a wall
869 (must be identical in radiant!)
872 vec3_t baseaxis[18] =
874 {0,0,1}, {1,0,0}, {0,-1,0}, // floor
875 {0,0,-1}, {1,0,0}, {0,-1,0}, // ceiling
876 {1,0,0}, {0,1,0}, {0,0,-1}, // west wall
877 {-1,0,0}, {0,1,0}, {0,0,-1}, // east wall
878 {0,1,0}, {1,0,0}, {0,0,-1}, // south wall
879 {0,-1,0}, {1,0,0}, {0,0,-1} // north wall
882 void TextureAxisFromPlane( plane_t *pln, vec3_t xv, vec3_t yv ){
890 for ( i = 0 ; i < 6 ; i++ )
892 dot = DotProduct( pln->normal, baseaxis[i * 3] );
893 if ( dot > best + 0.0001f ) { /* ydnar: bug 637 fix, suggested by jmonroe */
899 VectorCopy( baseaxis[bestaxis * 3 + 1], xv );
900 VectorCopy( baseaxis[bestaxis * 3 + 2], yv );
907 creates world-to-texture mapping vecs for crappy quake plane arrangements
910 void QuakeTextureVecs( plane_t *plane, vec_t shift[ 2 ], vec_t rotate, vec_t scale[ 2 ], vec_t mappingVecs[ 2 ][ 4 ] ){
913 vec_t ang, sinv, cosv;
918 TextureAxisFromPlane( plane, vecs[0], vecs[1] );
931 else if ( rotate == 90 ) {
934 else if ( rotate == 180 ) {
935 sinv = 0 ; cosv = -1;
937 else if ( rotate == 270 ) {
938 sinv = -1 ; cosv = 0;
942 ang = rotate / 180 * Q_PI;
950 else if ( vecs[0][1] ) {
960 else if ( vecs[1][1] ) {
967 for ( i = 0 ; i < 2 ; i++ ) {
968 ns = cosv * vecs[i][sv] - sinv * vecs[i][tv];
969 nt = sinv * vecs[i][sv] + cosv * vecs[i][tv];
974 for ( i = 0 ; i < 2 ; i++ )
975 for ( j = 0 ; j < 3 ; j++ )
976 mappingVecs[i][j] = vecs[i][j] / scale[i];
978 mappingVecs[0][3] = shift[0];
979 mappingVecs[1][3] = shift[1];
986 parses the sides into buildBrush->sides[], nothing else.
987 no validation, back plane removal, etc.
990 added brush epairs parsing ( ignoring actually )
992 added exclusive brush primitive parsing
994 support for old brush format back in
995 NOTE: it would be "cleaner" to have seperate functions to parse between old and new brushes
998 static void ParseRawBrush( qboolean onlyLights ){
1000 vec3_t planePoints[ 3 ];
1006 char name[ MAX_QPATH ];
1007 char shader[ MAX_QPATH ];
1012 buildBrush->numsides = 0;
1013 buildBrush->detail = qfalse;
1016 if ( g_bBrushPrimit == BPRIMIT_NEWBRUSHES ) {
1023 if ( !GetToken( qtrue ) ) {
1026 if ( !strcmp( token, "}" ) ) {
1030 /* ttimo : bp: here we may have to jump over brush epairs (only used in editor) */
1031 if ( g_bBrushPrimit == BPRIMIT_NEWBRUSHES ) {
1034 if ( strcmp( token, "(" ) ) {
1045 /* test side count */
1046 if ( buildBrush->numsides >= MAX_BUILD_SIDES ) {
1047 xml_Select( "MAX_BUILD_SIDES", buildBrush->entityNum, buildBrush->brushNum, qtrue );
1051 side = &buildBrush->sides[ buildBrush->numsides ];
1052 memset( side, 0, sizeof( *side ) );
1053 buildBrush->numsides++;
1055 /* read the three point plane definition */
1056 Parse1DMatrix( 3, planePoints[ 0 ] );
1057 Parse1DMatrix( 3, planePoints[ 1 ] );
1058 Parse1DMatrix( 3, planePoints[ 2 ] );
1060 /* bp: read the texture matrix */
1061 if ( g_bBrushPrimit == BPRIMIT_NEWBRUSHES ) {
1062 Parse2DMatrix( 2, 3, (float*) side->texMat );
1065 /* read shader name */
1067 strcpy( name, token );
1070 if ( g_bBrushPrimit == BPRIMIT_OLDBRUSHES ) {
1072 shift[ 0 ] = atof( token );
1074 shift[ 1 ] = atof( token );
1076 rotate = atof( token );
1078 scale[ 0 ] = atof( token );
1080 scale[ 1 ] = atof( token );
1083 /* set default flags and values */
1084 sprintf( shader, "textures/%s", name );
1086 si = &shaderInfo[ 0 ];
1089 si = ShaderInfoForShader( shader );
1091 side->shaderInfo = si;
1092 side->surfaceFlags = si->surfaceFlags;
1093 side->contentFlags = si->contentFlags;
1094 side->compileFlags = si->compileFlags;
1095 side->value = si->value;
1097 /* ydnar: gs mods: bias texture shift */
1098 if ( si->globalTexture == qfalse ) {
1099 shift[ 0 ] -= ( floor( shift[ 0 ] / si->shaderWidth ) * si->shaderWidth );
1100 shift[ 1 ] -= ( floor( shift[ 1 ] / si->shaderHeight ) * si->shaderHeight );
1104 historically, there are 3 integer values at the end of a brushside line in a .map file.
1105 in quake 3, the only thing that mattered was the first of these three values, which
1106 was previously the content flags. and only then did a single bit matter, the detail
1107 bit. because every game has its own special flags for specifying detail, the
1108 traditionally game-specified CONTENTS_DETAIL flag was overridden for Q3Map 2.3.0
1109 by C_DETAIL, defined in q3map2.h. the value is exactly as it was before, but
1110 is stored in compileFlags, as opposed to contentFlags, for multiple-game
1114 if ( TokenAvailable() ) {
1115 /* get detail bit from map content flags */
1117 flags = atoi( token );
1118 if ( flags & C_DETAIL ) {
1119 side->compileFlags |= C_DETAIL;
1124 //% td.flags = atoi( token );
1126 //% td.value = atoi( token );
1129 /* find the plane number */
1130 planenum = MapPlaneFromPoints( planePoints );
1131 side->planenum = planenum;
1133 /* bp: get the texture mapping for this texturedef / plane combination */
1134 if ( g_bBrushPrimit == BPRIMIT_OLDBRUSHES ) {
1135 QuakeTextureVecs( &mapplanes[ planenum ], shift, rotate, scale, side->vecs );
1140 if ( g_bBrushPrimit == BPRIMIT_NEWBRUSHES ) {
1150 RemoveDuplicateBrushPlanes
1151 returns false if the brush has a mirrored set of planes,
1152 meaning it encloses no volume.
1153 also removes planes without any normal
1156 qboolean RemoveDuplicateBrushPlanes( brush_t *b ){
1162 for ( i = 1 ; i < b->numsides ; i++ ) {
1164 // check for a degenerate plane
1165 if ( sides[i].planenum == -1 ) {
1166 xml_Select( "degenerate plane", b->entityNum, b->brushNum, qfalse );
1168 for ( k = i + 1 ; k < b->numsides ; k++ ) {
1169 sides[k - 1] = sides[k];
1176 // check for duplication and mirroring
1177 for ( j = 0 ; j < i ; j++ ) {
1178 if ( sides[i].planenum == sides[j].planenum ) {
1179 xml_Select( "duplicate plane", b->entityNum, b->brushNum, qfalse );
1180 // remove the second duplicate
1181 for ( k = i + 1 ; k < b->numsides ; k++ ) {
1182 sides[k - 1] = sides[k];
1189 if ( sides[i].planenum == ( sides[j].planenum ^ 1 ) ) {
1190 // mirror plane, brush is invalid
1191 xml_Select( "mirrored plane", b->entityNum, b->brushNum, qfalse );
1203 parses a brush out of a map file and sets it up
1206 static void ParseBrush( qboolean onlyLights, qboolean noCollapseGroups ){
1207 /* parse the brush out of the map */
1208 ParseRawBrush( onlyLights );
1210 /* only go this far? */
1215 /* set some defaults */
1216 buildBrush->portalareas[ 0 ] = -1;
1217 buildBrush->portalareas[ 1 ] = -1;
1218 buildBrush->entityNum = numMapEntities - 1;
1219 buildBrush->brushNum = entitySourceBrushes;
1221 /* if there are mirrored planes, the entire brush is invalid */
1222 if ( !RemoveDuplicateBrushPlanes( buildBrush ) ) {
1226 /* get the content for the entire brush */
1227 SetBrushContents( buildBrush );
1229 /* allow detail brushes to be removed */
1230 if ( nodetail && ( buildBrush->compileFlags & C_DETAIL ) ) {
1231 //% FreeBrush( buildBrush );
1235 /* allow liquid brushes to be removed */
1236 if ( nowater && ( buildBrush->compileFlags & C_LIQUID ) ) {
1237 //% FreeBrush( buildBrush );
1241 /* ydnar: allow hint brushes to be removed */
1242 if ( noHint && ( buildBrush->compileFlags & C_HINT ) ) {
1243 //% FreeBrush( buildBrush );
1247 /* finish the brush */
1248 FinishBrush( noCollapseGroups );
1254 MoveBrushesToWorld()
1255 takes all of the brushes from the current entity and
1256 adds them to the world's brush list
1257 (used by func_group)
1260 void AdjustBrushesForOrigin( entity_t *ent );
1261 void MoveBrushesToWorld( entity_t *ent ){
1265 /* we need to undo the common/origin adjustment, and instead shift them by the entity key origin */
1266 VectorScale( ent->origin, -1, ent->originbrush_origin );
1267 AdjustBrushesForOrigin( ent );
1268 VectorClear( ent->originbrush_origin );
1271 for ( b = ent->brushes; b != NULL; b = next )
1273 /* get next brush */
1276 /* link opaque brushes to head of list, translucent brushes to end */
1277 if ( b->opaque || entities[ 0 ].lastBrush == NULL ) {
1278 b->next = entities[ 0 ].brushes;
1279 entities[ 0 ].brushes = b;
1280 if ( entities[ 0 ].lastBrush == NULL ) {
1281 entities[ 0 ].lastBrush = b;
1287 entities[ 0 ].lastBrush->next = b;
1288 entities[ 0 ].lastBrush = b;
1291 ent->brushes = NULL;
1293 /* ydnar: move colormod brushes */
1294 if ( ent->colorModBrushes != NULL ) {
1295 for ( b = ent->colorModBrushes; b->nextColorModBrush != NULL; b = b->nextColorModBrush ) ;
1297 b->nextColorModBrush = entities[ 0 ].colorModBrushes;
1298 entities[ 0 ].colorModBrushes = ent->colorModBrushes;
1300 ent->colorModBrushes = NULL;
1304 if ( ent->patches != NULL ) {
1305 for ( pm = ent->patches; pm->next != NULL; pm = pm->next ) ;
1307 pm->next = entities[ 0 ].patches;
1308 entities[ 0 ].patches = ent->patches;
1310 ent->patches = NULL;
1317 AdjustBrushesForOrigin()
1320 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 ){
1373 /* walk the entity's brushes/patches and determine bounds */
1374 ClearBounds( mins, maxs );
1375 for ( b = e->brushes; b; b = b->next )
1377 AddPointToBounds( b->mins, mins, maxs );
1378 AddPointToBounds( b->maxs, mins, maxs );
1380 for ( p = e->patches; p; p = p->next )
1382 for ( i = 0; i < ( p->mesh.width * p->mesh.height ); i++ )
1383 AddPointToBounds( p->mesh.verts[ i ].xyz, mins, maxs );
1386 /* try to find explicit min/max key */
1387 value = ValueForKey( e, "min" );
1388 if ( value[ 0 ] != '\0' ) {
1389 GetVectorForKey( e, "min", mins );
1391 value = ValueForKey( e, "max" );
1392 if ( value[ 0 ] != '\0' ) {
1393 GetVectorForKey( e, "max", maxs );
1396 /* store the bounds */
1397 for ( b = e->brushes; b; b = b->next )
1399 VectorCopy( mins, b->eMins );
1400 VectorCopy( maxs, b->eMaxs );
1402 for ( p = e->patches; p; p = p->next )
1404 VectorCopy( mins, p->eMins );
1405 VectorCopy( maxs, p->eMaxs );
1412 LoadEntityIndexMap() - ydnar
1413 based on LoadAlphaMap() from terrain.c, a little more generic
1416 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 ) {
1432 /* determine if there is an index map (support legacy "alphamap" key as well) */
1433 value = ValueForKey( e, "_indexmap" );
1434 if ( value[ 0 ] == '\0' ) {
1435 value = ValueForKey( e, "alphamap" );
1437 if ( value[ 0 ] == '\0' ) {
1440 indexMapFilename = value;
1442 /* get number of layers (support legacy "layers" key as well) */
1443 value = ValueForKey( e, "_layers" );
1444 if ( value[ 0 ] == '\0' ) {
1445 value = ValueForKey( e, "layers" );
1447 if ( value[ 0 ] == '\0' ) {
1448 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_layers\" or \"layers\" key\n", indexMapFilename );
1449 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1452 numLayers = atoi( value );
1453 if ( numLayers < 1 ) {
1454 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has < 1 layer (%d)\n", indexMapFilename, numLayers );
1455 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1459 /* get base shader name (support legacy "shader" key as well) */
1460 value = ValueForKey( mapEnt, "_shader" );
1461 if ( value[ 0 ] == '\0' ) {
1462 value = ValueForKey( e, "shader" );
1464 if ( value[ 0 ] == '\0' ) {
1465 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" has missing \"_shader\" or \"shader\" key\n", indexMapFilename );
1466 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1472 Sys_FPrintf( SYS_VRB, "Entity %d (%s) has shader index map \"%s\"\n", mapEnt->mapEntityNum, ValueForKey( e, "classname" ), indexMapFilename );
1474 /* get index map file extension */
1475 ExtractFileExtension( indexMapFilename, ext );
1477 /* handle tga image */
1478 if ( !Q_stricmp( ext, "tga" ) ) {
1480 Load32BitImage( indexMapFilename, &pixels32, &w, &h );
1482 /* convert to bytes */
1484 pixels = safe_malloc( size );
1485 for ( i = 0; i < size; i++ )
1487 pixels[ i ] = ( ( pixels32[ i ] & 0xFF ) * numLayers ) / 256;
1488 if ( pixels[ i ] >= numLayers ) {
1489 pixels[ i ] = numLayers - 1;
1493 /* free the 32 bit image */
1499 Load256Image( indexMapFilename, &pixels, NULL, &w, &h );
1502 //% Sys_Printf( "-------------------------------" );
1504 /* fix up out-of-range values */
1506 for ( i = 0; i < size; i++ )
1508 if ( pixels[ i ] >= numLayers ) {
1509 pixels[ i ] = numLayers - 1;
1513 //% if( (i % w) == 0 )
1514 //% Sys_Printf( "\n" );
1515 //% Sys_Printf( "%c", pixels[ i ] + '0' );
1519 //% Sys_Printf( "\n-------------------------------\n" );
1522 /* the index map must be at least 2x2 pixels */
1523 if ( w < 2 || h < 2 ) {
1524 Sys_Printf( "WARNING: Entity with index/alpha map \"%s\" is smaller than 2x2 pixels\n", indexMapFilename );
1525 Sys_Printf( "Entity will not be textured properly. Check your keys/values.\n" );
1530 /* create a new index map */
1531 im = safe_malloc( sizeof( *im ) );
1532 memset( im, 0, sizeof( *im ) );
1537 im->numLayers = numLayers;
1538 strcpy( im->name, indexMapFilename );
1539 strcpy( im->shader, shader );
1540 im->pixels = pixels;
1542 /* get height offsets */
1543 value = ValueForKey( mapEnt, "_offsets" );
1544 if ( value[ 0 ] == '\0' ) {
1545 value = ValueForKey( e, "offsets" );
1547 if ( value[ 0 ] != '\0' ) {
1548 /* value is a space-seperated set of numbers */
1549 strcpy( offset, value );
1552 /* get each value */
1553 for ( i = 0; i < 256 && *search != '\0'; i++ )
1555 space = strstr( search, " " );
1556 if ( space != NULL ) {
1559 im->offsets[ i ] = atof( search );
1560 if ( space == NULL ) {
1567 /* store the index map in every brush/patch in the entity */
1568 for ( b = e->brushes; b != NULL; b = b->next )
1570 for ( p = e->patches; p != NULL; p = p->next )
1582 parses a single entity out of a map file
1585 static qboolean ParseMapEntity( qboolean onlyLights, qboolean noCollapseGroups ){
1587 const char *classname, *value;
1588 float lightmapScale, shadeAngle;
1589 int lightmapSampleSize;
1590 char shader[ MAX_QPATH ];
1591 shaderInfo_t *celShader = NULL;
1595 int castShadows, recvShadows;
1599 if ( !GetToken( qtrue ) ) {
1603 /* conformance check */
1604 if ( strcmp( token, "{" ) ) {
1605 Sys_Printf( "WARNING: ParseEntity: { not found, found %s on line %d - last entity was at: <%4.2f, %4.2f, %4.2f>...\n"
1606 "Continuing to process map, but resulting BSP may be invalid.\n",
1607 token, scriptline, entities[ numEntities ].origin[ 0 ], entities[ numEntities ].origin[ 1 ], entities[ numEntities ].origin[ 2 ] );
1612 AUTOEXPAND_BY_REALLOC( entities, numEntities, allocatedEntities, 32 );
1615 entitySourceBrushes = 0;
1616 mapEnt = &entities[ numEntities ];
1618 memset( mapEnt, 0, sizeof( *mapEnt ) );
1620 /* ydnar: true entity numbering */
1621 mapEnt->mapEntityNum = numMapEntities;
1627 /* get initial token */
1628 if ( !GetToken( qtrue ) ) {
1629 Sys_Printf( "WARNING: ParseEntity: EOF without closing brace\n"
1630 "Continuing to process map, but resulting BSP may be invalid.\n" );
1634 if ( !strcmp( token, "}" ) ) {
1638 if ( !strcmp( token, "{" ) ) {
1639 /* parse a brush or patch */
1640 if ( !GetToken( qtrue ) ) {
1645 if ( !strcmp( token, "patchDef2" ) ) {
1647 ParsePatch( onlyLights );
1649 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" ) ) {
1654 if ( g_bBrushPrimit == BPRIMIT_OLDBRUSHES ) {
1655 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" );
1667 g_bBrushPrimit = BPRIMIT_OLDBRUSHES;
1669 /* parse old brush format */
1671 ParseBrush( onlyLights, noCollapseGroups );
1673 entitySourceBrushes++;
1677 /* parse a key / value pair */
1680 /* ydnar: 2002-07-06 fixed wolf bug with empty epairs */
1681 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 ) ) {
1697 /* ydnar: determine if this is a func_group */
1698 if ( !Q_stricmp( "func_group", classname ) ) {
1705 /* worldspawn (and func_groups) default to cast/recv shadows in worldspawn group */
1706 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" ) ) ) {
1729 /* get lightmap scale from entity */
1730 lightmapScale = FloatForKey( mapEnt, "lightmapscale" );
1731 if ( lightmapScale <= 0.0f ) {
1732 lightmapScale = FloatForKey( mapEnt, "_lightmapscale" );
1734 if ( lightmapScale <= 0.0f ) {
1735 lightmapScale = FloatForKey( mapEnt, "_ls" );
1737 if ( lightmapScale < 0.0f ) {
1738 lightmapScale = 0.0f;
1740 if ( lightmapScale > 0.0f ) {
1741 Sys_Printf( "Entity %d (%s) has lightmap scale of %.4f\n", mapEnt->mapEntityNum, classname, lightmapScale );
1745 /* ydnar: get cel shader :) for this entity */
1746 value = ValueForKey( mapEnt, "_celshader" );
1747 if ( value[ 0 ] == '\0' ) {
1748 value = ValueForKey( &entities[ 0 ], "_celshader" );
1750 if ( value[ 0 ] != '\0' ) {
1751 if ( strcmp( value, "none" ) ) {
1752 sprintf( shader, "textures/%s", value );
1753 celShader = ShaderInfoForShader( shader );
1754 Sys_Printf( "Entity %d (%s) has cel shader %s\n", mapEnt->mapEntityNum, classname, celShader->shader );
1762 celShader = ( *globalCelShader ? ShaderInfoForShader( globalCelShader ) : NULL );
1765 /* jal : entity based _shadeangle */
1767 if ( strcmp( "", ValueForKey( mapEnt, "_shadeangle" ) ) ) {
1768 shadeAngle = FloatForKey( mapEnt, "_shadeangle" );
1770 /* vortex' aliases */
1771 else if ( strcmp( "", ValueForKey( mapEnt, "_smoothnormals" ) ) ) {
1772 shadeAngle = FloatForKey( mapEnt, "_smoothnormals" );
1774 else if ( strcmp( "", ValueForKey( mapEnt, "_sn" ) ) ) {
1775 shadeAngle = FloatForKey( mapEnt, "_sn" );
1777 else if ( strcmp( "", ValueForKey( mapEnt, "_sa" ) ) ) {
1778 shadeAngle = FloatForKey( mapEnt, "_sa" );
1780 else if ( strcmp( "", ValueForKey( mapEnt, "_smooth" ) ) ) {
1781 shadeAngle = FloatForKey( mapEnt, "_smooth" );
1784 if ( shadeAngle < 0.0f ) {
1788 if ( shadeAngle > 0.0f ) {
1789 Sys_Printf( "Entity %d (%s) has shading angle of %.4f\n", mapEnt->mapEntityNum, classname, shadeAngle );
1792 /* jal : entity based _samplesize */
1793 lightmapSampleSize = 0;
1794 if ( strcmp( "", ValueForKey( mapEnt, "_lightmapsamplesize" ) ) ) {
1795 lightmapSampleSize = IntForKey( mapEnt, "_lightmapsamplesize" );
1797 else if ( strcmp( "", ValueForKey( mapEnt, "_samplesize" ) ) ) {
1798 lightmapSampleSize = IntForKey( mapEnt, "_samplesize" );
1800 else if ( strcmp( "", ValueForKey( mapEnt, "_ss" ) ) ) {
1801 lightmapSampleSize = IntForKey( mapEnt, "_ss" );
1804 if ( lightmapSampleSize < 0 ) {
1805 lightmapSampleSize = 0;
1808 if ( lightmapSampleSize > 0 ) {
1809 Sys_Printf( "Entity %d (%s) has lightmap sample size of %d\n", mapEnt->mapEntityNum, classname, lightmapSampleSize );
1812 /* attach stuff to everything in the entity */
1813 for ( brush = mapEnt->brushes; brush != NULL; brush = brush->next )
1815 brush->entityNum = mapEnt->mapEntityNum;
1816 brush->castShadows = castShadows;
1817 brush->recvShadows = recvShadows;
1818 brush->lightmapSampleSize = lightmapSampleSize;
1819 brush->lightmapScale = lightmapScale;
1820 brush->celShader = celShader;
1821 brush->shadeAngleDegrees = shadeAngle;
1824 for ( patch = mapEnt->patches; patch != NULL; patch = patch->next )
1826 patch->entityNum = mapEnt->mapEntityNum;
1827 patch->castShadows = castShadows;
1828 patch->recvShadows = recvShadows;
1829 patch->lightmapSampleSize = lightmapSampleSize;
1830 patch->lightmapScale = lightmapScale;
1831 patch->celShader = celShader;
1834 /* ydnar: gs mods: set entity bounds */
1835 SetEntityBounds( mapEnt );
1837 /* ydnar: gs mods: load shader index map (equivalent to old terrain alphamap) */
1838 LoadEntityIndexMap( mapEnt );
1840 /* get entity origin and adjust brushes */
1841 GetVectorForKey( mapEnt, "origin", mapEnt->origin );
1842 if ( mapEnt->originbrush_origin[ 0 ] || mapEnt->originbrush_origin[ 1 ] || mapEnt->originbrush_origin[ 2 ] ) {
1843 AdjustBrushesForOrigin( mapEnt );
1846 /* group_info entities are just for editor grouping (fixme: leak!) */
1847 if ( !noCollapseGroups && !Q_stricmp( "group_info", classname ) ) {
1852 /* group entities are just for editor convenience, toss all brushes into worldspawn */
1853 if ( !noCollapseGroups && funcGroup ) {
1854 MoveBrushesToWorld( mapEnt );
1867 loads a map file into a list of entities
1870 void LoadMapFile( char *filename, qboolean onlyLights, qboolean noCollapseGroups ){
1873 int oldNumEntities = 0, numMapBrushes;
1877 Sys_FPrintf( SYS_VRB, "--- LoadMapFile ---\n" );
1878 Sys_Printf( "Loading %s\n", filename );
1881 file = SafeOpenRead( filename );
1884 /* load the map file */
1885 LoadScriptFile( filename, -1 );
1889 oldNumEntities = numEntities;
1896 numMapDrawSurfs = 0;
1898 g_bBrushPrimit = BPRIMIT_UNDEFINED;
1900 /* allocate a very large temporary brush for building the brushes as they are loaded */
1901 buildBrush = AllocBrush( MAX_BUILD_SIDES );
1903 /* parse the map file */
1904 while ( ParseMapEntity( onlyLights, noCollapseGroups ) ) ;
1908 /* emit some statistics */
1909 Sys_FPrintf( SYS_VRB, "%9d light entities\n", numEntities - oldNumEntities );
1913 /* set map bounds */
1914 ClearBounds( mapMins, mapMaxs );
1915 for ( b = entities[ 0 ].brushes; b; b = b->next )
1917 AddPointToBounds( b->mins, mapMins, mapMaxs );
1918 AddPointToBounds( b->maxs, mapMins, mapMaxs );
1921 /* get brush counts */
1922 numMapBrushes = CountBrushList( entities[ 0 ].brushes );
1923 if ( (float) c_detail / (float) numMapBrushes < 0.10f && numMapBrushes > 500 ) {
1924 Sys_Printf( "WARNING: Over 90 percent structural map detected. Compile time may be adversely affected.\n" );
1927 /* emit some statistics */
1928 Sys_FPrintf( SYS_VRB, "%9d total world brushes\n", numMapBrushes );
1929 Sys_FPrintf( SYS_VRB, "%9d detail brushes\n", c_detail );
1930 Sys_FPrintf( SYS_VRB, "%9d patches\n", numMapPatches );
1931 Sys_FPrintf( SYS_VRB, "%9d boxbevels\n", c_boxbevels );
1932 Sys_FPrintf( SYS_VRB, "%9d edgebevels\n", c_edgebevels );
1933 Sys_FPrintf( SYS_VRB, "%9d entities\n", numEntities );
1934 Sys_FPrintf( SYS_VRB, "%9d planes\n", nummapplanes );
1935 Sys_Printf( "%9d areaportals\n", c_areaportals );
1936 Sys_Printf( "Size: %5.0f, %5.0f, %5.0f to %5.0f, %5.0f, %5.0f\n",
1937 mapMins[ 0 ], mapMins[ 1 ], mapMins[ 2 ],
1938 mapMaxs[ 0 ], mapMaxs[ 1 ], mapMaxs[ 2 ] );
1940 /* write bogus map */
1942 WriteBSPBrushMap( "fakemap.map", entities[ 0 ].brushes );